Skip to content
Game 1 Unit 9 of 64 1 hr learning time

Collision Detection

Detect when player touches enemies with visual feedback.

14% of Neon Nexus

What You’re Building

Collision detection. Touch an enemy and you flash - a warning that danger is real.

Collision Detection

Actions now have consequences.

Bounding Box Collision

The simplest collision test: do two rectangles overlap?

For 8×8 sprites, we check if the distance between centres is less than a threshold:

COLLISION_DIST = 6       ; Pixels of overlap needed

check_collisions:
    ldx #0
@check_enemy:
    ; Check X distance: |player_x - enemy_x| < COLLISION_DIST
    lda player_x
    sec
    sbc enemy_x, x
    bpl @check_x_positive
    ; Negative result - make it positive
    eor #$FF
    clc
    adc #1
@check_x_positive:
    cmp #COLLISION_DIST
    bcs @next_enemy      ; Too far apart in X

    ; Same check for Y...

Absolute Value

The 6502 has no absolute value instruction. We compute it manually:

  1. Subtract to get difference (may be negative)
  2. If result is negative (bit 7 set), negate it
  3. Negate: EOR #$FF then ADC #1 (two’s complement)

Visual Feedback

When collision is detected, the player flashes:

collision_flag: .res 1     ; Non-zero if hit
flash_timer:    .res 1     ; Frames remaining

; On collision:
    lda #1
    sta collision_flag
    lda #30              ; Flash for 30 frames
    sta flash_timer

The Flash Effect

Toggle sprite visibility every few frames:

update_player_sprite:
    lda flash_timer
    beq @no_flash

    dec flash_timer

    ; Flash every 4 frames
    and #%00000100
    beq @show_player

    ; Hide: move Y off screen
    lda #$FF
    sta oam_buffer+0
    rts

@show_player:
    lda player_y
    sta oam_buffer+0
    rts

Setting Y to $FF moves the sprite below the visible screen.

The Code

; =============================================================================
; NEON NEXUS - Unit 9: Collision Detection
; =============================================================================
; Detect when player touches enemies. Visual feedback on collision.
; =============================================================================

; -----------------------------------------------------------------------------
; NES Hardware Addresses
; -----------------------------------------------------------------------------
PPUCTRL   = $2000
PPUMASK   = $2001
PPUSTATUS = $2002
OAMADDR   = $2003
PPUSCROLL = $2005
PPUADDR   = $2006
PPUDATA   = $2007
OAMDMA    = $4014

JOYPAD1   = $4016
JOYPAD2   = $4017

; Controller buttons
BTN_A      = %10000000
BTN_B      = %01000000
BTN_SELECT = %00100000
BTN_START  = %00010000
BTN_UP     = %00001000
BTN_DOWN   = %00000100
BTN_LEFT   = %00000010
BTN_RIGHT  = %00000001

; -----------------------------------------------------------------------------
; Game Constants
; -----------------------------------------------------------------------------
PLAYER_START_X = 124
PLAYER_START_Y = 116
PLAYER_SPEED   = 2
ENEMY_SPEED    = 1

NUM_ENEMIES    = 4
SPRITE_SIZE    = 8       ; 8x8 pixel sprites
COLLISION_DIST = 6       ; Overlap distance for collision

; Tile indices
TILE_EMPTY     = 0
TILE_BORDER    = 1
TILE_FLOOR     = 2
TILE_CORNER_TL = 3
TILE_CORNER_TR = 4
TILE_CORNER_BL = 5
TILE_CORNER_BR = 6

SPRITE_PLAYER  = 7
SPRITE_ENEMY   = 8

; Arena boundaries
ARENA_LEFT   = 16
ARENA_RIGHT  = 232
ARENA_TOP    = 16
ARENA_BOTTOM = 208

DIR_RIGHT = 1
DIR_LEFT  = $FF
DIR_DOWN  = 1
DIR_UP    = $FF

BG_COLOUR = $0F

; -----------------------------------------------------------------------------
; Memory Layout
; -----------------------------------------------------------------------------
.segment "ZEROPAGE"
player_x:      .res 1
player_y:      .res 1
buttons:       .res 1
temp:          .res 1
temp2:         .res 1
row_counter:   .res 1
frame_count:   .res 1
collision_flag: .res 1     ; Non-zero if collision detected
flash_timer:   .res 1      ; Timer for visual feedback

