Sprite¶
Low-level bitmap descriptor and multi-layer composition for retro rendering.
Description¶
Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:
- 1bpp (Standard): Monochrome sprites, most memory-efficient
- 2bpp (Experimental): 4 colors per sprite
- 4bpp (Experimental): 16 colors per sprite
- MultiSprite: Multi-layer 1bpp sprites for multi-color effects
Namespace¶
namespace pixelroot32::graphics {
struct Sprite {
// ...
};
struct MultiSprite {
// ...
};
struct SpriteLayer {
// ...
};
}
Sprite Structure (1bpp)¶
Compact sprite descriptor for monochrome bitmapped sprites.
Members¶
const uint16_t* data: Pointer to packed row data (size = height)uint8_t width: Sprite width in pixels (≤ 16)uint8_t height: Sprite height in pixels
Data Format¶
Sprites are stored as an array of 16-bit rows. Each row packs horizontal pixels into bits:
- Bit 0: Leftmost pixel of the row
- Bit (width - 1): Rightmost pixel of the row
- Bit value 1: Pixel on (colored)
- Bit value 0: Pixel off (transparent/background)
Only the lowest width bits of each row are used.
Example¶
// 8x8 sprite (smiley face)
static const uint16_t SMILEY_DATA[] = {
0b00111100, // Row 0: ████
0b01111110, // Row 1: ██████
0b11011011, // Row 2: ██ ██ ██
0b11111111, // Row 3: ████████
0b11011011, // Row 4: ██ ██ ██
0b01100110, // Row 5: ██ ██
0b01111110, // Row 6: ██████
0b00111100 // Row 7: ████
};
static const Sprite SMILEY_SPRITE = {
SMILEY_DATA,
8, // width
8 // height
};
SpriteLayer Structure¶
Single monochrome layer used by layered sprites.
Members¶
const uint16_t* data: Pointer to packed row data for this layerColor color: Color used for "on" pixels in this layer
Notes¶
- Each layer uses the same width/height as its owning MultiSprite
- Layers can have different colors
- Layers are drawn in array order
MultiSprite Structure¶
Multi-layer, multi-color sprite built from 1bpp layers.
Members¶
uint8_t width: Sprite width in pixels (≤ 16)uint8_t height: Sprite height in pixelsconst SpriteLayer* layers: Pointer to array of layersuint8_t layerCount: Number of layers in the array
Notes¶
- Combines several 1bpp layers with different colors
- Layers are drawn in array order
- Enables multi-color sprites without higher bit-depths
- More layers = more draw calls (but still efficient)
Example¶
// Outline layer
static const uint16_t OUTLINE_DATA[] = {
0b11111111,
0b10000001,
0b10000001,
0b11111111
};
// Fill layer
static const uint16_t FILL_DATA[] = {
0b00000000,
0b01111110,
0b01111110,
0b00000000
};
static const SpriteLayer LAYERS[] = {
{OUTLINE_DATA, Color::Black}, // Layer 0: Black outline
{FILL_DATA, Color::Red} // Layer 1: Red fill
};
static const MultiSprite PLAYER_MULTISPRITE = {
8, // width
8, // height
LAYERS, // layers array
2 // layer count
};
Sprite2bpp Structure (Experimental)¶
2-bit per pixel sprite (4 colors).
Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag
Members¶
const uint8_t* data: Packed 2bpp dataconst Color* palette: Local palette (4 colors)uint8_t width: Sprite widthuint8_t height: Sprite heightuint8_t paletteSize: Number of colors (typically 4)
Notes¶
- Experimental feature
- Uses more memory than 1bpp
- Each pixel can be one of 4 colors from local palette
Sprite4bpp Structure (Experimental)¶
4-bit per pixel sprite (16 colors).
Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag
Members¶
const uint8_t* data: Packed 4bpp dataconst Color* palette: Local palette (16 colors)uint8_t width: Sprite widthuint8_t height: Sprite heightuint8_t paletteSize: Number of colors (typically 16)
Notes¶
- Experimental feature
- Uses more memory than 1bpp/2bpp
- Each pixel can be one of 16 colors from local palette
Sprite Animation¶
SpriteAnimationFrame Structure¶
Frame that can reference either a Sprite or a MultiSprite.
Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame
Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types
SpriteAnimation Structure¶
Lightweight, step-based sprite animation controller.
Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)
Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite
Example:
static const SpriteAnimationFrame WALK_FRAMES[] = {
{&walkFrame1, nullptr},
{&walkFrame2, nullptr},
{&walkFrame3, nullptr},
{&walkFrame2, nullptr} // Loop back
};
static SpriteAnimation walkAnimation = {
WALK_FRAMES,
4, // frameCount
0 // current
};
// In update
walkAnimation.step();
const Sprite* currentSprite = walkAnimation.getCurrentSprite();
Usage Examples¶
Creating 1bpp Sprites¶
// 8x8 player sprite
static const uint16_t PLAYER_DATA[] = {
0b00111100,
0b01111110,
0b11111111,
0b11011011,
0b11111111,
0b01111110,
0b00111100,
0b00011000
};
static const Sprite PLAYER_SPRITE = {
PLAYER_DATA,
8, // width
8 // height
};
// Use in rendering
renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);
Creating MultiSprite¶
// Outline layer
static const uint16_t OUTLINE[] = {
0b11111111,
0b10000001,
0b10000001,
0b11111111
};
// Fill layer
static const uint16_t FILL[] = {
0b00000000,
0b01111110,
0b01111110,
0b00000000
};
static const SpriteLayer LAYERS[] = {
{OUTLINE, Color::Black},
{FILL, Color::Red}
};
static const MultiSprite ENEMY_SPRITE = {
8, // width
8, // height
LAYERS, // layers
2 // layer count
};
// Use in rendering
renderer.drawMultiSprite(ENEMY_SPRITE, 100, 100);
Sprite Animation¶
class AnimatedActor : public pixelroot32::core::Actor {
private:
SpriteAnimation animation;
unsigned long animTimer = 0;
unsigned long animInterval = 200; // 200ms per frame
public:
void update(unsigned long deltaTime) override {
Actor::update(deltaTime);
animTimer += deltaTime;
if (animTimer >= animInterval) {
animTimer -= animInterval;
animation.step();
}
}
void draw(pixelroot32::graphics::Renderer& renderer) override {
const Sprite* frame = animation.getCurrentSprite();
if (frame) {
renderer.drawSprite(*frame,
static_cast<int>(x),
static_cast<int>(y),
Color::White);
}
}
};
Sprite Flipping¶
Sprites can be flipped horizontally:
// Draw normal
renderer.drawSprite(sprite, 100, 100, Color::White, false);
// Draw flipped
renderer.drawSprite(sprite, 100, 100, Color::White, true);
Performance Considerations¶
- 1bpp sprites: Most efficient (integer-only operations)
- MultiSprite: Each layer is a separate draw call (still efficient)
- 2bpp/4bpp: Experimental, uses more memory and CPU
- Storage: Store sprite data in flash (const/constexpr) for best performance
- Size limit: Sprites are limited to 16 pixels wide for 1bpp format
ESP32 Considerations¶
- Memory: Store sprite data in flash, not RAM
- Sprite size: Smaller sprites = faster drawing
- Format choice: Use 1bpp when possible for best performance
- MultiSprite: More layers = more draw calls (but acceptable)
See Also¶
- Renderer - Rendering system that draws sprites
- Color - Color constants for sprites
- Manual - Sprites and Animation
- API Overview