What is it?
TAS, or Tiles-And-Sprites, Mode is a screen mode optimized for rendering tilemaps with sprites on top. It uses very little memory compared to modes based on a framebuffer and supports up to 256 colors at either low or high resolution.
Specs
Resolution: 220x176 or 110x88
Colors: 256
RAM: 512 bytes palette, 220 bytes line buffer, 4 bytes per tile, 16 bytes per Sprite
Good for: Games with tiles in the background
Not good for: Games that need of drawing primitives
How do I get started?
MicroPython
Just go to https://pokittopython.herokuapp.com, click on the Flags tab, then change the resolution to either TAS Low
or TAS High
. Thatâs it.
C++
Download:
Youâre ready! In FemtoIDE create a new project and select the 2-C++ Tiles+Sprites Game
template. You now have a project that converts Tiled TMX files into C++ and draws them on the Pokitto. To convert all the TMX files in the maps folder, just click Scripts -> Convert TMX. Also included is a script that converts animated sprites into easy-to-use code.
If, instead of high-res you prefer 110x88, open My_settings.h and set the screenmode like this:
#define PROJ_SCREENMODE TASMODELOW
Optionally, you can tweak following parameters.
-
PROJ_MAX_SPRITES
Default: 100. Defines the maximum amount of sprites you can draw per frame. -
PROJ_TILE_W
: Default: 16. Defines the width of each tile -
PROJ_TILE_H
: Default: 16. Defines the height of each tile.
For example, if you want a tilemap with 32x32 pixel tiles and only 10 sprites, youâd have this in your projectâs My_settings.h
:
#define PROJ_SCREENMODE TASMODE
#define PROJ_TILE_W 32
#define PROJ_TILE_H 32
#define PROJ_MAX_SPRITES 10
Tiles
The tilemap in TAS Mode is one tile larger than the screen, in each dimension. With each tile being 32x32 pixels, that means you get an 8x7 tilemap. This might seem limiting at first, but used together with the included Tilemap library, it is all that is necessary to draw all the scenery you can fit into your game.
To learn about how to use the Tilemap library, have a look at @filmoteâs excellent tutorials.
The Tilemap in TAS Mode has a few minor differences, compared to other screen modes:
- Every tile must be of the same size.
- The tilemap is always drawn under the sprites, in the background
- There can be only one tilemap
- You can have either 1 color, 4-bit (16 colors), or 8-bit (256 color) tiles.
To save space you can limit your tiles to use only the first 16 colors of the palette. Your tiles must then be in the same format as in one of the 4-bit modes. For this to work, you just need to change the displayâs color depth:
Pokitto::Display::setColorDepth(4);
If you want to have a tile that is a single color, there is no need to create a bitmap for it. Instead, you can use:
tilemap.setColorTile(tileIndex, colorIndex);
To draw a sea, for example, if the color blue is on index #2 in your palette, tilemap.setColorTile(0, 2)
will create a blue tile wherever tile #0 is found in the map.
Sprites
In TAS mode, instead of drawing everything into a large framebuffer and sending that to the screen, a list is kept with all the drawing operations (sprites). This means that, if you donât need to draw a lot of sprites at once, you can have a very small list and free up RAM for other things.
If you try to draw more sprites than you defined in PROJ_MAX_SPRITES, the list will clear itself and everything you drew before will be erased.
Since there is no framebuffer, not all drawing operations are supported.
These are the functions that work:
- drawColumn
- drawRow
- drawRectangle
- fillRectangle
- bufferChar (and, consequently,
print
) - drawBitmapData2BPP
- drawBitmapData4BPP
- drawBitmapData8BPP
- drawBitmapDataXFlipped4BPP
- drawBitmapDataYFlipped (4BPP only)
- drawBitmapDataXFlipped4BPP
- drawBitmapDataXFlipped8BPP
- drawSprite
Each one of these puts a sprite in the list of drawing operations. print
calls bufferChar
for each letter in a string, so Display::print("hello world!")
will take up 12 sprites!
TAS mode is optimized for 8-bit images, but to draw a 4-bit image, the following is supported:
Display::setColorDepth(4);
Display::drawBitmap(x, y, bitmap);
For 8-bit images, it is better to use the drawSprite function:
Display::drawSprite(int x, int y, const uint8_t *data, bool flipped=0, bool mirrored=0, uint8_t recolor=0)
Flip (mirror vertically) and mirror can be used together. Recolor will add the specified value to each non-transparent pixel. If you want to make a football game, you can have a single bitmap for the players, but add a different recolor offset for each team. Or in a fighting game, if both fighers choose the same character, you can recolor one of them so they can be told apart.
Note that, for performance reasons, drawSprite
always uses zero as the invisible color. If you need the color in that position, then you must edit the palette.
Masking
It is possible to disable TAS mode for certain parts of the screen. This allows you to draw freely in the disabled areas using direct mode.
Disabling is done in rows of 8 pixels using setTASRowMask:
Pokitto::Display::setTASRowMask(0b1111'00000000'11111111);
From right-to-left, each 1 enables a row, resulting in the following image:
MicroPython
PyInSky is being updated to support TAS Mode. Due to the flash space constraints, it will use only the 4-bit drawing functions, but it will have a lot more free RAM than the currently supported modes.
TL;DR - Show me the code, already!
// My_settings.h
#define PROJ_SCREENMODE TASMODE
#define PROJ_TILE_H 16
#define PROJ_TILE_W 16
#define MAX_TILE_COUNT 256
// main.cpp
#include <Pokitto.h>
#include <miloslav.h>
#include <Tilemap.hpp>
#include "gardenPath.h"
#include "smile.h"
int main(){
using PC=Pokitto::Core;
using PD=Pokitto::Display;
using PB=Pokitto::Buttons;
PC::begin();
PD::invisiblecolor = 0;
PD::loadRGBPalette(Miloslav);
auto& map = gardenPath;
Tilemap tilemap;
tilemap.set(map[0], map[1], map+2);
for(int i=0; i<sizeof(tiles)/(POK_TILE_W*POK_TILE_H); i++)
tilemap.setTile(i, POK_TILE_W, POK_TILE_H, tiles+i*POK_TILE_W*POK_TILE_H);
auto start = PC::getTime();
while( PC::isRunning() ){
if( !PC::update() ) continue;
for(int i=0; i<8; ++i)
PD::drawSprite(i*16, rand()%32+40, Smile);
PD::setCursor(60, 60);
PD::fontSize = 2;
PD::print("Hello world!");
tilemap.draw(x, y);
}
}