Skip to content

Commit fe8b226

Browse files
committed
Part 7: collision detection against platforms, snowball AI using them
1 parent fe6ce5e commit fe8b226

File tree

1 file changed

+131
-7
lines changed

1 file changed

+131
-7
lines changed

SpecBong.asm

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ GameLoop:
204204
call Player1MoveByControls
205205
call SnowballvsPlayerCollision
206206

207+
IF 0 ; DEBUG wait for fire key after frame
208+
.waitForFire: call ReadInputDevices : ld a,(Player1Controls) : bit JOY_BIT_FIRE,a : jr z,.waitForFire
209+
.waitForRelease: call ReadInputDevices : ld a,(Player1Controls) : bit JOY_BIT_FIRE,a : jr nz,.waitForRelease
210+
ENDIF
211+
207212
; do the GameLoop infinitely
208213
jr GameLoop
209214

@@ -238,6 +243,8 @@ SnowballvsPlayerCollision:
238243
; the collision detection will use circle formula (x*x+y*y=r*r), but we will first reject
239244
; the fine-calculation by box-check, player must be +-15px (cetre vs centre) near ball
240245
; to do the fine centres distance test (16*16=256 => overflow in the simplified MUL logic)
246+
bit 7,(ix+S_SPRITE_4B_ATTR.vpat)
247+
jr z,.skipCollisionCheck ; ball is invisible, skip the test
241248
; read and normalize snowball pos X
242249
ld e,(ix+S_SPRITE_4B_ATTR.x)
243250
ld d,(ix+S_SPRITE_4B_ATTR.mrx8)
@@ -315,6 +322,60 @@ SnowballvsPlayerCollision:
315322
ld (ix+S_SPRITE_4B_ATTR.mrx8),a
316323
ret
317324

325+
;-------------------------------------------------------------------------------------
326+
; Part 7 - platforms collisions
327+
; These don't check the image pixels, but instead there are few columns accross
328+
; the screen, and for each column there can be 8 platforms defined. These data are
329+
; hand-tuned for the background image. Each column is 16px wide, so there are 16 columns
330+
; per PAPER area. But the background is actually only 192x192 (12 columns), and I will
331+
; define +1 column extra on each side in case some sprite is partially out of screen.
332+
; Single column data is 16 bytes: 8x[height of platform, extras] where extras will be
333+
; 8bit flags for things like ladders/etc.
334+
335+
GetPlatformPosUnder:
336+
; In: IX = sprite pointer (centre x-coordinate is used for collision, i.e. +8)
337+
; Out: A = platform coordinate (in sprite coordinates 0..255), C = extras byte
338+
; for X coordinate outside of range, or very low Y the [A:255, C:0] is always returned
339+
push hl
340+
call .implementation
341+
; returns through here only when outside of range or no platform found
342+
ld a,255
343+
ld c,0
344+
pop hl
345+
ret
346+
.implementation:
347+
bit 0,(ix+S_SPRITE_4B_ATTR.mrx8)
348+
ret nz ; 256..511 is invalid X range (no column data)
349+
ld a,(ix+S_SPRITE_4B_ATTR.x)
350+
sub 16-8 ; -16 to skip 16px, +8 to test centre of sprite (not left edge)
351+
ret c ; 0..7 is invalid X range (no column data)
352+
; each column is 8 platforms x2 bytes = 16 bytes -> the X coordinate top 4 bits
353+
; are indentical to address of particular column! (no need to multiply/divide)
354+
cp low PlatformsCollisionDataEnd
355+
ret nc ; 224 <= (X-16) -> invalid X range (224 = 14*16) - 14 columns are defined
356+
and -16 ; clear the bottom four bits of X -> becomes low-byte of address
357+
ld l,a
358+
ld h,high PlatformsCollisionData ; HL = address of column data
359+
ld a,(ix+S_SPRITE_4B_ATTR.y) ; raw sprite Y (top edge)
360+
cp 255-13
361+
ret nc ; already too low to even check (after +13 only 13..254 are valid for check)
362+
add a,13 ; the base-line coordinate, the sprite can be 2px deep into platform to "hit" it
363+
; now we are ready to compare against the data in column table
364+
jr .columnDataLoopEntry
365+
.columnDataLoop:
366+
inc l
367+
inc l
368+
.columnDataLoopEntry:
369+
cp (hl)
370+
jr nc,.columnDataLoop ; platformY <= spriteY_base_line -> will not catch this one
371+
; this platform is below baseline, report it as hit
372+
ld a,(hl)
373+
inc l
374+
ld c,(hl)
375+
pop hl ; discard return address from .implementation
376+
pop hl ; restore HL
377+
ret ; return directly to caller with results in A and C
378+
318379
;-------------------------------------------------------------------------------------
319380
; "AI" subroutines
320381

