Game Examples Guide¶
This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.
Available Examples¶
PixelRoot32 (in the PixelRoot32 Game Samples project) includes these games and demos:
- Metroidvania: 2D platformer with multi-layer 4bpp tilemap and tile-based collision (requires
PIXELROOT32_ENABLE_4BPP_SPRITES; no scroll/camera) - Space Invaders: Full shooter with enemies, projectiles, bunkers and audio
- Pong: Classic with physics and collisions
- BrickBreaker: Breakout-style with particles and advanced audio
- Snake: Grid-based game with entity pooling
- TicTacToe: Turn-based with simple AI
- CameraDemo: Platformer with camera and parallax
- SpritesDemo: 2bpp and 4bpp sprites
- TileMapDemo: 4bpp tilemaps (with viewport culling)
Space Invaders¶
Location: src/examples/SpaceInvaders/
Architecture¶
Space Invaders demonstrates a complete game with multiple systems:
- Scene Management:
SpaceInvadersScenemanages game state - Actor Hierarchy:
PlayerActor,AlienActor,ProjectileActor,BunkerActor - Collision System: Uses collision layers for player, enemies, projectiles
- Audio Integration: Sound effects for shooting, explosions, music
- Background: Starfield (code-generated star pattern) or tilemap
Key Systems¶
Collision Layers¶
namespace Layers {
constexpr uint16_t PLAYER = 0x0001;
constexpr uint16_t ALIEN = 0x0002;
constexpr uint16_t PROJECTILE = 0x0004;
constexpr uint16_t BUNKER = 0x0008;
}
// Player can collide with aliens and bunkers
player->setCollisionLayer(Layers::PLAYER);
player->setCollisionMask(Layers::ALIEN | Layers::BUNKER);
// Projectiles can hit aliens and bunkers
projectile->setCollisionLayer(Layers::PROJECTILE);
projectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);
Entity Management¶
- Uses object pooling for projectiles
- Manages alien formation with grid layout
- Handles game state (playing, game over)
Audio Integration¶
- Background music using
MusicPlayer - Sound effects for various events
- Audio events triggered on collisions
Patterns Used¶
- Object Pooling: Projectiles are pooled and reused
- State Machine: Game states (playing, game over, victory)
- Grid Layout: Alien formation uses grid-based positioning
- Event-Driven Audio: Sounds triggered by game events
Lessons Learned¶
- Collision layers are essential for complex games
- Object pooling improves performance
- Starfield or tilemap backgrounds are efficient
- Audio enhances game feel significantly
Metroidvania¶
Location: src/examples/Games/Metroidvania/
Assets: Sprites and tilesets for this example come from the Tiny Metroidvania 8x8 pack by Kenmi (kenmi-art.itch.io).
Architecture¶
Metroidvania is a 2D platformer example with multi-layer tilemap and optimizations aimed at ESP32. It does not use scroll or camera; the level is drawn with a fixed origin (0,0).
- Scene:
MetroidvaniaScenewith a singlePlayerActorand several tilemap layers (background, platforms, details, stairs). - PlayerActor: Horizontal and vertical movement, stairs, tile-based collision (no rectangle lists).
- Tilemap: 4bpp (
TileMap4bpp), with viewport culling and palette cache in the engine. Level 40×30 tiles (320×240 px). - No camera: The view does not follow the player; for scroll you would use
Camera2Dand apply offset in the renderer (as in CameraDemo).
Engine features used¶
- Tile-based collision: Direct tile checks around the player (
getTileAt), instead of iterating overplatformRects. - 4bpp sprites: Player with animations (idle, run, jump) from generated headers (e.g. Sprite Compiler).
- Rendering optimizations: Viewport culling in
drawTileMap, optimized 4bppdrawSprite, layers culled by viewport. - Optional: Scene arena, DMA, IRAM_ATTR on critical paths (per example optimization plan).
Patterns used¶
- Tile-based collision: Single O(1) access per tile instead of O(N) rectangles.
- Stair detection: Single result reused for collision and state change.
- Simplified hitbox: Fewer vertical check points (head and feet).
Lessons learned¶
- Tile-based collision scales better than rectangle lists on large levels.
- Viewport and 4bpp optimizations improve FPS on ESP32.
- Metroidvania serves as a reference for platformers with tilemap and camera.
Pong¶
Location: src/examples/Pong/
Architecture¶
Pong demonstrates physics and collision handling:
- PhysicsActor: Ball uses
PhysicsActorfor automatic physics - Collision Callbacks: Paddles and ball handle collisions
- Score System: Simple score tracking and display
- Game State: Reset and game over handling
Key Systems¶
Physics Setup¶
class BallActor : public pixelroot32::core::PhysicsActor {
public:
BallActor(float x, float y, float speed, int radius)
: PhysicsActor(x, y, radius * 2, radius * 2) {
setRestitution(0.8f); // Bouncy
setFriction(0.1f); // Low friction
setWorldSize(240, 240);
}
};
Collision Response¶
void BallActor::onCollision(pixelroot32::core::Actor* other) {
// Adjust ball position
// Modify velocity based on impact point
// Play bounce sound
}
Patterns Used¶
- Physics Integration: Uses
PhysicsActorfor automatic movement - Collision Response: Custom collision handling
- Score Management: Simple state tracking
- Audio Feedback: Sound on collision
Lessons Learned¶
PhysicsActorsimplifies physics-based games- Collision callbacks allow custom response logic
- Simple games can be very effective
Snake¶
Location: src/examples/Snake/
Architecture¶
Snake demonstrates entity pooling and grid-based movement:
- Entity Pooling: Snake segments are pooled
- Grid Movement: Movement constrained to grid
- Game Logic: Food spawning, collision detection
- State Management: Game over, reset functionality
Key Systems¶
Entity Pooling¶
class SnakeScene {
private:
std::vector<SnakeSegmentActor*> segmentPool;
std::vector<SnakeSegmentActor*> snakeSegments;
void resetGame() {
// Reuse pooled segments
for (int i = 0; i < initialLength; ++i) {
SnakeSegmentActor* segment = segmentPool[i];
segment->resetAlive();
snakeSegments.push_back(segment);
addEntity(segment);
}
}
};
Grid-Based Movement¶
class SnakeSegmentActor : public pixelroot32::core::Actor {
private:
int cellX, cellY; // Grid position
public:
void setCellPosition(int x, int y) {
cellX = x;
cellY = y;
// Convert to world position
this->x = cellX * CELL_SIZE;
this->y = cellY * CELL_SIZE;
}
};
Patterns Used¶
- Object Pooling: Segments are pre-allocated and reused
- Grid System: Discrete grid-based movement
- Linked List: Snake segments form a linked structure
- Food Spawning: Random food placement with collision checking
Lessons Learned¶
- Entity pooling is essential for dynamic entities
- Grid-based movement simplifies collision detection
- Pre-allocation avoids memory fragmentation
TicTacToe¶
Location: src/examples/TicTacToe/
Architecture¶
TicTacToe demonstrates turn-based logic and simple AI:
- Turn Management: Player vs AI turns
- Game Board: 3x3 grid representation
- Win Detection: Check for winning conditions
- Simple AI: Random move selection
Key Systems¶
Board Representation¶
class TicTacToeScene {
private:
int board[3][3]; // 0=empty, 1=X, 2=O
bool playerTurn = true;
bool makeMove(int row, int col, int player) {
if (board[row][col] == 0) {
board[row][col] = player;
return true;
}
return false;
}
};
Win Detection¶
int checkWinner() {
// Check rows
for (int i = 0; i < 3; i++) {
if (board[i][0] == board[i][1] && board[i][1] == board[i][2]) {
return board[i][0];
}
}
// Check columns, diagonals...
return 0; // No winner
}
Patterns Used¶
- State Machine: Turn-based state management
- Grid Logic: 2D array for board representation
- Simple AI: Random valid move selection
- UI Integration: Buttons for player input
Lessons Learned¶
- Turn-based games are straightforward to implement
- Simple AI can be effective for basic games
- Grid-based logic is easy to reason about
CameraDemo¶
Location: src/examples/CameraDemo/
Architecture¶
CameraDemo demonstrates scrolling and parallax:
- Camera2D: Camera following player
- Tilemap: Level built with tilemap
- Parallax: Multiple background layers
- Platformer Physics: Player with jumping and gravity
Key Systems¶
Camera Setup¶
class CameraDemoScene {
private:
pixelroot32::graphics::Camera2D camera;
float levelWidth;
public:
void init() override {
camera = pixelroot32::graphics::Camera2D(240, 240);
camera.setBounds(0, levelWidth - 240);
}
void update(unsigned long deltaTime) override {
camera.followTarget(player->x, player->y);
}
void draw(pixelroot32::graphics::Renderer& renderer) override {
camera.apply(renderer);
renderer.drawTileMap(levelTileMap, 0, 0, Color::White);
Scene::draw(renderer);
}
};
Platformer Physics¶
class PlayerCube : public pixelroot32::core::PhysicsActor {
public:
void update(unsigned long deltaTime) override {
// Input handling
// Gravity application
// Jump logic
// Platform collision
PhysicsActor::update(deltaTime);
}
};
Patterns Used¶
- Camera Following: Dead-zone camera following
- Tilemap Rendering: Efficient level rendering
- Parallax Scrolling: Multiple background layers
- Platform Collision: Custom collision with platforms
Lessons Learned¶
- Camera system enables large levels
- Tilemaps are efficient for level data
- Parallax adds depth to 2D games
- Platform collision requires custom logic
SpritesDemo¶
Location: src/examples/SpritesDemo/
Architecture¶
SpritesDemo showcases advanced sprite formats:
- 2bpp Sprites: 4-color sprite format
- 4bpp Sprites: 16-color sprite format (if enabled)
- Animation: Sprite animation examples
- Format Comparison: Side-by-side format display
Key Systems¶
2bpp Sprite Usage¶
#ifdef PIXELROOT32_ENABLE_2BPP_SPRITES
static const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {
SPRITE_DATA,
SPRITE_PALETTE,
16, 32, 4
};
renderer.drawSprite(SPRITE_2BPP, x, y, false);
#endif
Animation Display¶
class SpritesDemoActor : public pixelroot32::core::Entity {
private:
unsigned long timer = 0;
uint8_t currentFrame = 0;
public:
void update(unsigned long deltaTime) override {
timer += deltaTime;
if (timer >= 150) {
timer -= 150;
currentFrame = (currentFrame + 1) % 9;
}
}
};
Patterns Used¶
- Format Comparison: Shows different sprite formats
- Animation Loop: Frame-based animation
- Conditional Compilation: Uses build flags
Lessons Learned¶
- Advanced formats provide more color options
- Animation is straightforward with frame arrays
- Build flags enable/disable experimental features
Common Patterns Across Examples¶
Screen Resolution¶
All examples are configured for a 240x240 screen resolution.
Scene Initialization¶
All examples follow this pattern:
void init() override {
// 1. Set palette
pixelroot32::graphics::setPalette(PaletteType::NES);
// 2. Create background entity
auto bg = std::make_unique<BackgroundEntity>();
addEntity(bg.get());
entities.push_back(std::move(bg)); // Keep ownership
// 3. Create game entities
auto playerPtr = std::make_unique<PlayerActor>(...);
addEntity(playerPtr.get());
player = playerPtr.get(); // Keep raw pointer for logic
entities.push_back(std::move(playerPtr)); // Keep ownership
// 4. Initialize game state
resetGame();
}
Update Pattern¶
void update(unsigned long deltaTime) override {
// 1. Process input
handleInput();
// 2. Update game logic
updateGameLogic();
// 3. Call parent update (updates all entities)
Scene::update(deltaTime);
// 4. Post-update logic
checkGameState();
}
Draw Pattern¶
void draw(pixelroot32::graphics::Renderer& renderer) override {
// 1. Apply camera (if used)
camera.apply(renderer);
// 2. Draw background/tilemap
if (background) {
renderer.drawTileMap(*background, 0, 0, Color::White);
}
// 3. Call parent draw (draws all entities)
Scene::draw(renderer);
// 4. Draw UI/HUD
drawHUD(renderer);
}
Learning Path¶
Beginner Examples¶
- Pong: Basic physics and collisions
- TicTacToe: Turn-based logic
- Snake: Entity pooling and grid
Intermediate Examples¶
- CameraDemo: Camera and parallax
- SpritesDemo: 2bpp and 4bpp formats
- BrickBreaker: Physics, particles and audio
Advanced Examples¶
- Space Invaders: Full game (1bpp sprites, collisions, audio)
- Metroidvania: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera)
Code Study Recommendations¶
For Learning Physics¶
- Study
Pong/BallActor.cpp- PhysicsActor usage - Study
CameraDemo/PlayerCube.cpp- Platformer physics
For Learning Collisions¶
- Study
SpaceInvaders- Complex collision layers - Study
Pong- Simple collision response
For Learning Memory Management¶
- Study
Snake/SnakeScene.cpp- Entity pooling - Study
SpaceInvaders- Projectile pooling
For Learning Audio¶
- Study
SpaceInvaders- Music and sound effects - Study
Pong- Simple audio integration
For Learning UI¶
- Study
TicTacToe- Button-based UI - Study menu scenes - Layout usage
Extending Examples¶
Adding Features¶
- Pong: Add power-ups, multiple balls
- Snake: Add obstacles, multiple food types
- Space Invaders: Add boss battles, power-ups
Creating Variations¶
- Pong: Make it vertical, add walls
- Snake: Change to hexagonal grid
- TicTacToe: Make it 4x4 or 5x5
Best Practices from Examples¶
- Pre-allocate Resources: All examples pre-allocate entities
- Use Object Pooling: For frequently created/destroyed entities
- Organize by Layers: Clear collision layer organization
- Separate Concerns: Game logic separate from rendering
- State Management: Clear game state handling
See Also¶
- Code Examples - Reusable code snippets
- API Reference Overview - Complete API documentation
- Manual - Game Development - Detailed guides
Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.