This example demonstrates how to compose large characters from multiple hardware sprites using the metasprite system. The SNES OAM supports only two sprite sizes per frame (selected via register $2101), so characters larger than one hardware sprite must be assembled from several. This example displays hero characters at three different OBJ size configurations – 8x8/16x16, 8x8/32x32, and 16x16/32x32 – and lets you switch between them with the D-PAD to see how the same character is built from different hardware sprite sizes.
Screenshot
What You'll Learn
- How metasprites combine multiple hardware sprites into one logical character
- How the SNES OBJ size register ($2101) selects two active sprite sizes per frame
- How
MetaspriteItem structures define relative positions and tile offsets
- How
oamDrawMeta() places a list of hardware sprites from a single function call
- How VRAM tile layout relates to OAM tile numbering
SNES Concepts
OBJ Size Modes (Register $2101)
The SNES PPU supports 128 hardware sprites (OAM entries), but only two sizes are available at any time, controlled by bits 5-7 of register $2101 (OBJSEL). Each OAM entry has a size bit selecting either "small" or "large." The three modes used in this example:
| OBJSEL Mode | Small | Large | Constant |
| OBJ_SIZE8_L16 | 8x8 | 16x16 | 0x00 (bits 7-5) |
| OBJ_SIZE8_L32 | 8x8 | 32x32 | 0x20 (bits 7-5) |
| OBJ_SIZE16_L32 | 16x16 | 32x32 | 0x60 (bits 7-5) |
You cannot mix three sizes (e.g. 8x8 + 16x16 + 32x32) in one frame. Choose the two sizes that best suit your game's needs.
MetaspriteItem Structure
A metasprite is defined as an array of MetaspriteItem entries, terminated by METASPR_TERM. Each item specifies the X/Y offset from the metasprite origin, a tile number, and attribute flags (palette, priority, flip):
};
static const MetaspriteItem hero16_frame0[]
16x16 metasprite: 32x48 character (2x3 grid of 16x16)
Definition main.c:63
#define OBJ_PRIO(prio)
Metasprite priority attribute macro.
Definition sprite.h:479
#define METASPR_ITEM(dx, dy, tile, attr)
Metasprite item macro (PVSnesLib compatible)
Definition sprite.h:467
#define METASPR_TERM
End marker for metasprite data (dx = -128)
Definition sprite.h:470
#define OBJ_PAL(pal)
Metasprite palette attribute macro.
Definition sprite.h:476
VRAM Tile Layout and OAM Tile Numbers
OAM tile numbers reference 8x8 tiles in VRAM, regardless of the hardware sprite size. A 16x16 sprite uses 4 consecutive 8x8 tiles (2 across x 2 down in the VRAM character grid, which is 16 tiles wide). A 32x32 sprite uses 16 tiles. The -T flag in gfx4snes transposes the sprite sheet to match the SNES OBJ VRAM grid layout, so tile indices in the metasprite data correspond directly to positions in the converted .pic file.
This example packs all three sprite sheets into contiguous VRAM starting at $0000:
| Sprite Set | VRAM Address | Tiles | Base Tile |
| hero32 | $0000 | 192 | 0 |
| hero16 | $0C00 | 96 | 192 |
| hero8 | $1200 | 16 | 288 |
Controls
| Button | Action |
| D-PAD Up | Select previous OBJ size mode |
| D-PAD Down | Select next OBJ size mode |
How It Works
1. Load all sprite tiles – Three sprite sheets are DMA'd to contiguous VRAM regions while the screen is blanked:
void dmaCopyCGram(u8 *source, u16 startColor, u16 size)
Copy palette data to CGRAM (PVSnesLib compatible)
void dmaCopyVram(u8 *source, u16 vramAddr, u16 size)
Copy data to VRAM (PVSnesLib compatible)
#define VRAM_HERO32
Metasprite frame definitions generated by gfx4snes -P.
Definition main.c:74
#define VRAM_HERO16
VRAM word address for 16x16 hero tiles (after 32x32 tiles)
Definition main.c:76
#define HERO8_TILES
Number of tiles in the 8x8 hero sprite sheet.
Definition main.c:89
#define HERO32_TILES
Number of tiles in the 32x32 hero sprite sheet.
Definition main.c:85
#define HERO16_TILES
Number of tiles in the 16x16 hero sprite sheet.
Definition main.c:87
#define TILE_BYTES
Bytes per 4bpp 8x8 tile (8 rows x 4 bits/pixel x 8 pixels / 8 bits = 32)
Definition main.c:91
#define VRAM_HERO8
VRAM word address for 8x8 hero tiles (after 16x16 tiles)
Definition main.c:78
2. Set OBJ size mode – When the user switches modes, the OBJSEL register is updated and OAM is cleared:
else
}
void WaitForVBlank(void)
Wait for next VBlank period.
u16 selectedItem
Definition main.c:86
void changeObjSize(void)
Reinitialize sprite engine and upload all tiles during force blank.
Definition main.c:137
#define REG_OBJSEL
Object (sprite) size and base (W)
Definition registers.h:52
#define OBJ_SIZE16_L32
Definition sprite.h:52
#define OBJ_SIZE_TO_REG(size)
Macro to convert size index to OBJSEL register value.
Definition sprite.h:57
#define OBJ_SIZE8_L32
Definition sprite.h:50
void oamClear(void)
Clear all sprites.
#define OBJ_SIZE8_L16
Sprite size indices (for oamInit, oamInitGfxSet)
Definition sprite.h:49
3. Draw metasprites – oamDrawMeta() iterates the MetaspriteItem array, placing hardware sprites at the origin plus each item's (dx, dy) offset. The baseTile parameter offsets all tile indices so that different sprite sheets can share the same VRAM region. It returns the next available OAM ID, enabling sequential placement of multiple metasprites:
static u16 bx
Definition main.c:159
static const MetaspriteItem hero8_frame0[]
8x8 metasprite: 16x16 character (2x2 grid of 8x8)
Definition main.c:74
#define BASE_TILE_8
Definition main.c:103
#define BASE_TILE_16
Definition main.c:102
u8 oamDrawMeta(u8 startId, s16 x, s16 y, const MetaspriteItem *meta, u16 baseTile, u8 basePalette, u8 size)
Draw a metasprite (PVSnesLib compatible)
#define OBJ_LARGE
Definition sprite.h:104
#define OBJ_SMALL
Sprite size selection.
Definition sprite.h:103
4. Text menu – A text overlay on BG1 (Mode 1, 4bpp font) shows the current selection and the two active sprite sizes, updated whenever the user presses Up or Down.
Project Structure
metasprite/
├── main.c — OBJ mode switching, metasprite drawing, text menu
├── data.asm — ROM data: three sprite tile sets and palettes
├── Makefile — Build configuration with gfx4snes -T (transpose) rules
└── res/
├── spritehero32.png — 64x64 hero sprite sheet (32x32 tiles, 4bpp)
├── spritehero16.png — 32x48 hero sprite sheet (16x16 tiles, 4bpp)
├── spritehero8.png — 16x16 hero sprite sheet (8x8 tiles, 4bpp)
├── hero32_meta.inc — MetaspriteItem definitions for 32x32 frames
├── hero16_meta.inc — MetaspriteItem definitions for 16x16 frames
└── hero8_meta.inc — MetaspriteItem definitions for 8x8 frames
Build & Run
cd $OPENSNES_HOME
make -C examples/graphics/sprites/metasprite
Then open metasprite.sfc in your emulator (Mesen2 recommended).