Browse Source

Implement player collision routine

master
Forest Belton 2 years ago
parent
commit
d9cdd42f2e
5 changed files with 270 additions and 84 deletions
  1. BIN
      png/map/intro_coll.png
  2. +23
    -22
      scripts/generate_map.py
  3. +19
    -1
      src/bg.s
  4. +133
    -0
      src/collision.s
  5. +95
    -61
      src/player.s

BIN
png/map/intro_coll.png View File

Before After
Width: 160  |  Height: 144  |  Size: 545 B Width: 160  |  Height: 144  |  Size: 378 B

+ 23
- 22
scripts/generate_map.py View File

@ -16,7 +16,9 @@ def abort(msg: str) -> NoReturn:
sys.exit(1)
def generate_coll_map(in_path: pathlib.Path, width: int, height: int) -> str:
def generate_coll_map(
in_path: pathlib.Path, width: int, height: int, compress: bool = False
) -> str:
png = Image.open(in_path).convert("RGB")
if png.width % 8 != 0 or png.height % 8 != 0:
abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)")
@ -33,37 +35,35 @@ def generate_coll_map(in_path: pathlib.Path, width: int, height: int) -> str:
bit = None
if pixel == RED:
bit = 1
elif pixel == GREEN:
bit = 0
elif pixel == GREEN:
bit = 1
else:
abort(f"unsupported pixel in collision map: {pixel}")
bits.append(bit)
if len(bits) == 8:
byte = sum([bit << i for i, bit in enumerate(bits)])
out_bytes.append(byte)
bits = []
if compress:
bits.append(bit)
if len(bits) == 8:
byte = sum([bit << i for i, bit in enumerate(bits)])
out_bytes.append(byte)
bits = []
else:
out_bytes.append(bit)
png.close()
lines = []
for line_no in range(0, len(out_bytes), 16):
line = out_bytes[line_no : line_no + 16]
lines.append(" DB " + ", ".join(["$%02X" % b for b in line]))
return "\n".join(lines)
return format_bytes(out_bytes, width=width)
def format_bytes(data: bytes) -> str:
def format_bytes(data: bytes, width: int = 16) -> str:
print(f"formatting with width={width}")
lines = []
for line_no in range(0, len(data), 16):
line = data[line_no : line_no + 16]
for line_no in range(0, len(data), width):
line = data[line_no : line_no + width]
lines.append(" DB " + ", ".join(["$%02X" % b for b in line]))
return "\n".join(lines)
def generate_map(pngfile: str) -> None:
def generate_map(pngfile: str, compress: bool = False) -> None:
pngpath = pathlib.Path(pngfile).resolve()
incpath = pngpath.parent / pngpath.name.replace(".png", ".inc")
spath = pngpath.parent / pngpath.name.replace(".png", ".s")
@ -90,13 +90,13 @@ def generate_map(pngfile: str) -> None:
]
)
map_data = format_bytes(tilef.read())
map_data = format_bytes(tilef.read(), width=width)
tile_data = format_bytes(mapf.read())
section = pngpath.name.replace(".png", "")
collpath = pngpath.parent / pngpath.name.replace(".png", "_coll.png")
coll_map = generate_coll_map(collpath, width, height)
coll_map = generate_coll_map(collpath, width, height, compress=compress)
with open(incpath, "w") as outf:
outf.write(
@ -131,9 +131,10 @@ ASSERT {section}_MAP_SIZE == {section}_WIDTH * {section}_HEIGHT
def main() -> None:
parser = argparse.ArgumentParser("generate_map")
parser.add_argument("-c", "--compress", default=False)
parser.add_argument("png")
args = parser.parse_args()
generate_map(args.png)
generate_map(args.png, compress=(not not args.compress))
if __name__ == "__main__":

+ 19
- 1
src/bg.s View File

@ -1,7 +1,23 @@
INCLUDE "hardware.inc"
INCLUDE "png/map/intro.inc"
SECTION "BG0", ROM0
SECTION "BG Data", WRAM0
BG_COLLISION_DATA:: dw
BG_MAP_WIDTH:: db
SECTION "BG Code", ROM0
MACRO update_map_info
ld hl, BG_COLLISION_DATA
ld a, HIGH(\1)
ld [hli], a
ld a, LOW(\1)
ld [hli], a
ld a, \2
ld [hli], a
ENDM
BG_Init::
; copy map
@ -31,4 +47,6 @@ BG_Init::
ld d, intro_NUM_TILES
call memcpy
update_map_info intro_COLLISION, intro_WIDTH
ret

+ 133
- 0
src/collision.s View File

@ -0,0 +1,133 @@
INCLUDE "oam.inc"
SECTION "Collision", ROMX
; Check whether a 16x16 metasprite collides with the background
; @param a The index of the first (top left) OAM entry of the sprite
; @destroy hl
; note: doesn't work atm (use srl, not sra?)
sprite16x16_bg_collides::
; compute oam entry address (_OAM + a * 4)
; a is guaranteed not to overflow since l = $00 and a < 40
ld hl, _OAM
sla a
sla a
add l
ld l, a
; top left (x,y)
ld a, [hl+]
sra a
sra a
sra a
ld c, [hl]
sra c
sra c
sra c
call can_move_to
ret nz
; other three sprites
REPT 3
ld a, [hl+]
sra a
sra a
sra a
ld c, [hl]
sra c
sra c
sra c
call can_move_to
ret nz
ENDR
ret
player_bg_collides::
; c = x/8
ld hl, PLAYER_X
ld a, [hl]
ld c, a
and %111
jr z, .skip_inc_c
ld a, c
add 8
ld c, a
.skip_inc_c:
srl c
srl c
srl c
; b = y/8
inc hl
ld a, [hl]
ld b, a
and %111
jr z, .skip_inc_b
ld a, b
add 8
ld b, a
.skip_inc_b:
srl b
srl b
srl b
; top left
call can_move_to
ret z
; top right
inc c
call can_move_to
ret z
; bottom left
dec c
inc b
call can_move_to
ret z
; bottom right
inc c
call can_move_to
ret
; Check whether the location can be moved to or not
; @param b y-coordinate
; @param c x-coordinate
; @destroy a, d, e
; @note Z = false, NZ = true
can_move_to:
; todo: should be aware of scx/scy
push bc
push hl
; hl = bg_collision_data
ld hl, BG_COLLISION_DATA
ld d, [hl]
inc hl
ld e, [hl]
push de
; de = map width
ld hl, BG_MAP_WIDTH
ld d, 0
ld e, [hl]
pop hl
; compute map index (HL + B * (BG_MAP_WIDTH + 1) + C)
.mul_y_width:
add hl, de
dec b
jr nz, .mul_y_width
add hl, bc
; check [hl] = 1
ld a, [hl]
cp 0
pop hl
pop bc
ret

+ 95
- 61
src/player.s View File

@ -7,6 +7,11 @@ Section "Player Data", WRAM0
playerWorldX: dw
playerWorldY: dw
; player data
PLAYER_X:: db
PLAYER_Y:: db
PLAYER_DIR:: db
Section "Player Code", ROM0
spriteData:
@ -18,6 +23,14 @@ DEF SPRITE_WIDTH EQU 2
DEF SPRITE_HEIGHT EQU 2
Player_Init::
; clear player data
ld hl, PLAYER_X
ld [hl], 0
inc hl
ld [hl], 144-32
inc hl
ld [hl], 0
ld a, 8
ld hl, _OAM + 1
ld [hl], a
@ -56,13 +69,27 @@ Player_Update::
jr z, .left
; check for right boundary
ld_OAM_x hl, SPRITE_OAM_IDX + 1
ld hl, PLAYER_X
ld a, [hl]
add 16
cp SCRN_X
jr nc, .left
; TODO: check collision
call move_right
inc [hl]
call player_bg_collides
jr z, .right_rollback
ld hl, PLAYER_DIR
ld [hl], 0
call update_oam
ret
.right_rollback:
ld hl, PLAYER_X
dec [hl]
ret
.left:
; check for left button
@ -71,90 +98,97 @@ Player_Update::
ret z
; check for left boundary
ld_OAM_x hl, SPRITE_OAM_IDX
ld hl, PLAYER_X
ld a, [hl]
cp 9
ret c
or a
ret z
; TODO: check collision
call move_left
dec [hl]
ret
call player_bg_collides
jr z, .left_rollback
move_right:
ld_OAM_x hl, SPRITE_OAM_IDX
ld hl, PLAYER_DIR
ld [hl], $ff
; update top left sprite
inc [hl]
inc hl
ld [hl], SPRITE_IDX
inc hl
res OAMB_XFLIP, [hl]
call update_oam
ret
; update top right sprite
inc hl
inc hl
inc [hl]
inc hl
ld [hl], SPRITE_IDX + 1
inc hl
res OAMB_XFLIP, [hl]
.left_rollback:
ld hl, PLAYER_X
dec [hl]
ret
; update bottom left sprite
inc hl
inc hl
inc [hl]
inc hl
ld [hl], SPRITE_IDX + 2
inc hl
res OAMB_XFLIP, [hl]
; Update sprite OAM entries with current position and direction
update_oam:
ld hl, PLAYER_DIR
ld a, [hl-] ; dir (0 = right , $ff = left)
and OAMF_XFLIP
ld c, a
; update bottom right sprite
inc hl
inc hl
inc [hl]
inc hl
ld [hl], SPRITE_IDX + 3
inc hl
res OAMB_XFLIP, [hl]
ld a, [hl-] ; y
add 16
ret
ld d, a
ld a, [hl] ; x
add 8
ld b, a
ld a, d
move_left:
ld_OAM_x hl, SPRITE_OAM_IDX
ld_OAM_y hl, SPRITE_OAM_IDX
; top left
dec [hl]
; top left sprite
ld [hli], a
ld [hl], b
inc hl
ld [hl], SPRITE_IDX + 1
ld [hl], SPRITE_IDX
inc hl
ld [hl], c
inc hl
set OAMB_XFLIP, [hl]
; top right
ld d, a
ld a, b
add 8
ld b, a
ld a, d
ld [hli], a
ld [hl], b
inc hl
ld [hl], SPRITE_IDX + 1
inc hl
dec [hl]
inc hl
ld [hl], SPRITE_IDX
ld [hl], c
inc hl
set OAMB_XFLIP, [hl]
; bottom left
add 8
ld d, a
ld a, b
sub 8
ld b, a
ld a, d
ld [hli], a
ld [hl], b
inc hl
ld [hl], SPRITE_IDX + 2
inc hl
dec [hl]
inc hl
ld [hl], SPRITE_IDX + 3
ld [hl], c
inc hl
set OAMB_XFLIP, [hl]
; bottom right
ld d, a
ld a, b
add 8
ld b, a
ld a, d
ld [hli], a
ld [hl], b
inc hl
ld [hl], SPRITE_IDX + 3
inc hl
dec [hl]
inc hl
ld [hl], SPRITE_IDX + 2
inc hl
set OAMB_XFLIP, [hl]
ld [hl], c
ret

Loading…
Cancel
Save