@@ -407,15 +468,40 @@ SnowballsAI:
407468
ld de,S_SPRITE_4B_ATTR
408469
ld b,SNOWBALLS_CNT-1 ; move all of them except last
409470
.loop:
471+
bit 7,(ix+S_SPRITE_4B_ATTR.vpat)
472+
jr z,.doNextSnowball ; if the sprite is not visible, don't process it
473+
; check the Y coordinate against platform's collisions
474+
call GetPlatformPosUnder
475+
sub 16 ; snowball wants platform at +16 (bottom of ball is bottom of sprite)
476+
cp (ix+S_SPRITE_4B_ATTR.y)
477+
jr z,.isInOrAtPlatform ; at plaform
478+
jr c,.isInOrAtPlatform ; in platform (will reset Y to be *at*)
479+
ld l,a ; keep the platformY for compare
480+
; falling, keep the previous direction (extracted from mirroX flag into C=0/1)
481+
ld c,0
482+
bit 3,(ix+S_SPRITE_4B_ATTR.mrx8) ; mirroX bit
483+
jr z,.fallingRight
484+
inc c ; falling left
485+
.fallingRight
486+
ld a,(ix+S_SPRITE_4B_ATTR.y)
487+
inc a ; Y += 1
488+
cp l ; did it land at platform now?
489+
adc a,0 ; if not, do another Y += 1 (falling at +2 speed)
490+
; continue with "in/at platform" code, but keeps direction, sets new fall Y
491+
.isInOrAtPlatform:
492+
ld (ix+S_SPRITE_4B_ATTR.y),a
493+
; check if the ball is not under the screen, make it invisible then (+simpler AI)
494+
rl (ix+S_SPRITE_4B_ATTR.vpat) ; throw away visibility flag
495+
cp 192+32 ; Fc=1 if (Y < 192+32)
496+
rr (ix+S_SPRITE_4B_ATTR.vpat) ; set new visibility from carry
497+
; if at platform, choose direction by platform extras
498+
rr c ; extras bit 0 to carry
499+
sbc a,a ; 0 = right, $FF = left
410500
; HL = current X coordinate (9 bit)
411501
ld l,(ix+S_SPRITE_4B_ATTR.x)
412502
ld h,(ix+S_SPRITE_4B_ATTR.mrx8)
413-
; adjust it by some +- value deducted from B (32..1 index)
414503
ld c,0 ; mirrorX flag = 0
415-
ld a,b
416-
and 3 ; A = 0,1,2,3
417-
sli a ; A = 1, 3, 5, 7
418-
sub 4 ; A = -3, -1, +1, +3
504+
sli a ; Right: A=+1 Fc=0 || Left: A=-1 Fc=1
419505
; do: HL += signed(A) (the "add hl,a" is "unsigned", so extra jump+adjust needed)
420506
jr nc,.moveRight
421507
dec h
@@ -434,11 +520,11 @@ SnowballsAI:
434520
ld a,(TotalFrames)
435521
xor b
436522
and 7
437-
jr nz,.notEightFrameYet
523+
jr nz,.doNextSnowball
438524
ld a,(ix+S_SPRITE_4B_ATTR.vpat)
439525
xor 1
440526
ld (ix+S_SPRITE_4B_ATTR.vpat),a
441-
.notEightFrameYet:
527+
.doNextSnowball:
442528
add ix,de ; next snowball
443529
djnz .loop ; do all of them
444530
ret
@@ -547,6 +633,44 @@ SprSnowballs: EQU Sprites + 0*S_SPRITE_4B_ATTR ; first snowball sprite
547633
SprPlayer: EQU Sprites + SNOWBALLS_CNT*S_SPRITE_4B_ATTR ; player sprite is here
548634
SprCollisionFx: EQU SprPlayer + S_SPRITE_4B_ATTR
549635

