Loading...
Searching...
No Matches
Dynamic Sprite -- VRAM Streaming for Animated Sprites
Screenshot

What This Example Shows

How to use the dynamic sprite system to stream sprite tile data into VRAM every frame. Unlike static sprites (where all frames live in VRAM permanently), dynamic sprites upload only the current animation frame, saving VRAM space when sprites have many frames.

This demo displays 4 animated 16x16 sprites, each cycling through 24 frames of animation.

Prerequisites

Read sprites/simple_sprite first (OAM basics), then sprites/animated_sprite (frame-based animation).

Controls

No interactive controls. Four sprites animate automatically.

Build & Run

cd $OPENSNES_HOME
make -C examples/graphics/sprites/dynamic_sprite

Then open dynamic_sprite.sfc in your emulator (Mesen2 recommended).

How It Works

1. Initialize the dynamic sprite engine

static const OamDynamicConfig dyn = {
.vramLarge = 0x0000,
.vramSmall = 0x1000,
.slotLargeInit = 0,
.slotSmallInit = 0,
.sizeMode = OBJ_SIZE8_L16,
};
static u16 bx
Definition main.c:159
void oamDynamicInit(const OamDynamicConfig *cfg)
Initialize the dynamic sprite engine from a config struct.
#define OBJ_SIZE8_L16
Sprite size indices (for oamInit, oamInitGfxSet)
Definition sprite.h:49
Configuration for the dynamic sprite engine.
Definition sprite.h:574
u16 vramLarge
Definition sprite.h:575

This configures the engine:

  • VRAM base for large tiles: $0000
  • VRAM base for small tiles: $1000
  • Sprite size pair: 8x8 small / 16x16 large

2. Set up sprites via the oambuffer

oambuffer[0].oamx = 64;
oambuffer[0].oamy = 100;
u8 spr16_tiles[]
ROM source for the 16x16 sprite sheet tile data.
#define OAM_SET_GFX(id, gfx)
Set sprite graphics address (bank $00 only)
Definition sprite.h:225
t_sprites oambuffer[128]
Dynamic sprite buffer (128 entries, 2048 bytes)
#define OBJ_PRIO(prio)
Metasprite priority attribute macro.
Definition sprite.h:479
u16 oamframeid
Definition sprite.h:191
s16 oamx
Definition sprite.h:189
s16 oamy
Definition sprite.h:190
u8 oamattribute
Definition sprite.h:192
u8 oamrefresh
Definition sprite.h:193

Each dynamic sprite has:

  • oamframeid: which animation frame to display (index into the sprite sheet)
  • oamrefresh: set to 1 when the frame changes – tells the engine to re-upload tiles
  • OAM_SET_GFX: points to the source tile data in ROM

3. Draw and upload every frame

oamDynamicDraw(0); /* Queue sprite 0; NMI auto-flushes the VRAM tile
* queue and hides last frame's leftovers. */
void oamDynamicDraw(u16 id)
Draw a dynamic sprite — engine picks the size routine.

The draw call runs every frame in the main loop. Only sprites with oamrefresh = 1 actually trigger a DMA transfer. The NMI handler finalises the frame automatically — it hides leftover sprites, resets the slot allocator, and DMAs the queued tiles to VRAM — so user code never needs to issue a manual flush.

4. Animate by changing frame IDs

if (frame0 >= 24) frame0 = 0;
static u16 frame0
Animation frame index for sprite 0 (0-23, wraps at 24)
Definition main.c:65

Every 8 frames, the animation advances. Setting oamrefresh = 1 tells the engine to upload the new frame's tiles to VRAM on the next VBlank.

SNES Concepts

VRAM Budget During VBlank

The SNES can only write to VRAM during VBlank. With DMA, you get roughly 4-5 KB per frame. Each 16x16 sprite frame is 128 bytes (4 tiles x 32 bytes). With 4 sprites updating simultaneously, that is 512 bytes – well within budget.

Static vs Dynamic Sprites

  • Static: All frames pre-loaded in VRAM. Frame selection is instant (just change the tile number). Downside: a 24-frame sprite uses 3 KB of VRAM permanently.
  • Dynamic: Only the current frame lives in VRAM. Supports hundreds of frames with minimal VRAM usage. Downside: each frame change costs DMA time during VBlank.

Dynamic sprites are the right choice when sprites have many animation frames (RPG characters with walk cycles, attack animations, etc.) or when multiple unique characters share limited VRAM space.

Force Blank for Initial Setup

This demo uses setScreenOff() (which sets REG_INIDISP = 0x80) during setup to allow unlimited VRAM writes. During gameplay, writes happen via DMA in VBlank only.

Project Structure

File Purpose
main.c Dynamic sprite initialization, animation loop
data.asm Sprite tile data (24-frame sheet) and palette via .INCBIN
res/sprite16_grid.png 16x16 sprite sheet source
Makefile LIB_MODULES := console sprite sprite_dynamic sprite_lut dma background input

Going Further

  • Move sprites with input: Add padHeld() and update oambuffer[0].oamx/oamy based on D-pad direction.
  • Mix static and dynamic: Use static sprites for simple objects (bullets, UI elements) and dynamic sprites for complex animated characters.
  • Explore related examples:
    • games/breakout – Dynamic sprites in a real game context
    • games/likemario – Dynamic sprites with scrolling backgrounds