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.

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:
- Subtract to get difference (may be negative)
- If result is negative (bit 7 set), negate it
- Negate:
EOR #$FFthenADC #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
| 1 | 1 | ; ============================================================================= | |
| 2 | - | ; NEON NEXUS - Unit 8: Enemy Movement | |
| 2 | + | ; NEON NEXUS - Unit 9: Collision Detection | |
| 3 | 3 | ; ============================================================================= | |
| 4 | - | ; Make enemies patrol the arena with bounce patterns. | |
| 4 | + | ; Detect when player touches enemies. Visual feedback on collision. | |
| 5 | 5 | ; ============================================================================= | |
| 6 | 6 | | |
| 7 | 7 | ; ----------------------------------------------------------------------------- | |
| ... | |||
| 38 | 38 | ENEMY_SPEED = 1 | |
| 39 | 39 | | |
| 40 | 40 | NUM_ENEMIES = 4 | |
| 41 | + | SPRITE_SIZE = 8 ; 8x8 pixel sprites | |
| 42 | + | COLLISION_DIST = 6 ; Overlap distance for collision | |
| 41 | 43 | | |
| 42 | 44 | ; Tile indices | |
| 43 | 45 | TILE_EMPTY = 0 | |
| ... | |||
| 57 | 59 | ARENA_TOP = 16 | |
| 58 | 60 | ARENA_BOTTOM = 208 | |
| 59 | 61 | | |
| 60 | - | ; Direction flags | |
| 61 | 62 | DIR_RIGHT = 1 | |
| 62 | - | DIR_LEFT = $FF ; -1 in two's complement | |
| 63 | + | DIR_LEFT = $FF | |
| 63 | 64 | DIR_DOWN = 1 | |
| 64 | 65 | DIR_UP = $FF | |
| 65 | 66 | | |
| ... | |||
| 69 | 70 | ; Memory Layout | |
| 70 | 71 | ; ----------------------------------------------------------------------------- | |
| 71 | 72 | .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 | |
| 78 | 82 | | |
| 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 | |
| 84 | 87 | | |
| 85 | 88 | .segment "OAM" | |
| 86 | - | oam_buffer: .res 256 | |
| 89 | + | oam_buffer: .res 256 | |
| 87 | 90 | | |
| 88 | 91 | .segment "BSS" | |
| 89 | 92 | | |
| ... | |||
| 145 | 148 | sta player_x | |
| 146 | 149 | lda #PLAYER_START_Y | |
| 147 | 150 | sta player_y | |
| 151 | + | | |
| 152 | + | lda #0 | |
| 153 | + | sta collision_flag | |
| 154 | + | sta flash_timer | |
| 148 | 155 | | |
| 149 | 156 | ; Player sprite | |
| 150 | 157 | lda player_y | |
| ... | |||
| 180 | 187 | jsr read_controller | |
| 181 | 188 | jsr move_player | |
| 182 | 189 | jsr move_enemies | |
| 190 | + | jsr check_collisions | |
| 191 | + | jsr update_player_sprite | |
| 183 | 192 | jsr update_enemy_sprites | |
| 184 | 193 | jmp main_loop | |
| 185 | 194 | | |
| 186 | 195 | ; ----------------------------------------------------------------------------- | |
| 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 | |
| 188 | 278 | ; ----------------------------------------------------------------------------- | |
| 189 | 279 | init_enemies: | |
| 190 | - | ; Enemy 0: Top-left, moving right and down | |
| 191 | 280 | lda #48 | |
| 192 | 281 | sta enemy_x+0 | |
| 193 | 282 | lda #48 | |
| ... | |||
| 197 | 286 | lda #DIR_DOWN | |
| 198 | 287 | sta enemy_dir_y+0 | |
| 199 | 288 | | |
| 200 | - | ; Enemy 1: Top-right, moving left and down | |
| 201 | 289 | lda #200 | |
| 202 | 290 | sta enemy_x+1 | |
| 203 | 291 | lda #48 | |
| ... | |||
| 207 | 295 | lda #DIR_DOWN | |
| 208 | 296 | sta enemy_dir_y+1 | |
| 209 | 297 | | |
| 210 | - | ; Enemy 2: Bottom-left, moving right and up | |
| 211 | 298 | lda #48 | |
| 212 | 299 | sta enemy_x+2 | |
| 213 | 300 | lda #176 | |
| ... | |||
| 217 | 304 | lda #DIR_UP | |
| 218 | 305 | sta enemy_dir_y+2 | |
| 219 | 306 | | |
| 220 | - | ; Enemy 3: Bottom-right, moving left and up | |
| 221 | 307 | lda #200 | |
| 222 | 308 | sta enemy_x+3 | |
| 223 | 309 | lda #176 | |
| ... | |||
| 230 | 316 | rts | |
| 231 | 317 | | |
| 232 | 318 | ; ----------------------------------------------------------------------------- | |
| 233 | - | ; Move Enemies - Bounce off arena walls | |
| 319 | + | ; Move Enemies | |
| 234 | 320 | ; ----------------------------------------------------------------------------- | |
| 235 | 321 | move_enemies: | |
| 236 | 322 | ldx #0 | |
| 237 | 323 | | |
| 238 | 324 | @enemy_loop: | |
| 239 | - | ; Move X | |
| 240 | 325 | lda enemy_x, x | |
| 241 | 326 | clc | |
| 242 | 327 | adc enemy_dir_x, x | |
| 243 | 328 | sta enemy_x, x | |
| 244 | 329 | | |
| 245 | - | ; Check X bounds | |
| 246 | 330 | cmp #ARENA_LEFT | |
| 247 | 331 | bcs @check_right | |
| 248 | - | ; Hit left wall - reverse X direction | |
| 249 | 332 | lda #DIR_RIGHT | |
| 250 | 333 | sta enemy_dir_x, x | |
| 251 | 334 | lda #ARENA_LEFT | |
| ... | |||
| 255 | 338 | @check_right: | |
| 256 | 339 | cmp #ARENA_RIGHT | |
| 257 | 340 | bcc @move_y | |
| 258 | - | ; Hit right wall - reverse X direction | |
| 259 | 341 | lda #DIR_LEFT | |
| 260 | 342 | sta enemy_dir_x, x | |
| 261 | 343 | lda #ARENA_RIGHT | |
| ... | |||
| 264 | 346 | sta enemy_x, x | |
| 265 | 347 | | |
| 266 | 348 | @move_y: | |
| 267 | - | ; Move Y | |
| 268 | 349 | lda enemy_y, x | |
| 269 | 350 | clc | |
| 270 | 351 | adc enemy_dir_y, x | |
| 271 | 352 | sta enemy_y, x | |
| 272 | 353 | | |
| 273 | - | ; Check Y bounds | |
| 274 | 354 | cmp #ARENA_TOP | |
| 275 | 355 | bcs @check_bottom | |
| 276 | - | ; Hit top wall - reverse Y direction | |
| 277 | 356 | lda #DIR_DOWN | |
| 278 | 357 | sta enemy_dir_y, x | |
| 279 | 358 | lda #ARENA_TOP | |
| ... | |||
| 283 | 362 | @check_bottom: | |
| 284 | 363 | cmp #ARENA_BOTTOM | |
| 285 | 364 | bcc @next_enemy | |
| 286 | - | ; Hit bottom wall - reverse Y direction | |
| 287 | 365 | lda #DIR_UP | |
| 288 | 366 | sta enemy_dir_y, x | |
| 289 | 367 | lda #ARENA_BOTTOM | |
| ... | |||
| 295 | 373 | inx | |
| 296 | 374 | cpx #NUM_ENEMIES | |
| 297 | 375 | bne @enemy_loop | |
| 298 | - | | |
| 299 | 376 | rts | |
| 300 | 377 | | |
| 301 | 378 | ; ----------------------------------------------------------------------------- | |
| 302 | - | ; Update Enemy Sprites in OAM | |
| 379 | + | ; Update Enemy Sprites | |
| 303 | 380 | ; ----------------------------------------------------------------------------- | |
| 304 | 381 | update_enemy_sprites: | |
| 305 | 382 | ldx #0 | |
| ... | |||
| 309 | 386 | lda enemy_y, x | |
| 310 | 387 | sta oam_buffer, y | |
| 311 | 388 | iny | |
| 312 | - | | |
| 313 | 389 | lda #SPRITE_ENEMY | |
| 314 | 390 | sta oam_buffer, y | |
| 315 | 391 | iny | |
| 316 | - | | |
| 317 | 392 | lda #%00000001 | |
| 318 | 393 | sta oam_buffer, y | |
| 319 | 394 | iny | |
| 320 | - | | |
| 321 | 395 | lda enemy_x, x | |
| 322 | 396 | sta oam_buffer, y | |
| 323 | 397 | iny | |
| 324 | - | | |
| 325 | 398 | inx | |
| 326 | 399 | cpx #NUM_ENEMIES | |
| 327 | 400 | bne @loop | |
| 328 | - | | |
| 329 | 401 | rts | |
| 330 | 402 | | |
| 331 | 403 | ; ----------------------------------------------------------------------------- | |
| ... | |||
| 337 | 409 | sta PPUADDR | |
| 338 | 410 | lda #$00 | |
| 339 | 411 | sta PPUADDR | |
| 340 | - | | |
| 341 | 412 | ldx #0 | |
| 342 | 413 | @loop: | |
| 343 | 414 | lda palette_data, x | |
| ... | |||
| 362 | 433 | | |
| 363 | 434 | @draw_row: | |
| 364 | 435 | lda row_counter | |
| 365 | - | | |
| 366 | 436 | cmp #0 | |
| 367 | 437 | beq @top_row | |
| 368 | 438 | cmp #1 | |
| 369 | 439 | beq @top_row | |
| 370 | - | | |
| 371 | 440 | cmp #28 | |
| 372 | 441 | beq @bottom_row | |
| 373 | 442 | cmp #29 | |
| 374 | 443 | beq @bottom_row | |
| 375 | - | | |
| 376 | 444 | jmp @middle_row | |
| 377 | 445 | | |
| 378 | 446 | @top_row: | |
| 379 | 447 | lda row_counter | |
| 380 | 448 | cmp #0 | |
| 381 | 449 | bne @top_row_inner | |
| 382 | - | | |
| 383 | 450 | lda #TILE_CORNER_TL | |
| 384 | 451 | sta PPUDATA | |
| 385 | 452 | lda #TILE_BORDER | |
| ... | |||
| 405 | 472 | lda row_counter | |
| 406 | 473 | cmp #29 | |
| 407 | 474 | bne @bottom_row_inner | |
| 408 | - | | |
| 409 | 475 | lda #TILE_CORNER_BL | |
| 410 | 476 | sta PPUDATA | |
| 411 | 477 | lda #TILE_BORDER | |
| ... | |||
| 431 | 497 | lda #TILE_BORDER | |
| 432 | 498 | sta PPUDATA | |
| 433 | 499 | sta PPUDATA | |
| 434 | - | | |
| 435 | 500 | lda #TILE_FLOOR | |
| 436 | 501 | ldx #28 | |
| 437 | 502 | @floor_fill: | |
| 438 | 503 | sta PPUDATA | |
| 439 | 504 | dex | |
| 440 | 505 | bne @floor_fill | |
| 441 | - | | |
| 442 | 506 | lda #TILE_BORDER | |
| 443 | 507 | sta PPUDATA | |
| 444 | 508 | sta PPUDATA | |
| ... | |||
| 492 | 556 | sta PPUDATA | |
| 493 | 557 | dex | |
| 494 | 558 | bne @attr_bottom | |
| 495 | - | | |
| 496 | 559 | rts | |
| 497 | 560 | | |
| 498 | 561 | ; ----------------------------------------------------------------------------- | |
| ... | |||
| 503 | 566 | sta JOYPAD1 | |
| 504 | 567 | lda #0 | |
| 505 | 568 | sta JOYPAD1 | |
| 506 | - | | |
| 507 | 569 | ldx #8 | |
| 508 | 570 | @read_loop: | |
| 509 | 571 | lda JOYPAD1 | |
| ... | |||
| 575 | 637 | sta OAMADDR | |
| 576 | 638 | lda #>oam_buffer | |
| 577 | 639 | sta OAMDMA | |
| 578 | - | | |
| 579 | - | lda player_y | |
| 580 | - | sta oam_buffer+0 | |
| 581 | - | lda player_x | |
| 582 | - | sta oam_buffer+3 | |
| 583 | 640 | | |
| 584 | 641 | inc frame_count | |
| 585 | 642 | |