World-space sprites — a camera, culling, a depth sort and OAM in one call per batch.
More...
World-space sprites — a camera, culling, a depth sort and OAM in one call per batch.
A scrolling game keeps its actors in WORLD coordinates (a 512 x 448 field here) and shows a 256 x 224 window of it. Every frame it has to subtract the camera from each actor, decide whether the result is on screen, write the OAM entry or hide it, and set the ninth x bit for a sprite half out on the left. In compiled C that loop cost a real game about 6,000 master cycles a sprite (issue #165); oamPlaceWorld() does it in assembly for a whole batch, about 1,700 a sprite on the library bench (docs/PERF.md, row world).
The game keeps one array per property (structure of arrays: what the 65816 indexes cheaply) and fills an OamWorldBatch with their addresses once. Two batches, because a batch has one size: 17 coins of 16 x 16 (the SMALL size of OBJ_SIZE16_L32) and six walkers of 32 x 32 (the LARGE size, set once with oamSetSize(): the call never touches the size bit).
The walkers show order. Seen from above, the one lower on the screen is nearer and must be drawn in front — and OAM order is drawing order, the lower slot in front. So the walkers are sorted by y every frame (an insertion sort of six bytes) and the batch's order says which walker each slot shows: the arrays stay indexed by the walker, only the six bytes of order move. This is what a real game does with twenty players and robots on a pitch.
Hold the D-pad to move the camera. visible[] comes back from the call: the example counts the coins in view with it (world_visible); a game reads it to stream the animation frames of the sprites that are shown and skip the others — a sprite off screen should cost no VRAM DMA.
ROM mode: LoROM (project default).
- SNES Concepts
- World coordinates minus a camera = screen coordinates; a sprite partly off the left edge needs the ninth x bit (x in 256..511), which the PPU reads as negative
- OAM culling: a sprite off screen is hidden (x = 257, y = 240), not left wherever it was
- OAM order is drawing order: a depth sort permutes slots, not data
- Structure of arrays for per-sprite data, and the size bit set once
- What to Observe
- Six square walkers pace up and down near the centre and pass in front of and behind one another as their y changes; gold coins drift over the field; the D-pad scrolls the camera and stops at the world's edge. world_visible (probe oracle) follows how many coins are in view, world_front which walker is in the front slot, cam_x / cam_y the camera.
- Modules Used
- console, dma, background, sprite, input, tile
- See also
- lib/include/snes/sprite.h — OamWorldBatch, oamPlaceWorld, oamSetSize
-
examples/sprites/sprite_swarm — the same sprites moved by direct OAM writes, no camera