enemy_x:       .res NUM_ENEMIES
enemy_y:       .res NUM_ENEMIES
enemy_dir_x:   .res NUM_ENEMIES
enemy_dir_y:   .res NUM_ENEMIES

.segment "OAM"
oam_buffer:    .res 256

.segment "BSS"

; -----------------------------------------------------------------------------
; iNES Header
; -----------------------------------------------------------------------------
.segment "HEADER"
    .byte "NES", $1A
    .byte 2
    .byte 1
    .byte $01
    .byte $00
    .byte 0,0,0,0,0,0,0,0

; -----------------------------------------------------------------------------
; Code
; -----------------------------------------------------------------------------
.segment "CODE"

reset:
    sei
    cld
    ldx #$40
    stx $4017
    ldx #$FF
    txs
    inx
    stx PPUCTRL
    stx PPUMASK
    stx $4010

@vblank1:
    bit PPUSTATUS
    bpl @vblank1

    lda #0
@clear_ram:
    sta $0000, x
    sta $0100, x
    sta $0200, x
    sta $0300, x
    sta $0400, x
    sta $0500, x
    sta $0600, x
    sta $0700, x
    inx
    bne @clear_ram

@vblank2:
    bit PPUSTATUS
    bpl @vblank2

    jsr load_palette
    jsr draw_arena
    jsr set_attributes
    jsr init_enemies

    lda #PLAYER_START_X
    sta player_x
    lda #PLAYER_START_Y
    sta player_y

    lda #0
    sta collision_flag
    sta flash_timer

    ; Player sprite
    lda player_y
    sta oam_buffer+0
    lda #SPRITE_PLAYER
    sta oam_buffer+1
    lda #0
    sta oam_buffer+2
    lda player_x
    sta oam_buffer+3

    jsr update_enemy_sprites

    ; Hide remaining sprites
    lda #$FF
    ldx #20
@hide_sprites:
    sta oam_buffer, x
    inx
    bne @hide_sprites

    lda #0
    sta PPUSCROLL
    sta PPUSCROLL
    sta frame_count

    lda #%10000000
    sta PPUCTRL
    lda #%00011110
    sta PPUMASK

main_loop:
    jsr read_controller
    jsr move_player
    jsr move_enemies
    jsr check_collisions
    jsr update_player_sprite
    jsr update_enemy_sprites
    jmp main_loop

; -----------------------------------------------------------------------------
; Check Collisions - Player vs all enemies
; -----------------------------------------------------------------------------
check_collisions:
    lda #0
    sta collision_flag

    ldx #0
@check_enemy:
    ; Check X overlap
    ; |player_x - enemy_x| < COLLISION_DIST
    lda player_x
    sec
    sbc enemy_x, x
    bpl @check_x_positive
    ; Negative - negate it
    eor #$FF
    clc
    adc #1
@check_x_positive:
    cmp #COLLISION_DIST
    bcs @next_enemy      ; No X overlap

    ; Check Y overlap
    lda player_y
    sec
    sbc enemy_y, x
    bpl @check_y_positive
    eor #$FF
    clc
    adc #1
@check_y_positive:
    cmp #COLLISION_DIST
    bcs @next_enemy      ; No Y overlap

    ; Collision detected!
    lda #1
    sta collision_flag
    lda #30              ; Flash for 30 frames
    sta flash_timer
    rts                  ; Exit early on first collision

@next_enemy:
    inx
    cpx #NUM_ENEMIES
    bne @check_enemy
    rts

; -----------------------------------------------------------------------------
; Update Player Sprite - Flash when hit
; -----------------------------------------------------------------------------
update_player_sprite:
    ; Update position
    lda player_y
    sta oam_buffer+0
    lda player_x
    sta oam_buffer+3

    ; Check if flashing
    lda flash_timer
    beq @no_flash

    ; Decrement timer
    dec flash_timer

    ; Flash by toggling visibility every 4 frames
    and #%00000100
    beq @show_player
    ; Hide player (move off screen)
    lda #$FF
    sta oam_buffer+0
    rts

@show_player:
    lda player_y
    sta oam_buffer+0
    rts

@no_flash:
    rts

