Game 1 Unit 8 of 64 1 hr learning time
Enemy Movement
Make enemies patrol the arena with bounce patterns.
13% of Neon Nexus
What You’re Building
Enemies that patrol. They bounce off walls, constantly hunting.

The arena is now alive with danger.
Direction Variables
Each enemy needs a direction for X and Y movement:
enemy_dir_x: .res NUM_ENEMIES ; +1 (right) or -1 (left)
enemy_dir_y: .res NUM_ENEMIES ; +1 (down) or -1 (up)
We use $FF for -1 (two’s complement). When added to a position, it subtracts 1.
Initialising Directions
Each enemy starts moving in a different diagonal:
init_enemies:
; Enemy 0: Top-left, moving right and down
lda #1
sta enemy_dir_x+0
sta enemy_dir_y+0
; Enemy 1: Top-right, moving left and down
lda #$FF ; -1
sta enemy_dir_x+1
lda #1
sta enemy_dir_y+1
; ...
Movement Logic
Each frame, move each enemy and check for wall collisions:
move_enemies:
ldx #0
@enemy_loop:
; Move X
lda enemy_x, x
clc
adc enemy_dir_x, x
sta enemy_x, x
; Check bounds
cmp #ARENA_LEFT
bcs @check_right
; Hit left wall - reverse
lda #1
sta enemy_dir_x, x
lda #ARENA_LEFT
sta enemy_x, x
jmp @move_y
@check_right:
cmp #ARENA_RIGHT
bcc @move_y
; Hit right wall - reverse
lda #$FF
sta enemy_dir_x, x
; ... same pattern for Y
The Bounce
When an enemy hits a wall:
- Reverse its direction for that axis
- Clamp its position to the boundary
This creates the classic “bounce” behaviour seen in many early games.
Calling Movement
Add enemy movement to the main loop:
main_loop:
jsr read_controller
jsr move_player
jsr move_enemies ; NEW
jsr update_enemy_sprites
jmp main_loop
The Code
; =============================================================================
; NEON NEXUS - Unit 8: Enemy Movement
; =============================================================================
; Make enemies patrol the arena with bounce patterns.
; =============================================================================
; -----------------------------------------------------------------------------
; 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
; 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
; Direction flags
DIR_RIGHT = 1
DIR_LEFT = $FF ; -1 in two's complement
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
row_counter: .res 1
frame_count: .res 1
; Enemy data
enemy_x: .res NUM_ENEMIES
enemy_y: .res NUM_ENEMIES
enemy_dir_x: .res NUM_ENEMIES ; +1 or -1
enemy_dir_y: .res NUM_ENEMIES ; +1 or -1
.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
; 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 update_enemy_sprites
jmp main_loop
; -----------------------------------------------------------------------------
; Initialise Enemies with positions and directions
; -----------------------------------------------------------------------------
init_enemies:
; Enemy 0: Top-left, moving right and down
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
; Enemy 1: Top-right, moving left and down
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
; Enemy 2: Bottom-left, moving right and up
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
; Enemy 3: Bottom-right, moving left and up
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 - Bounce off arena walls
; -----------------------------------------------------------------------------
move_enemies:
ldx #0
@enemy_loop:
; Move X
lda enemy_x, x
clc
adc enemy_dir_x, x
sta enemy_x, x
; Check X bounds
cmp #ARENA_LEFT
bcs @check_right
; Hit left wall - reverse X direction
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
; Hit right wall - reverse X direction
lda #DIR_LEFT
sta enemy_dir_x, x
lda #ARENA_RIGHT
sec
sbc #1
sta enemy_x, x
@move_y:
; Move Y
lda enemy_y, x
clc
adc enemy_dir_y, x
sta enemy_y, x
; Check Y bounds
cmp #ARENA_TOP
bcs @check_bottom
; Hit top wall - reverse Y direction
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
; Hit bottom wall - reverse Y direction
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 in OAM
; -----------------------------------------------------------------------------
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
lda player_y
sta oam_buffer+0
lda player_x
sta oam_buffer+3
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
Enemies now patrol endlessly. The arena feels dangerous.
Next
Movement creates threat, but there’s no consequence for touching enemies yet. Unit 9 adds collision detection.
What Changed
Unit 7 → Unit 8
+166-172
| 1 | 1 | ; ============================================================================= | |
| 2 | - | ; NEON NEXUS - Unit 7: Enemies Appear | |
| 2 | + | ; NEON NEXUS - Unit 8: Enemy Movement | |
| 3 | 3 | ; ============================================================================= | |
| 4 | - | ; Add enemy sprites to the arena. | |
| 4 | + | ; Make enemies patrol the arena with bounce patterns. | |
| 5 | 5 | ; ============================================================================= | |
| 6 | 6 | | |
| 7 | 7 | ; ----------------------------------------------------------------------------- | |
| ... | |||
| 35 | 35 | PLAYER_START_X = 124 | |
| 36 | 36 | PLAYER_START_Y = 116 | |
| 37 | 37 | PLAYER_SPEED = 2 | |
| 38 | + | ENEMY_SPEED = 1 | |
| 38 | 39 | | |
| 39 | 40 | NUM_ENEMIES = 4 | |
| 40 | 41 | | |
| 41 | - | ; Tile indices (background) | |
| 42 | + | ; Tile indices | |
| 42 | 43 | TILE_EMPTY = 0 | |
| 43 | 44 | TILE_BORDER = 1 | |
| 44 | 45 | TILE_FLOOR = 2 | |
| ... | |||
| 47 | 48 | TILE_CORNER_BL = 5 | |
| 48 | 49 | TILE_CORNER_BR = 6 | |
| 49 | 50 | | |
| 50 | - | ; Sprite tiles | |
| 51 | 51 | SPRITE_PLAYER = 7 | |
| 52 | 52 | SPRITE_ENEMY = 8 | |
| 53 | 53 | | |
| ... | |||
| 56 | 56 | ARENA_RIGHT = 232 | |
| 57 | 57 | ARENA_TOP = 16 | |
| 58 | 58 | ARENA_BOTTOM = 208 | |
| 59 | + | | |
| 60 | + | ; Direction flags | |
| 61 | + | DIR_RIGHT = 1 | |
| 62 | + | DIR_LEFT = $FF ; -1 in two's complement | |
| 63 | + | DIR_DOWN = 1 | |
| 64 | + | DIR_UP = $FF | |
| 59 | 65 | | |
| 60 | 66 | BG_COLOUR = $0F | |
| 61 | 67 | | |
| ... | |||
| 63 | 69 | ; Memory Layout | |
| 64 | 70 | ; ----------------------------------------------------------------------------- | |
| 65 | 71 | .segment "ZEROPAGE" | |
| 66 | - | player_x: .res 1 | |
| 67 | - | player_y: .res 1 | |
| 68 | - | buttons: .res 1 | |
| 69 | - | temp: .res 1 | |
| 70 | - | row_counter: .res 1 | |
| 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 | |
| 71 | 78 | | |
| 72 | - | ; Enemy positions (4 enemies) | |
| 73 | - | enemy_x: .res NUM_ENEMIES | |
| 74 | - | enemy_y: .res NUM_ENEMIES | |
| 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 | |
| 75 | 84 | | |
| 76 | 85 | .segment "OAM" | |
| 77 | - | oam_buffer: .res 256 | |
| 86 | + | oam_buffer: .res 256 | |
| 78 | 87 | | |
| 79 | 88 | .segment "BSS" | |
| 80 | 89 | | |
| ... | |||
| 132 | 141 | jsr set_attributes | |
| 133 | 142 | jsr init_enemies | |
| 134 | 143 | | |
| 135 | - | ; Set up player | |
| 136 | 144 | lda #PLAYER_START_X | |
| 137 | 145 | sta player_x | |
| 138 | 146 | lda #PLAYER_START_Y | |
| 139 | 147 | sta player_y | |
| 140 | 148 | | |
| 141 | - | ; Initialise player sprite (OAM slot 0) | |
| 149 | + | ; Player sprite | |
| 142 | 150 | lda player_y | |
| 143 | 151 | sta oam_buffer+0 | |
| 144 | 152 | lda #SPRITE_PLAYER | |
| 145 | 153 | sta oam_buffer+1 | |
| 146 | - | lda #0 ; Attributes: palette 0, no flip | |
| 154 | + | lda #0 | |
| 147 | 155 | sta oam_buffer+2 | |
| 148 | 156 | lda player_x | |
| 149 | 157 | sta oam_buffer+3 | |
| 150 | 158 | | |
| 151 | - | ; Set up enemy sprites in OAM | |
| 152 | 159 | jsr update_enemy_sprites | |
| 153 | 160 | | |
| 154 | - | ; Hide remaining sprites (after player + 4 enemies = 5 sprites used) | |
| 161 | + | ; Hide remaining sprites | |
| 155 | 162 | lda #$FF | |
| 156 | - | ldx #20 ; Start after 5 sprites (5*4=20) | |
| 163 | + | ldx #20 | |
| 157 | 164 | @hide_sprites: | |
| 158 | 165 | sta oam_buffer, x | |
| 159 | 166 | inx | |
| ... | |||
| 162 | 169 | lda #0 | |
| 163 | 170 | sta PPUSCROLL | |
| 164 | 171 | sta PPUSCROLL | |
| 172 | + | sta frame_count | |
| 165 | 173 | | |
| 166 | 174 | lda #%10000000 | |
| 167 | 175 | sta PPUCTRL | |
| ... | |||
| 171 | 179 | main_loop: | |
| 172 | 180 | jsr read_controller | |
| 173 | 181 | jsr move_player | |
| 182 | + | jsr move_enemies | |
| 183 | + | jsr update_enemy_sprites | |
| 174 | 184 | jmp main_loop | |
| 175 | 185 | | |
| 176 | 186 | ; ----------------------------------------------------------------------------- | |
| 177 | - | ; Initialise Enemies - Place 4 enemies at corners of play area | |
| 187 | + | ; Initialise Enemies with positions and directions | |
| 178 | 188 | ; ----------------------------------------------------------------------------- | |
| 179 | 189 | init_enemies: | |
| 180 | - | ; Enemy 0: Top-left area | |
| 190 | + | ; Enemy 0: Top-left, moving right and down | |
| 181 | 191 | lda #48 | |
| 182 | 192 | sta enemy_x+0 | |
| 183 | 193 | lda #48 | |
| 184 | 194 | sta enemy_y+0 | |
| 195 | + | lda #DIR_RIGHT | |
| 196 | + | sta enemy_dir_x+0 | |
| 197 | + | lda #DIR_DOWN | |
| 198 | + | sta enemy_dir_y+0 | |
| 185 | 199 | | |
| 186 | - | ; Enemy 1: Top-right area | |
| 200 | + | ; Enemy 1: Top-right, moving left and down | |
| 187 | 201 | lda #200 | |
| 188 | 202 | sta enemy_x+1 | |
| 189 | 203 | lda #48 | |
| 190 | 204 | sta enemy_y+1 | |
| 205 | + | lda #DIR_LEFT | |
| 206 | + | sta enemy_dir_x+1 | |
| 207 | + | lda #DIR_DOWN | |
| 208 | + | sta enemy_dir_y+1 | |
| 191 | 209 | | |
| 192 | - | ; Enemy 2: Bottom-left area | |
| 210 | + | ; Enemy 2: Bottom-left, moving right and up | |
| 193 | 211 | lda #48 | |
| 194 | 212 | sta enemy_x+2 | |
| 195 | 213 | lda #176 | |
| 196 | 214 | sta enemy_y+2 | |
| 215 | + | lda #DIR_RIGHT | |
| 216 | + | sta enemy_dir_x+2 | |
| 217 | + | lda #DIR_UP | |
| 218 | + | sta enemy_dir_y+2 | |
| 197 | 219 | | |
| 198 | - | ; Enemy 3: Bottom-right area | |
| 220 | + | ; Enemy 3: Bottom-right, moving left and up | |
| 199 | 221 | lda #200 | |
| 200 | 222 | sta enemy_x+3 | |
| 201 | 223 | lda #176 | |
| 202 | 224 | sta enemy_y+3 | |
| 225 | + | lda #DIR_LEFT | |
| 226 | + | sta enemy_dir_x+3 | |
| 227 | + | lda #DIR_UP | |
| 228 | + | sta enemy_dir_y+3 | |
| 229 | + | | |
| 230 | + | rts | |
| 231 | + | | |
| 232 | + | ; ----------------------------------------------------------------------------- | |
| 233 | + | ; Move Enemies - Bounce off arena walls | |
| 234 | + | ; ----------------------------------------------------------------------------- | |
| 235 | + | move_enemies: | |
| 236 | + | ldx #0 | |
| 237 | + | | |
| 238 | + | @enemy_loop: | |
| 239 | + | ; Move X | |
| 240 | + | lda enemy_x, x | |
| 241 | + | clc | |
| 242 | + | adc enemy_dir_x, x | |
| 243 | + | sta enemy_x, x | |
| 244 | + | | |
| 245 | + | ; Check X bounds | |
| 246 | + | cmp #ARENA_LEFT | |
| 247 | + | bcs @check_right | |
| 248 | + | ; Hit left wall - reverse X direction | |
| 249 | + | lda #DIR_RIGHT | |
| 250 | + | sta enemy_dir_x, x | |
| 251 | + | lda #ARENA_LEFT | |
| 252 | + | sta enemy_x, x | |
| 253 | + | jmp @move_y | |
| 254 | + | | |
| 255 | + | @check_right: | |
| 256 | + | cmp #ARENA_RIGHT | |
| 257 | + | bcc @move_y | |
| 258 | + | ; Hit right wall - reverse X direction | |
| 259 | + | lda #DIR_LEFT | |
| 260 | + | sta enemy_dir_x, x | |
| 261 | + | lda #ARENA_RIGHT | |
| 262 | + | sec | |
| 263 | + | sbc #1 | |
| 264 | + | sta enemy_x, x | |
| 265 | + | | |
| 266 | + | @move_y: | |
| 267 | + | ; Move Y | |
| 268 | + | lda enemy_y, x | |
| 269 | + | clc | |
| 270 | + | adc enemy_dir_y, x | |
| 271 | + | sta enemy_y, x | |
| 272 | + | | |
| 273 | + | ; Check Y bounds | |
| 274 | + | cmp #ARENA_TOP | |
| 275 | + | bcs @check_bottom | |
| 276 | + | ; Hit top wall - reverse Y direction | |
| 277 | + | lda #DIR_DOWN | |
| 278 | + | sta enemy_dir_y, x | |
| 279 | + | lda #ARENA_TOP | |
| 280 | + | sta enemy_y, x | |
| 281 | + | jmp @next_enemy | |
| 282 | + | | |
| 283 | + | @check_bottom: | |
| 284 | + | cmp #ARENA_BOTTOM | |
| 285 | + | bcc @next_enemy | |
| 286 | + | ; Hit bottom wall - reverse Y direction | |
| 287 | + | lda #DIR_UP | |
| 288 | + | sta enemy_dir_y, x | |
| 289 | + | lda #ARENA_BOTTOM | |
| 290 | + | sec | |
| 291 | + | sbc #1 | |
| 292 | + | sta enemy_y, x | |
| 293 | + | | |
| 294 | + | @next_enemy: | |
| 295 | + | inx | |
| 296 | + | cpx #NUM_ENEMIES | |
| 297 | + | bne @enemy_loop | |
| 203 | 298 | | |
| 204 | 299 | rts | |
| 205 | 300 | | |
| ... | |||
| 207 | 302 | ; Update Enemy Sprites in OAM | |
| 208 | 303 | ; ----------------------------------------------------------------------------- | |
| 209 | 304 | update_enemy_sprites: | |
| 210 | - | ldx #0 ; Enemy index | |
| 211 | - | ldy #4 ; OAM offset (after player sprite) | |
| 305 | + | ldx #0 | |
| 306 | + | ldy #4 | |
| 212 | 307 | | |
| 213 | 308 | @loop: | |
| 214 | - | ; Y position | |
| 215 | 309 | lda enemy_y, x | |
| 216 | 310 | sta oam_buffer, y | |
| 217 | 311 | iny | |
| 218 | 312 | | |
| 219 | - | ; Tile | |
| 220 | 313 | lda #SPRITE_ENEMY | |
| 221 | 314 | sta oam_buffer, y | |
| 222 | 315 | iny | |
| 223 | 316 | | |
| 224 | - | ; Attributes: palette 1 (different colour from player) | |
| 225 | 317 | lda #%00000001 | |
| 226 | 318 | sta oam_buffer, y | |
| 227 | 319 | iny | |
| 228 | 320 | | |
| 229 | - | ; X position | |
| 230 | 321 | lda enemy_x, x | |
| 231 | 322 | sta oam_buffer, y | |
| 232 | 323 | iny | |
| ... | |||
| 485 | 576 | lda #>oam_buffer | |
| 486 | 577 | sta OAMDMA | |
| 487 | 578 | | |
| 488 | - | ; Update player sprite position | |
| 489 | 579 | lda player_y | |
| 490 | 580 | sta oam_buffer+0 | |
| 491 | 581 | lda player_x | |
| 492 | 582 | sta oam_buffer+3 | |
| 583 | + | | |
| 584 | + | inc frame_count | |
| 493 | 585 | | |
| 494 | 586 | lda #0 | |
| 495 | 587 | sta PPUSCROLL | |
| ... | |||
| 509 | 601 | ; Data | |
| 510 | 602 | ; ----------------------------------------------------------------------------- | |
| 511 | 603 | palette_data: | |
| 512 | - | ; Background palettes | |
| 513 | - | .byte BG_COLOUR, $11, $21, $31 ; Palette 0: Blue | |
| 514 | - | .byte BG_COLOUR, $13, $23, $33 ; Palette 1: Purple | |
| 515 | - | .byte BG_COLOUR, $19, $29, $39 ; Palette 2: Green | |
| 516 | - | .byte BG_COLOUR, $16, $26, $36 ; Palette 3: Red | |
| 517 | - | ; Sprite palettes | |
| 518 | - | .byte BG_COLOUR, $30, $27, $17 ; Palette 0: Player (white/orange) | |
| 519 | - | .byte BG_COLOUR, $16, $26, $36 ; Palette 1: Enemy (red) | |
| 604 | + | .byte BG_COLOUR, $11, $21, $31 | |
| 605 | + | .byte BG_COLOUR, $13, $23, $33 | |
| 606 | + | .byte BG_COLOUR, $19, $29, $39 | |
| 607 | + | .byte BG_COLOUR, $16, $26, $36 | |
| 608 | + | .byte BG_COLOUR, $30, $27, $17 | |
| 609 | + | .byte BG_COLOUR, $16, $26, $36 | |
| 520 | 610 | .byte BG_COLOUR, $30, $27, $17 | |
| 521 | 611 | .byte BG_COLOUR, $30, $27, $17 | |
| 522 | 612 | | |
| ... | |||
| 537 | 627 | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 538 | 628 | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 539 | 629 | | |
| 540 | - | ; Tile 1: Border (brick pattern) | |
| 541 | - | .byte %11111111 | |
| 542 | - | .byte %10000001 | |
| 543 | - | .byte %10000001 | |
| 544 | - | .byte %11111111 | |
| 545 | - | .byte %11111111 | |
| 546 | - | .byte %00010001 | |
| 547 | - | .byte %00010001 | |
| 548 | - | .byte %11111111 | |
| 549 | - | .byte %00000000 | |
| 550 | - | .byte %01111110 | |
| 551 | - | .byte %01111110 | |
| 552 | - | .byte %00000000 | |
| 553 | - | .byte %00000000 | |
| 554 | - | .byte %11101110 | |
| 555 | - | .byte %11101110 | |
| 556 | - | .byte %00000000 | |
| 630 | + | ; Tile 1: Border | |
| 631 | + | .byte %11111111,%10000001,%10000001,%11111111 | |
| 632 | + | .byte %11111111,%00010001,%00010001,%11111111 | |
| 633 | + | .byte %00000000,%01111110,%01111110,%00000000 | |
| 634 | + | .byte %00000000,%11101110,%11101110,%00000000 | |
| 557 | 635 | | |
| 558 | - | ; Tile 2: Floor (subtle grid) | |
| 559 | - | .byte %00000000 | |
| 560 | - | .byte %00000000 | |
| 561 | - | .byte %00000000 | |
| 562 | - | .byte %00000000 | |
| 563 | - | .byte %00000000 | |
| 564 | - | .byte %00000000 | |
| 565 | - | .byte %00000000 | |
| 566 | - | .byte %10000001 | |
| 567 | - | .byte %00000000 | |
| 568 | - | .byte %00000000 | |
| 569 | - | .byte %00000000 | |
| 570 | - | .byte %00000000 | |
| 571 | - | .byte %00000000 | |
| 572 | - | .byte %00000000 | |
| 573 | - | .byte %00000000 | |
| 574 | - | .byte %00000000 | |
| 636 | + | ; Tile 2: Floor | |
| 637 | + | .byte %00000000,%00000000,%00000000,%00000000 | |
| 638 | + | .byte %00000000,%00000000,%00000000,%10000001 | |
| 639 | + | .byte %00000000,%00000000,%00000000,%00000000 | |
| 640 | + | .byte %00000000,%00000000,%00000000,%00000000 | |
| 575 | 641 | | |
| 576 | 642 | ; Tile 3: Corner TL | |
| 577 | - | .byte %11111111 | |
| 578 | - | .byte %11000000 | |
| 579 | - | .byte %10100000 | |
| 580 | - | .byte %10010000 | |
| 581 | - | .byte %10001000 | |
| 582 | - | .byte %10000100 | |
| 583 | - | .byte %10000010 | |
| 584 | - | .byte %10000001 | |
| 585 | - | .byte %00000000 | |
| 586 | - | .byte %00111111 | |
| 587 | - | .byte %01011111 | |
| 588 | - | .byte %01101111 | |
| 589 | - | .byte %01110111 | |
| 590 | - | .byte %01111011 | |
| 591 | - | .byte %01111101 | |
| 592 | - | .byte %01111110 | |
| 643 | + | .byte %11111111,%11000000,%10100000,%10010000 | |
| 644 | + | .byte %10001000,%10000100,%10000010,%10000001 | |
| 645 | + | .byte %00000000,%00111111,%01011111,%01101111 | |
| 646 | + | .byte %01110111,%01111011,%01111101,%01111110 | |
| 593 | 647 | | |
| 594 | 648 | ; Tile 4: Corner TR | |
| 595 | - | .byte %11111111 | |
| 596 | - | .byte %00000011 | |
| 597 | - | .byte %00000101 | |
| 598 | - | .byte %00001001 | |
| 599 | - | .byte %00010001 | |
| 600 | - | .byte %00100001 | |
| 601 | - | .byte %01000001 | |
| 602 | - | .byte %10000001 | |
| 603 | - | .byte %00000000 | |
| 604 | - | .byte %11111100 | |
| 605 | - | .byte %11111010 | |
| 606 | - | .byte %11110110 | |
| 607 | - | .byte %11101110 | |
| 608 | - | .byte %11011110 | |
| 609 | - | .byte %10111110 | |
| 610 | - | .byte %01111110 | |
| 649 | + | .byte %11111111,%00000011,%00000101,%00001001 | |
| 650 | + | .byte %00010001,%00100001,%01000001,%10000001 | |
| 651 | + | .byte %00000000,%11111100,%11111010,%11110110 | |
| 652 | + | .byte %11101110,%11011110,%10111110,%01111110 | |
| 611 | 653 | | |
| 612 | 654 | ; Tile 5: Corner BL | |
| 613 | - | .byte %10000001 | |
| 614 | - | .byte %10000010 | |
| 615 | - | .byte %10000100 | |
| 616 | - | .byte %10001000 | |
| 617 | - | .byte %10010000 | |
| 618 | - | .byte %10100000 | |
| 619 | - | .byte %11000000 | |
| 620 | - | .byte %11111111 | |
| 621 | - | .byte %01111110 | |
| 622 | - | .byte %01111101 | |
| 623 | - | .byte %01111011 | |
| 624 | - | .byte %01110111 | |
| 625 | - | .byte %01101111 | |
| 626 | - | .byte %01011111 | |
| 627 | - | .byte %00111111 | |
| 628 | - | .byte %00000000 | |
| 655 | + | .byte %10000001,%10000010,%10000100,%10001000 | |
| 656 | + | .byte %10010000,%10100000,%11000000,%11111111 | |
| 657 | + | .byte %01111110,%01111101,%01111011,%01110111 | |
| 658 | + | .byte %01101111,%01011111,%00111111,%00000000 | |
| 629 | 659 | | |
| 630 | 660 | ; Tile 6: Corner BR | |
| 631 | - | .byte %10000001 | |
| 632 | - | .byte %01000001 | |
| 633 | - | .byte %00100001 | |
| 634 | - | .byte %00010001 | |
| 635 | - | .byte %00001001 | |
| 636 | - | .byte %00000101 | |
| 637 | - | .byte %00000011 | |
| 638 | - | .byte %11111111 | |
| 639 | - | .byte %01111110 | |
| 640 | - | .byte %10111110 | |
| 641 | - | .byte %11011110 | |
| 642 | - | .byte %11101110 | |
| 643 | - | .byte %11110110 | |
| 644 | - | .byte %11111010 | |
| 645 | - | .byte %11111100 | |
| 646 | - | .byte %00000000 | |
| 661 | + | .byte %10000001,%01000001,%00100001,%00010001 | |
| 662 | + | .byte %00001001,%00000101,%00000011,%11111111 | |
| 663 | + | .byte %01111110,%10111110,%11011110,%11101110 | |
| 664 | + | .byte %11110110,%11111010,%11111100,%00000000 | |
| 647 | 665 | | |
| 648 | - | ; Tile 7: Player sprite | |
| 649 | - | .byte %00011000 | |
| 650 | - | .byte %00011000 | |
| 651 | - | .byte %00111100 | |
| 652 | - | .byte %01111110 | |
| 653 | - | .byte %11111111 | |
| 654 | - | .byte %10111101 | |
| 655 | - | .byte %00100100 | |
| 656 | - | .byte %00100100 | |
| 657 | - | .byte %00000000 | |
| 658 | - | .byte %00011000 | |
| 659 | - | .byte %00011000 | |
| 660 | - | .byte %00111100 | |
| 661 | - | .byte %01000010 | |
| 662 | - | .byte %01000010 | |
| 663 | - | .byte %00011000 | |
| 664 | - | .byte %00000000 | |
| 666 | + | ; Tile 7: Player | |
| 667 | + | .byte %00011000,%00011000,%00111100,%01111110 | |
| 668 | + | .byte %11111111,%10111101,%00100100,%00100100 | |
| 669 | + | .byte %00000000,%00011000,%00011000,%00111100 | |
| 670 | + | .byte %01000010,%01000010,%00011000,%00000000 | |
| 665 | 671 | | |
| 666 | - | ; Tile 8: Enemy sprite (hostile diamond shape) | |
| 667 | - | .byte %00011000 | |
| 668 | - | .byte %00111100 | |
| 669 | - | .byte %01111110 | |
| 670 | - | .byte %11111111 | |
| 671 | - | .byte %11111111 | |
| 672 | - | .byte %01111110 | |
| 673 | - | .byte %00111100 | |
| 674 | - | .byte %00011000 | |
| 675 | - | .byte %00000000 | |
| 676 | - | .byte %00011000 | |
| 677 | - | .byte %00100100 | |
| 678 | - | .byte %01000010 | |
| 679 | - | .byte %01000010 | |
| 680 | - | .byte %00100100 | |
| 681 | - | .byte %00011000 | |
| 682 | - | .byte %00000000 | |
| 672 | + | ; Tile 8: Enemy | |
| 673 | + | .byte %00011000,%00111100,%01111110,%11111111 | |
| 674 | + | .byte %11111111,%01111110,%00111100,%00011000 | |
| 675 | + | .byte %00000000,%00011000,%00100100,%01000010 | |
| 676 | + | .byte %01000010,%00100100,%00011000,%00000000 | |
| 683 | 677 | | |
| 684 | - | ; Fill rest of CHR-ROM | |
| 678 | + | ; Fill rest | |
| 685 | 679 | .res 8192 - 144, $00 | |
| 686 | 680 | |