Skip to content

AABB Collision Detection

Axis-Aligned Bounding Box collision — fast rectangle overlap test for sprites and objects.

collisionphysicsspritesaabb

Overview

AABB (Axis-Aligned Bounding Box) collision detects when two rectangles overlap. It's fast, simple, and good enough for most games. Each object has a position (x, y) and size (width, height). Two boxes collide if they overlap on both axes.

This is the same algorithm on every platform — only the implementation details change.

Algorithm

Two rectangles overlap if and only if:

  • Box A's left edge is left of Box B's right edge, AND
  • Box A's right edge is right of Box B's left edge, AND
  • Box A's top edge is above Box B's bottom edge, AND
  • Box A's bottom edge is below Box B's top edge
; Returns: true if collision, false otherwise
check_collision(ax, ay, aw, ah, bx, by, bw, bh):
    if ax >= bx + bw: return false    ; A is right of B
    if ax + aw <= bx: return false    ; A is left of B
    if ay >= by + bh: return false    ; A is below B
    if ay + ah <= by: return false    ; A is above B
    return true                        ; Overlap!

Pseudocode

; Object structure
player_x, player_y: byte
player_w, player_h: byte = 16, 16

enemy_x, enemy_y: byte
enemy_w, enemy_h: byte = 16, 16

; Check player vs enemy collision
check_player_enemy:
    ; Check X axis overlap
    load player_x
    add player_w
    if result <= enemy_x: return NO_COLLISION

    load enemy_x
    add enemy_w
    if result <= player_x: return NO_COLLISION

    ; Check Y axis overlap
    load player_y
    add player_h
    if result <= enemy_y: return NO_COLLISION

    load enemy_y
    add enemy_h
    if result <= player_y: return NO_COLLISION

    return COLLISION

Implementation Notes

Optimised check order: Test the most likely failure first. If objects usually approach from the sides, check X before Y. This lets you exit early most of the time.

Sprite centre vs corner: Hardware sprites often use top-left as origin. If your collision box should be centred, offset the check coordinates:

; For a 16x16 sprite with 8x8 collision box centred
collision_x = sprite_x + 4
collision_y = sprite_y + 4
collision_w = 8
collision_h = 8

Multiple enemies: Loop through enemy array, checking each against player:

for i = 0 to num_enemies - 1:
    if check_collision(player, enemies[i]):
        handle_hit(i)

Trade-offs

AspectCost
CPU~30-60 cycles per check (platform dependent)
Memory4 bytes per object (x, y, w, h)
AccuracyRectangle only — diagonal corners may miss or false-hit

When to use: Most games — shooters, platformers, action games.

When to avoid: Games requiring pixel-perfect collision (use sprite mask AND instead).

Variations

  • Point-in-box: Simplified check for bullets (w=1, h=1)
  • Expanded box: Add a few pixels for more forgiving collision
  • Shrunk box: Smaller than sprite for tighter, fairer hits

Many-object optimisation: spatial bucketing

A naive "player vs all enemies" loop does N checks per frame; "all enemies vs all enemies" does N². At 16 enemies this is already 256 checks per frame, eating cycle budget on 8-bit CPUs.

The standard mitigation is spatial bucketing: divide the screen into a coarse grid (e.g. 8×8 cells of 32×32 pixels each on a Spectrum). Each frame, sort objects into buckets by their position. Then for each object, only test against others in the same bucket (and adjacent buckets if the object spans a boundary).

; 8x8 bucket grid for a 256x192 Spectrum screen
buckets: array[64] of object_list
bucket_size_x = 32
bucket_size_y = 24

rebucket_objects:
    clear all buckets
    for each object o:
        col = o.x / bucket_size_x
        row = o.y / bucket_size_y
        bucket = row * 8 + col
        add o to buckets[bucket]

check_collisions:
    for each bucket b:
        check all object pairs within b   ; small N
        check b against right neighbour
        check b against bottom neighbour
        check b against bottom-right neighbour

Bucketing trades memory (the bucket arrays) for cycles. Worthwhile once N exceeds 8-12 objects.