; -----------------------------------------------------------------------------
; Initialise Enemies
; -----------------------------------------------------------------------------
init_enemies:
    lda #48
    sta enemy_x+0
    lda #48
    sta enemy_y+0
    lda #DIR_RIGHT
    sta enemy_dir_x+0
    lda #DIR_DOWN
    sta enemy_dir_y+0

    lda #200
    sta enemy_x+1
    lda #48
    sta enemy_y+1
    lda #DIR_LEFT
    sta enemy_dir_x+1
    lda #DIR_DOWN
    sta enemy_dir_y+1

    lda #48
    sta enemy_x+2
    lda #176
    sta enemy_y+2
    lda #DIR_RIGHT
    sta enemy_dir_x+2
    lda #DIR_UP
    sta enemy_dir_y+2

    lda #200
    sta enemy_x+3
    lda #176
    sta enemy_y+3
    lda #DIR_LEFT
    sta enemy_dir_x+3
    lda #DIR_UP
    sta enemy_dir_y+3

    rts

; -----------------------------------------------------------------------------
; Move Enemies
; -----------------------------------------------------------------------------
move_enemies:
    ldx #0

@enemy_loop:
    lda enemy_x, x
    clc
    adc enemy_dir_x, x
    sta enemy_x, x

    cmp #ARENA_LEFT
    bcs @check_right
    lda #DIR_RIGHT
    sta enemy_dir_x, x
    lda #ARENA_LEFT
    sta enemy_x, x
    jmp @move_y

@check_right:
    cmp #ARENA_RIGHT
    bcc @move_y
    lda #DIR_LEFT
    sta enemy_dir_x, x
    lda #ARENA_RIGHT
    sec
    sbc #1
    sta enemy_x, x

@move_y:
    lda enemy_y, x
    clc
    adc enemy_dir_y, x
    sta enemy_y, x

    cmp #ARENA_TOP
    bcs @check_bottom
    lda #DIR_DOWN
    sta enemy_dir_y, x
    lda #ARENA_TOP
    sta enemy_y, x
    jmp @next_enemy

@check_bottom:
    cmp #ARENA_BOTTOM
    bcc @next_enemy
    lda #DIR_UP
    sta enemy_dir_y, x
    lda #ARENA_BOTTOM
    sec
    sbc #1
    sta enemy_y, x

@next_enemy:
    inx
    cpx #NUM_ENEMIES
    bne @enemy_loop
    rts

; -----------------------------------------------------------------------------
; Update Enemy Sprites
; -----------------------------------------------------------------------------
update_enemy_sprites:
    ldx #0
    ldy #4

@loop:
    lda enemy_y, x
    sta oam_buffer, y
    iny
    lda #SPRITE_ENEMY
    sta oam_buffer, y
    iny
    lda #%00000001
    sta oam_buffer, y
    iny
    lda enemy_x, x
    sta oam_buffer, y
    iny
    inx
    cpx #NUM_ENEMIES
    bne @loop
    rts

; -----------------------------------------------------------------------------
; Load Palette
; -----------------------------------------------------------------------------
load_palette:
    bit PPUSTATUS
    lda #$3F
    sta PPUADDR
    lda #$00
    sta PPUADDR
    ldx #0
@loop:
    lda palette_data, x
    sta PPUDATA
    inx
    cpx #32
    bne @loop
    rts

; -----------------------------------------------------------------------------
; Draw Arena
; -----------------------------------------------------------------------------
draw_arena:
    bit PPUSTATUS
    lda #$20
    sta PPUADDR
    lda #$00
    sta PPUADDR

    lda #0
    sta row_counter

@draw_row:
    lda row_counter
    cmp #0
    beq @top_row
    cmp #1
    beq @top_row
    cmp #28
    beq @bottom_row
    cmp #29
    beq @bottom_row
    jmp @middle_row

@top_row:
    lda row_counter
    cmp #0
    bne @top_row_inner
    lda #TILE_CORNER_TL
    sta PPUDATA
    lda #TILE_BORDER
    ldx #30
@top_fill:
    sta PPUDATA
    dex
    bne @top_fill
    lda #TILE_CORNER_TR
    sta PPUDATA
    jmp @next_row

@top_row_inner:
    lda #TILE_BORDER
    ldx #32
@top_inner_fill:
    sta PPUDATA
    dex
    bne @top_inner_fill
    jmp @next_row

