Graphics & Sprites
VRAM layout, spritesheets, backgrounds, tiles, metasprites, shadow OAM, and CGB attribute maps. The graphics system is implemented in C.
VRAM Layout
- Game Boy has 384 tiles in VRAM (3 banks of 128)
- CGB adds a second VRAM bank for attribute maps
- Tile data: 8×8 pixels, 2bpp (2 bits per pixel), 16 bytes per tile
- Background map: 32×32 tiles (256×256 pixels), wrapping
- Window map: separate 32×32 tile map, overlaid on background
- OAM: 40 sprite entries, each 4 bytes (Y, X, tile, attributes)
Sprite Mode
- GB Studio 4.2 uses 8×16 sprite mode (tall sprites)
- Each hardware sprite is 8×16 pixels
- Larger actors are composed of multiple hardware sprites (metasprites)
- 40 hardware sprites max across all actors
Spritesheet Structure
typedef struct spritesheet_t {
UINT8 num_frames;
UINT8 num_tiles; // tiles per frame
// followed by metasprite data and tile data
} spritesheet_t;
- Spritesheets are banked (
far_ptr_treferences) - Each frame contains a metasprite definition + tile data
- Metasprite defines relative positions of hardware sprites
Background System
- Backgrounds are composed of tilemap + tileset
image_tile_width,image_tile_height— scene dimensions in tilesimage_width_subpx,image_height_subpx— scene dimensions in subpixels- Tilemap: array of tile indices
- CGB attribute map: palette + bank selection per tile
- Tilesets are deduplicated (shared tiles across the map)
Background Loading
load_scene()loads background tileset into VRAM- Tilemap loaded via scroll system (incremental row/column loading)
scroll_repaint()— full screen repaint (loads all visible rows/columns)scroll_load_row(x, y)— load a single tile row (NONBANKED, not declared in scroll.h)- Must add manual declaration:
void scroll_load_row(UBYTE x, UBYTE y);
Tile Replacement
- Individual tiles can be replaced at runtime
- GBVM:
VM_REPLACE_TILE_XYreplaces a tile at position VM_LOAD_TILESETloads additional tiles into VRAM- Used for dynamic background changes (animations, state changes)
Shadow OAM (Double Buffering)
shadow_OAM2[40]— secondary OAM buffertoggle_shadow_OAM()— swap buffers before renderingactivate_shadow_OAM()— apply shadow OAM to hardware, hide unused sprite slots- Prevents flickering during OAM updates
- OAM DMA transfers shadow buffer to hardware during VBlank
Metasprite Rendering
actors_render()iterates active actor list- For each actor: reads metasprite data, writes hardware sprites to shadow OAM
- Sprite priority: first in OAM = highest priority (drawn on top when overlapping)
- Actor render order determined by position in active linked list
- Use EditActorActiveIndex plugin to control render order
CGB Attributes
Each background tile has an attribute byte:
- Bits 0–2: palette number (0–7)
- Bit 3: VRAM bank (0 or 1)
- Bit 4: unused
- Bit 5: horizontal flip
- Bit 6: vertical flip
- Bit 7: BG-to-OAM priority
Each sprite has similar attributes in OAM entry byte 3.
Common Graphics Operations
Examples of common graphics tasks in plugin and engine code:
- Loading a tileset from C
- Replacing background tiles
- Managing sprite priorities
- Working with CGB attributes
Submap System
- Copy rectangular regions from background tilemap to overlay window
- SubmappingExPlugin: row-by-row copy using
tmp_tile_buffer[32] - Supports CGB attribute maps and tile offset
- Used for menus, HUDs, inventory screens