[Tutorial]Building a Simple TileMap Game, Part 9 of 9

Sixteen tiles only?


In the previous section, I introduced an approach to swapping the tile set you are using based on the level or world you are currently playing. This allows you to restrict each individual world to 16 tiles which, in turn, allows the compression of the world using the ‘two tiles per byte’ encoding shown throughout this tutorial so far.

But what if your levels are more complex and require more than 16 tiles?

A recent PR submitted by @hanski allows you to increase the tile count from 16 to a high value up to 256 tiles. This is typically enough for even the most complex worlds especially when combined with the ability to replace tiles per level. The downside of having more than 16 tiles is that the tiles cannot be compressed into the world map array (easily) and your world map will literally double in size. This may not be an issue for the maps stored in flash but copying these to RAM may be a concern.

Download the code in the repo https://github.com/filmote/Tilemap_7 This is essentially the same code as Project 6 however it has been changed to support additional tiles.

Adding support for additional tiles starts with adding a simple #define in the My_settings.hconfiguration file, as shown below. The number of tiles you specify (32 in my case) can be any number from 16 to 256. Omitting the declaration will result in the default value of 16 being used.

#define MAX_TILE_COUNT 32

In the sample project, have used an #ifdef, #else and #endif compilation directives to conditionally compile sections of code depending on this value. In this way, you can see the original code and its replacement side by side. The sample code actually does not use more than 16 tiles so feel free to change this value from between 16 and any other value and recompile the code. If you are using FemtoIDE, make sure you select the ‘Clean’ option before building to ensure the changes are detected.

Changing the setting to anything more than 16 will require a change to the data in the world maps from the ‘two tiles per byte’ encoding to a simple ‘single byte per tile’. This is clearly shown in the examples below - the first section of code show our original world map whereas the second shows the same map in the ‘single byte per tile’ encoding. The highlighted values in both samples highlights the difference in size between the two approaches – the second approach literally taking twice as many bytes as the first.

    const uint8_t worldMap_0[] = {
        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x10, 0x00,
        0x48, 0x88, 0x88, 0x88, 0x88, 0x84, 0x11, 0x00,
        0x48, 0x44, 0x48, 0x84, 0x44, 0x84, 0x21, 0x00,
        0x48, 0x48, 0x88, 0x88, 0x84, 0x84, 0x21, 0x00,
        …
    };


    const uint8_t worldMap_0[] = {
        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 0, 0, 0, 
        4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 1, 1, 0, 0, 
        4, 8, 4, 4, 4, 8, 8, 4, 4, 4, 8, 4, 2, 1, 0, 0, 
        4, 8, 4, 8, 8, 8, 8, 8, 8, 4, 8, 4, 2, 1, 0, 0,             
        …
    };

Other changes are required when declaring the worldMap array and the functions that subsequently populate and update it. These changes are shown below with both the original and changed code. In all cases, the changes simply account for the two encoding types.

#if MAX_TILE_COUNT == 16

    uint8_t worldMap[Constants::mapTileWidth * Constants::mapTileHeight / 2];
    
#else

    uint8_t worldMap[Constants::mapTileWidth * Constants::mapTileHeight];
    
#endif
void initWorld(uint8_t worldIndex) {


    // Populate the world data ..
    
    #if MAX_TILE_COUNT == 16
    
        for (uint16_t i = 0; i < Constants::mapTileWidth * 
                                 Constants::mapTileHeight / 2; i++) {
            
            worldMap[i] = Data::worldMaps[worldIndex][i];
        }

    #else    
    
        for (uint16_t i = 0; i < Constants::mapTileWidth * 
                                 Constants::mapTileHeight; i++) {
            
            worldMap[i] = Data::worldMaps[worldIndex][i];
        }

    #endif
    …

}
void updateTileType(uint16_t tileIndex, TileType tileType) {

    #if MAX_TILE_COUNT == 16

        if (tileIndex % 2 == 0) {
    
            worldMap[tileIndex / 2] = (worldMap[tileIndex / 2] & 0x0f) | 
                                      (tileType << 4);            
        }   
        else {
    
            worldMap[tileIndex / 2] = (worldMap[tileIndex / 2] & 0xf0) | 
                                      tileType;    
        }
        
    #else

        worldMap[tileIndex] = tileType;
    
    #endif
                
}



<< Prev | Next >>

Part 1 of 9: Building a Simple Tilemap Game
Part 2 of 9: Moving a Player Around the World
Part 3 of 9: Rendering the Viewport and Player
Part 4 of 9: Detecting Obstacles
Part 5 of 9: Adding Enemies
Part 6 of 9: Using FemtoIDE and Piskel
Part 7 of 9: Doors and Inventory Items
Part 8 of 9: Multiple Worlds
Part 9 of 9: Sixteen Tiles Only?

2 Likes