@bottom_row:
    lda row_counter
    cmp #29
    bne @bottom_row_inner
    lda #TILE_CORNER_BL
    sta PPUDATA
    lda #TILE_BORDER
    ldx #30
@bottom_fill:
    sta PPUDATA
    dex
    bne @bottom_fill
    lda #TILE_CORNER_BR
    sta PPUDATA
    jmp @next_row

@bottom_row_inner:
    lda #TILE_BORDER
    ldx #32
@bottom_inner_fill:
    sta PPUDATA
    dex
    bne @bottom_inner_fill
    jmp @next_row

@middle_row:
    lda #TILE_BORDER
    sta PPUDATA
    sta PPUDATA
    lda #TILE_FLOOR
    ldx #28
@floor_fill:
    sta PPUDATA
    dex
    bne @floor_fill
    lda #TILE_BORDER
    sta PPUDATA
    sta PPUDATA

@next_row:
    inc row_counter
    lda row_counter
    cmp #30
    beq @done_drawing
    jmp @draw_row

@done_drawing:
    rts

; -----------------------------------------------------------------------------
; Set Attribute Table
; -----------------------------------------------------------------------------
set_attributes:
    bit PPUSTATUS
    lda #$23
    sta PPUADDR
    lda #$C0
    sta PPUADDR

    ldx #8
    lda #$00
@attr_top:
    sta PPUDATA
    dex
    bne @attr_top

    ldx #6
@attr_floor_rows:
    lda #$00
    sta PPUDATA
    lda #%01010101
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    lda #$00
    sta PPUDATA
    dex
    bne @attr_floor_rows

    ldx #8
    lda #$00
@attr_bottom:
    sta PPUDATA
    dex
    bne @attr_bottom
    rts

; -----------------------------------------------------------------------------
; Read Controller
; -----------------------------------------------------------------------------
read_controller:
    lda #1
    sta JOYPAD1
    lda #0
    sta JOYPAD1
    ldx #8
@read_loop:
    lda JOYPAD1
    lsr a
    rol buttons
    dex
    bne @read_loop
    rts

; -----------------------------------------------------------------------------
; Move Player
; -----------------------------------------------------------------------------
move_player:
    lda buttons
    and #BTN_UP
    beq @check_down
    lda player_y
    sec
    sbc #PLAYER_SPEED
    cmp #ARENA_TOP
    bcc @check_down
    sta player_y

@check_down:
    lda buttons
    and #BTN_DOWN
    beq @check_left
    lda player_y
    clc
    adc #PLAYER_SPEED
    cmp #ARENA_BOTTOM
    bcs @check_left
    sta player_y

@check_left:
    lda buttons
    and #BTN_LEFT
    beq @check_right
    lda player_x
    sec
    sbc #PLAYER_SPEED
    cmp #ARENA_LEFT
    bcc @check_right
    sta player_x

@check_right:
    lda buttons
    and #BTN_RIGHT
    beq @done
    lda player_x
    clc
    adc #PLAYER_SPEED
    cmp #ARENA_RIGHT
    bcs @done
    sta player_x

@done:
    rts

; === NMI ===
nmi:
    pha
    txa
    pha
    tya
    pha

    lda #0
    sta OAMADDR
    lda #>oam_buffer
    sta OAMDMA

    inc frame_count

    lda #0
    sta PPUSCROLL
    sta PPUSCROLL

    pla
    tay
    pla
    tax
    pla
    rti

irq:
    rti

; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
palette_data:
    .byte BG_COLOUR, $11, $21, $31
    .byte BG_COLOUR, $13, $23, $33
    .byte BG_COLOUR, $19, $29, $39
    .byte BG_COLOUR, $16, $26, $36
    .byte BG_COLOUR, $30, $27, $17
    .byte BG_COLOUR, $16, $26, $36
    .byte BG_COLOUR, $30, $27, $17
    .byte BG_COLOUR, $30, $27, $17

; -----------------------------------------------------------------------------
; Vectors
; -----------------------------------------------------------------------------
.segment "VECTORS"
    .word nmi
    .word reset
    .word irq

; -----------------------------------------------------------------------------
; CHR-ROM
; -----------------------------------------------------------------------------
.segment "CHARS"

; Tile 0: Empty
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00

