TileMap¶
Structure for tile-based background rendering.
Description¶
TileMap is a compact structure for rendering tile-based backgrounds efficiently. It uses 1bpp sprites as tiles and stores level data as a compact array of tile indices.
Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.
Namespace¶
Structure¶
uint8_t* indices¶
Array of tile indices mapping to tile sprites.
Type: uint8_t*
Access: Read-write
Notes: - Array size = width * height - Each value is an index into the tiles array - 0 = first tile, 1 = second tile, etc. - Should be stored in flash (const) for best performance
Example:
// 16x16 tilemap (256 tiles)
static const uint8_t LEVEL_INDICES[] = {
0, 0, 0, 0, 1, 1, 1, 1, // Row 0
0, 2, 2, 2, 2, 2, 2, 0, // Row 1
// ... more rows
};
uint8_t width¶
Width of the tilemap in tiles.
Type: uint8_t
Access: Read-write
Example:
uint8_t height¶
Height of the tilemap in tiles.
Type: uint8_t
Access: Read-write
Example:
const Sprite* tiles¶
Array of tile sprites.
Type: const Sprite*
Access: Read-only
Notes: - Array of sprite pointers, one per unique tile - Indices reference this array - All tiles should be the same size - Should be stored in flash (const) for best performance
Example:
static const Sprite TILE_SPRITES[] = {
EMPTY_TILE, // Index 0
WALL_TILE, // Index 1
FLOOR_TILE, // Index 2
// ... more tiles
};
uint8_t tileWidth¶
Width of each tile in pixels.
Type: uint8_t
Access: Read-write
Notes: - All tiles must have the same width - Common values: 8, 16 pixels - Should match sprite width
Example:
uint8_t tileHeight¶
Height of each tile in pixels.
Type: uint8_t
Access: Read-write
Notes: - All tiles must have the same height - Common values: 8, 16 pixels - Should match sprite height
Example:
uint16_t tileCount¶
Number of unique tiles in the tiles array.
Type: uint16_t
Access: Read-write
Notes: - Must match the size of the tiles array - Indices must be < tileCount
Example:
Creating Tilemaps¶
Step 1: Create Tile Sprites¶
// Empty tile (index 0)
static const uint16_t EMPTY_DATA[] = {
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
// Wall tile (index 1)
static const uint16_t WALL_DATA[] = {
0b11111111,
0b10000001,
0b10000001,
0b10000001,
0b10000001,
0b10000001,
0b10000001,
0b11111111
};
// Floor tile (index 2)
static const uint16_t FLOOR_DATA[] = {
0b00000000,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b00000000
};
static const Sprite TILE_SPRITES[] = {
{EMPTY_DATA, 8, 8}, // Index 0
{WALL_DATA, 8, 8}, // Index 1
{FLOOR_DATA, 8, 8} // Index 2
};
Step 2: Create Index Array¶
// 16x16 level (256 tiles)
// 0 = empty, 1 = wall, 2 = floor
static const uint8_t LEVEL_INDICES[] = {
// Row 0: Top wall
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// Row 1: Walls on sides
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
// Row 2
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
// ... more rows
// Row 15: Bottom wall
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
Step 3: Create TileMap Structure¶
static const TileMap LEVEL_MAP = {
const_cast<uint8_t*>(LEVEL_INDICES), // indices (non-const for struct)
16, // width in tiles
16, // height in tiles
TILE_SPRITES, // tile sprites array
8, // tile width
8, // tile height
3 // tile count
};
Rendering Tilemaps¶
Use Renderer::drawTileMap():
void draw(pixelroot32::graphics::Renderer& renderer) override {
// Draw tilemap at origin (0, 0)
renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);
// With camera offset
camera.apply(renderer);
renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);
}
Viewport Culling¶
Tilemaps automatically cull tiles outside the viewport:
- Only visible tiles are drawn
- Very efficient for large levels
- Works with camera scrolling
Example:
// Large level (256x256 tiles)
// Only tiles visible on screen are drawn
camera.apply(renderer);
renderer.drawTileMap(LARGE_LEVEL_MAP, 0, 0, Color::White);
Collision Detection with Tilemaps¶
Check tile at world position:
bool isSolidTile(int worldX, int worldY, const TileMap& map) {
int tileX = worldX / map.tileWidth;
int tileY = worldY / map.tileHeight;
if (tileX < 0 || tileX >= map.width ||
tileY < 0 || tileY >= map.height) {
return true; // Outside map = solid
}
int index = tileY * map.width + tileX;
uint8_t tileIndex = map.indices[index];
// Check if tile is solid (e.g., wall = index 1)
return (tileIndex == 1);
}
Usage Example¶
#include "graphics/TileMap.h"
#include "graphics/Renderer.h"
class LevelScene : public pixelroot32::core::Scene {
private:
pixelroot32::graphics::TileMap levelMap;
pixelroot32::graphics::Camera2D camera;
public:
void init() override {
// Level map is already defined (see above)
// Create camera
auto& renderer = engine.getRenderer();
camera = pixelroot32::graphics::Camera2D(
renderer.getWidth(),
renderer.getHeight()
);
// Set level boundaries
int levelWidth = levelMap.width * levelMap.tileWidth;
int levelHeight = levelMap.height * levelMap.tileHeight;
camera.setBounds(0.0f, levelWidth - renderer.getWidth());
camera.setVerticalBounds(0.0f, levelHeight - renderer.getHeight());
}
void update(unsigned long deltaTime) override {
Scene::update(deltaTime);
// Camera follows player
if (player) {
camera.followTarget(player->x, player->y);
}
}
void draw(pixelroot32::graphics::Renderer& renderer) override {
// Apply camera
camera.apply(renderer);
// Draw tilemap (viewport culling automatic)
renderer.drawTileMap(levelMap, 0, 0, Color::White);
// Draw entities
Scene::draw(renderer);
// Reset for UI
renderer.setDisplayOffset(0, 0);
}
bool checkTileCollision(float x, float y) {
int tileX = static_cast<int>(x) / levelMap.tileWidth;
int tileY = static_cast<int>(y) / levelMap.tileHeight;
if (tileX < 0 || tileX >= levelMap.width ||
tileY < 0 || tileY >= levelMap.height) {
return true; // Outside = solid
}
int index = tileY * levelMap.width + tileX;
uint8_t tile = levelMap.indices[index];
return (tile == 1); // Wall tile
}
};
Performance Considerations¶
- Viewport culling: Only visible tiles are drawn (automatic)
- Tile reuse: Reuse tile sprites across the map
- Index storage: Compact
uint8_tindices (1 byte per tile) - Memory: Store indices and tiles in flash (const) for best performance
- Tile size: Smaller tiles = more tiles to draw, but more detail
ESP32 Considerations¶
- Memory: Store tilemap data in flash, not RAM
- Map size: Large maps use more flash memory
- Tile count: Limit unique tiles to save memory
- Culling: Viewport culling is essential for large levels
See Also¶
- Renderer - Rendering system that draws tilemaps
- Sprite - Sprite structure used for tiles
- Camera2D - Camera for scrolling tilemaps
- Manual - Tilemaps
- API Overview