Loading...
Searching...
No Matches
Collision Detection Demo

‍AABB sprite-vs-sprite and tile-based wall collision in a walled arena. Move the white square with the D-pad. Overlapping a red enemy turns both green.

Build & Run

cd $OPENSNES_HOME
make -C examples/basics/collision_demo

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

Controls

Button Action
D-Pad Move player sprite (2 pixels/frame)

What You'll Learn

  • AABB collision via collideRect() with Rect struct pointers
  • Tile-based wall collision via collideTile() with a collision map array
  • Per-axis rejection for sliding along walls instead of stopping dead
  • Direct oamMemory[] buffer writes (avoiding oamSet()'s 158-byte stack overhead)
  • OAM high table management for X high bit and sprite size
  • Palette bank swapping for visual collision feedback

SNES Concepts

AABB Collision with Rect Pointers

collideRect() takes two Rect* pointers and returns non-zero if the rectangles overlap. The Rect struct holds x, y, width, and height fields:

collision_flags |= (1 << i);
}
static u8 collision_flags
Bitmask tracking which enemies the player is currently overlapping.
Definition main.c:74
#define PLAYER_SIZE
Definition main.c:49
static Rect player_box
Player bounding box for AABB collision tests via collideRect()
Definition main.c:59
static Rect enemy_box[4]
Bounding boxes for each enemy, rebuilt every frame for collideRect()
Definition main.c:66
static s16 player_y
Player Y position in screen coordinates.
Definition main.c:57
static s16 player_x
Player X position in screen coordinates.
Definition main.c:55
u8 collideRect(Rect *a, Rect *b)
Check rectangle vs rectangle collision (AABB)
static u8 i
Definition main.c:156
u16 height
Definition collision.h:58
s16 y
Definition collision.h:56
u16 width
Definition collision.h:57
s16 x
Definition collision.h:55

The example checks the player against four static enemy sprites each frame, storing results in a bitmask (collision_flags). Bit N set means enemy N is overlapping.

Tile-Based Wall Collision

collideTile() takes four parameters: a pixel X coordinate, a pixel Y coordinate, a pointer to the collision map array, and the map width in tiles:

static u8 collision_map[16 *14]
Tile-based collision map (16x14 = 224 bytes, 128x112 pixel area)
Definition main.c:94
#define MAP_WIDTH
Definition main.c:80
u8 collideTile(s16 px, s16 py, u8 *tilemap, u16 mapWidth)
Check collision with tile at pixel coordinates.
static u16 bx
Definition main.c:159

It converts the pixel position to a tile index (dividing by 8) and returns the tile value – 1 for solid, 0 for passable. The example checks all four corners of the player's 8x8 bounding box to detect wall overlap.

Per-Axis Sliding

When a diagonal movement would collide, each axis is tested independently. If only the X component collides, the Y movement still applies (and vice versa). This lets the player slide along walls:

} else {
}
static u8 check_wall_collision(s16 new_x, s16 new_y)
Test whether a position would overlap a solid wall tile.
Definition main.c:371

Direct oamMemory[] Writes

Instead of calling oamSet() (which has a 158-byte stack frame per call), the example writes sprite data directly into the OAM buffer. Each sprite is 4 bytes: X low, Y, tile number, and attribute byte (priority + palette bank):

oamMemory[2] = 0; /* tile 0 */
oamMemory[3] = (u8)((3 << 4) | (palette << 1)); /* priority 3, palette bank */
oam_update_flag = 1; /* NMI handler DMAs the buffer */
u8 palette[]
Full 256-color palette for BG and sprite layers (512 bytes)
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:46
u8 oamMemory[]
Hardware OAM buffer (544 bytes at $7E:0300)
volatile u8 oam_update_flag
OAM DMA trigger flag.

When a collision is active, the palette bank switches from 0 (normal colors: white player, red enemies) to 1 (green highlight for both), providing instant visual feedback.

Collision Map Layout

The arena is a 16x14 tile grid (128x112 pixels) centered on the 256x224 screen. Walls form the border with symmetric internal platforms:

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 (top wall)
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
...
1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1 (platforms)
...
1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1 (center platform)
...
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 (bottom wall)

The same data drives both the visual BG tilemap (drawn once at init) and the runtime collision checks (read every frame).


Files

File Purpose
main.c Collision logic, sprite management, game loop
Makefile Build config (LIB_MODULES := console input sprite dma collision background)

What's Next?

Game: Breakout - Complete game with collision, sprites, and levels