; Tile 1: Border
.byte %11111111,%10000001,%10000001,%11111111
.byte %11111111,%00010001,%00010001,%11111111
.byte %00000000,%01111110,%01111110,%00000000
.byte %00000000,%11101110,%11101110,%00000000

; Tile 2: Floor
.byte %00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%10000001
.byte %00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000

; Tile 3: Corner TL
.byte %11111111,%11000000,%10100000,%10010000
.byte %10001000,%10000100,%10000010,%10000001
.byte %00000000,%00111111,%01011111,%01101111
.byte %01110111,%01111011,%01111101,%01111110

; Tile 4: Corner TR
.byte %11111111,%00000011,%00000101,%00001001
.byte %00010001,%00100001,%01000001,%10000001
.byte %00000000,%11111100,%11111010,%11110110
.byte %11101110,%11011110,%10111110,%01111110

; Tile 5: Corner BL
.byte %10000001,%10000010,%10000100,%10001000
.byte %10010000,%10100000,%11000000,%11111111
.byte %01111110,%01111101,%01111011,%01110111
.byte %01101111,%01011111,%00111111,%00000000

; Tile 6: Corner BR
.byte %10000001,%01000001,%00100001,%00010001
.byte %00001001,%00000101,%00000011,%11111111
.byte %01111110,%10111110,%11011110,%11101110
.byte %11110110,%11111010,%11111100,%00000000

; Tile 7: Player
.byte %00011000,%00011000,%00111100,%01111110
.byte %11111111,%10111101,%00100100,%00100100
.byte %00000000,%00011000,%00011000,%00111100
.byte %01000010,%01000010,%00011000,%00000000

; Tile 8: Enemy
.byte %00011000,%00111100,%01111110,%11111111
.byte %11111111,%01111110,%00111100,%00011000
.byte %00000000,%00011000,%00100100,%01000010
.byte %01000010,%00100100,%00011000,%00000000

; Fill rest
.res 8192 - 144, $00

Build It

ca65 nexus.asm -o nexus.o
ld65 -C nes.cfg nexus.o -o nexus.nes

Touch an enemy and the player flashes. The game now reacts to your mistakes.

Next

Flashing is a warning. Unit 10 adds real consequences: lives and death.

What Changed