636+
;-------------------------------------------------------------------------------------
637+
; Part 7 - platforms collisions - data of platforms and their heights + extras
638+
639+
ALIGN 256 ; align for simpler calculation of address
640+
PlatformsCollisionData: ; the Y-coord of platforms are in sprite coordinates! (+32: 0..255)
641+
; extras:
642+
; bit 0: 0=slope to right, 1=slope to left (to affect snowballs movement)
643+
; bit 1: there's ladder near the platform
644+
; column 0 (sprite coordinates 16..31)
645+
DB 75, 0, 127, 0, 185, 0, 222, 1, 255, 0, 255, 0, 255, 0, 255, 0
646+
; column 1 (sprite coordinates 32..47)
647+
DB 75, 0, 128, 0, 186, 0, 222, 1, 255, 0, 255, 0, 255, 0, 255, 0
648+
; column 2 (sprite coordinates 48..)
649+
DB 75, 0, 108, 1, 129, 0, 167, 1, 187, 0, 222, 1, 255, 0, 255, 0
650+
; column 3 (sprite coordinates 64..)
651+
DB 75, 0, 107, 3, 130, 2, 166, 3, 188, 2, 222, 1, 255, 0, 255, 0
652+
; column 4 (sprite coordinates 80..)
653+
DB 51, 1, 75, 0, 106, 1, 131, 0, 165, 1, 189, 0, 222, 1, 255, 0
654+
; column 5 (sprite coordinates 96..)
655+
DB 51, 1, 75, 0, 105, 1, 132, 0, 164, 1, 190, 0, 222, 1, 255, 0
656+
; column 6 (sprite coordinates 112..)
657+
DB 51, 3, 75, 2, 104, 1, 133, 0, 163, 1, 191, 0, 221, 1, 255, 0
658+
; column 7 (sprite coordinates 128..)
659+
DB 51, 3, 75, 2, 103, 1, 134, 0, 162, 1, 192, 0, 220, 1, 255, 0
660+
; column 8 (sprite coordinates 144..)
661+
DB 76, 0, 102, 1, 135, 0, 161, 1, 193, 0, 219, 1, 255, 0, 255, 0
662+
; column 9 (sprite coordinates 160..)
663+
DB 77, 0, 101, 1, 136, 0, 160, 1, 194, 0, 218, 1, 255, 0, 255, 0
664+
; column 10 (sprite coordinates 176..)
665+
DB 78, 2, 100, 3, 137, 2, 159, 3, 195, 2, 217, 3, 255, 0, 255, 0
666+
; column 11 (sprite coordinates 192..)
667+
DB 79, 0, 99, 1, 138, 0, 158, 1, 196, 0, 216, 1, 255, 0, 255, 0
668+
; column 12 (sprite coordinates 208..)
669+
DB 98, 1, 157, 1, 215, 1, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0
670+
; column 13 (sprite coordinates 224..)
671+
DB 97, 1, 156, 1, 214, 1, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0
672+
PlatformsCollisionDataEnd:
673+
550674
;-------------------------------------------------------------------------------------
551675
; reserve area for stack at $B800..$BFFF region
552676
ORG $B800

0 commit comments

Comments
 (0)