Unit 8 → Unit 9
+109-52
11 ; =============================================================================
2-; NEON NEXUS - Unit 8: Enemy Movement
2+; NEON NEXUS - Unit 9: Collision Detection
33 ; =============================================================================
4-; Make enemies patrol the arena with bounce patterns.
4+; Detect when player touches enemies. Visual feedback on collision.
55 ; =============================================================================
66
77 ; -----------------------------------------------------------------------------
...
3838 ENEMY_SPEED = 1
3939
4040 NUM_ENEMIES = 4
41+SPRITE_SIZE = 8 ; 8x8 pixel sprites
42+COLLISION_DIST = 6 ; Overlap distance for collision
4143
4244 ; Tile indices
4345 TILE_EMPTY = 0
...
5759 ARENA_TOP = 16
5860 ARENA_BOTTOM = 208
5961
60-; Direction flags
6162 DIR_RIGHT = 1
62-DIR_LEFT = $FF ; -1 in two's complement
63+DIR_LEFT = $FF
6364 DIR_DOWN = 1
6465 DIR_UP = $FF
6566
...
6970 ; Memory Layout
7071 ; -----------------------------------------------------------------------------
7172 .segment "ZEROPAGE"
72-player_x: .res 1
73-player_y: .res 1
74-buttons: .res 1
75-temp: .res 1
76-row_counter: .res 1
77-frame_count: .res 1
73+player_x: .res 1
74+player_y: .res 1
75+buttons: .res 1
76+temp: .res 1
77+temp2: .res 1
78+row_counter: .res 1
79+frame_count: .res 1
80+collision_flag: .res 1 ; Non-zero if collision detected
81+flash_timer: .res 1 ; Timer for visual feedback
7882
79-; Enemy data
80-enemy_x: .res NUM_ENEMIES
81-enemy_y: .res NUM_ENEMIES
82-enemy_dir_x: .res NUM_ENEMIES ; +1 or -1
83-enemy_dir_y: .res NUM_ENEMIES ; +1 or -1
83+enemy_x: .res NUM_ENEMIES
84+enemy_y: .res NUM_ENEMIES
85+enemy_dir_x: .res NUM_ENEMIES
86+enemy_dir_y: .res NUM_ENEMIES
8487
8588 .segment "OAM"
86-oam_buffer: .res 256
89+oam_buffer: .res 256
8790
8891 .segment "BSS"
8992
...
145148 sta player_x
146149 lda #PLAYER_START_Y
147150 sta player_y
151+
152+ lda #0
153+ sta collision_flag
154+ sta flash_timer
148155
149156 ; Player sprite
150157 lda player_y
...
180187 jsr read_controller
181188 jsr move_player
182189 jsr move_enemies
190+ jsr check_collisions
191+ jsr update_player_sprite
183192 jsr update_enemy_sprites
184193 jmp main_loop
185194
186195 ; -----------------------------------------------------------------------------
187-; Initialise Enemies with positions and directions
196+; Check Collisions - Player vs all enemies
197+; -----------------------------------------------------------------------------
198+check_collisions:
199+ lda #0
200+ sta collision_flag
201+
202+ ldx #0
203+@check_enemy:
204+ ; Check X overlap
205+ ; |player_x - enemy_x| < COLLISION_DIST
206+ lda player_x
207+ sec
208+ sbc enemy_x, x
209+ bpl @check_x_positive
210+ ; Negative - negate it
211+ eor #$FF
212+ clc
213+ adc #1
214+@check_x_positive:
215+ cmp #COLLISION_DIST
216+ bcs @next_enemy ; No X overlap
217+
218+ ; Check Y overlap
219+ lda player_y
220+ sec
221+ sbc enemy_y, x
222+ bpl @check_y_positive
223+ eor #$FF
224+ clc
225+ adc #1
226+@check_y_positive:
227+ cmp #COLLISION_DIST
228+ bcs @next_enemy ; No Y overlap
229+
230+ ; Collision detected!
231+ lda #1
232+ sta collision_flag
233+ lda #30 ; Flash for 30 frames
234+ sta flash_timer
235+ rts ; Exit early on first collision
236+
237+@next_enemy:
238+ inx
239+ cpx #NUM_ENEMIES
240+ bne @check_enemy
241+ rts
242+
243+; -----------------------------------------------------------------------------
244+; Update Player Sprite - Flash when hit
245+; -----------------------------------------------------------------------------
246+update_player_sprite:
247+ ; Update position
248+ lda player_y
249+ sta oam_buffer+0
250+ lda player_x
251+ sta oam_buffer+3
252+
253+ ; Check if flashing
254+ lda flash_timer
255+ beq @no_flash
256+
257+ ; Decrement timer
258+ dec flash_timer
259+
260+ ; Flash by toggling visibility every 4 frames
261+ and #%00000100
262+ beq @show_player
263+ ; Hide player (move off screen)
264+ lda #$FF
265+ sta oam_buffer+0
266+ rts
267+
268+@show_player:
269+ lda player_y
270+ sta oam_buffer+0
271+ rts
272+
273+@no_flash:
274+ rts
275+
276+; -----------------------------------------------------------------------------
277+; Initialise Enemies
188278 ; -----------------------------------------------------------------------------
189279 init_enemies:
190- ; Enemy 0: Top-left, moving right and down
191280 lda #48
192281 sta enemy_x+0
193282 lda #48
...
197286 lda #DIR_DOWN
198287 sta enemy_dir_y+0
199288
200- ; Enemy 1: Top-right, moving left and down
201289 lda #200
202290 sta enemy_x+1
203291 lda #48
...
207295 lda #DIR_DOWN
208296 sta enemy_dir_y+1
209297
210- ; Enemy 2: Bottom-left, moving right and up
211298 lda #48
212299 sta enemy_x+2
213300 lda #176
...
217304 lda #DIR_UP
218305 sta enemy_dir_y+2
219306
220- ; Enemy 3: Bottom-right, moving left and up
221307 lda #200
222308 sta enemy_x+3
223309 lda #176
...
230316 rts
231317
232318 ; -----------------------------------------------------------------------------
233-; Move Enemies - Bounce off arena walls
319+; Move Enemies
234320 ; -----------------------------------------------------------------------------
235321 move_enemies:
236322 ldx #0
237323
238324 @enemy_loop:
239- ; Move X
240325 lda enemy_x, x
241326 clc
242327 adc enemy_dir_x, x
243328 sta enemy_x, x
244329
245- ; Check X bounds
246330 cmp #ARENA_LEFT
247331 bcs @check_right
248- ; Hit left wall - reverse X direction
249332 lda #DIR_RIGHT
250333 sta enemy_dir_x, x
251334 lda #ARENA_LEFT
...
255338 @check_right:
256339 cmp #ARENA_RIGHT
257340 bcc @move_y
258- ; Hit right wall - reverse X direction
259341 lda #DIR_LEFT
260342 sta enemy_dir_x, x
261343 lda #ARENA_RIGHT
...
264346 sta enemy_x, x
265347
266348 @move_y:
267- ; Move Y
268349 lda enemy_y, x
269350 clc
270351 adc enemy_dir_y, x
271352 sta enemy_y, x
272353
273- ; Check Y bounds
274354 cmp #ARENA_TOP
275355 bcs @check_bottom
276- ; Hit top wall - reverse Y direction
277356 lda #DIR_DOWN
278357 sta enemy_dir_y, x
279358 lda #ARENA_TOP
...
283362 @check_bottom:
284363 cmp #ARENA_BOTTOM
285364 bcc @next_enemy
286- ; Hit bottom wall - reverse Y direction
287365 lda #DIR_UP
288366 sta enemy_dir_y, x
289367 lda #ARENA_BOTTOM
...
295373 inx
296374 cpx #NUM_ENEMIES
297375 bne @enemy_loop
298-
299376 rts
300377
301378 ; -----------------------------------------------------------------------------
302-; Update Enemy Sprites in OAM
379+; Update Enemy Sprites
303380 ; -----------------------------------------------------------------------------
304381 update_enemy_sprites:
305382 ldx #0
...
309386 lda enemy_y, x
310387 sta oam_buffer, y
311388 iny
312-
313389 lda #SPRITE_ENEMY
314390 sta oam_buffer, y
315391 iny
316-
317392 lda #%00000001
318393 sta oam_buffer, y
319394 iny
320-
321395 lda enemy_x, x
322396 sta oam_buffer, y
323397 iny
324-
325398 inx
326399 cpx #NUM_ENEMIES
327400 bne @loop
328-
329401 rts
330402
331403 ; -----------------------------------------------------------------------------
...
337409 sta PPUADDR
338410 lda #$00
339411 sta PPUADDR
340-
341412 ldx #0
342413 @loop:
343414 lda palette_data, x
...
362433
363434 @draw_row:
364435 lda row_counter
365-
366436 cmp #0
367437 beq @top_row
368438 cmp #1
369439 beq @top_row
370-
371440 cmp #28
372441 beq @bottom_row
373442 cmp #29
374443 beq @bottom_row
375-
376444 jmp @middle_row
377445
378446 @top_row:
379447 lda row_counter
380448 cmp #0
381449 bne @top_row_inner
382-
383450 lda #TILE_CORNER_TL
384451 sta PPUDATA
385452 lda #TILE_BORDER
...
405472 lda row_counter
406473 cmp #29
407474 bne @bottom_row_inner
408-
409475 lda #TILE_CORNER_BL
410476 sta PPUDATA
411477 lda #TILE_BORDER
...
431497 lda #TILE_BORDER
432498 sta PPUDATA
433499 sta PPUDATA
434-
435500 lda #TILE_FLOOR
436501 ldx #28
437502 @floor_fill:
438503 sta PPUDATA
439504 dex
440505 bne @floor_fill
441-
442506 lda #TILE_BORDER
443507 sta PPUDATA
444508 sta PPUDATA
...
492556 sta PPUDATA
493557 dex
494558 bne @attr_bottom
495-
496559 rts
497560
498561 ; -----------------------------------------------------------------------------
...
503566 sta JOYPAD1
504567 lda #0
505568 sta JOYPAD1
506-
507569 ldx #8
508570 @read_loop:
509571 lda JOYPAD1
...
575637 sta OAMADDR
576638 lda #>oam_buffer
577639 sta OAMDMA
578-
579- lda player_y
580- sta oam_buffer+0
581- lda player_x
582- sta oam_buffer+3
583640
584641 inc frame_count
585642