Enemy-Bullet Collision

When we are udating enemies, we’ll call a function called “CheckCurrentEnemyAgainstBullets”. This will check the current enemy against all active bullets.

This fuction needs to loop through the bullet object pool, and check if our current enemy overlaps any bullet on both the x and y axis. If so, we’ll deactivate the enemy and bullet.

Our “CheckCurrentEnemyAgainstBullets” function starts off in a manner similar to how we updated enemies & bullets.

This function expects “hl” points to the curent enemy. We’ll save that in a variable for later usage.

include "src/main/utils/hardware.inc"
include "src/main/utils/constants.inc"
include "src/main/utils/hardware.inc"

SECTION "EnemyBulletCollisionVariables", WRAM0

wEnemyBulletCollisionCounter: db
wBulletAddresses: dw

SECTION "EnemyBulletCollision", ROM0

; called from enemies.asm
CheckCurrentEnemyAgainstBullets::


    ld a, l
    ld [wUpdateEnemiesCurrentEnemyAddress], a
    ld a, h
    ld [wUpdateEnemiesCurrentEnemyAddress+1], a

    xor a
    ld [wEnemyBulletCollisionCounter], a
    
    ; Copy our bullets address into wBulletAddress
    ld a, LOW(wBullets)
    ld l, a
    ld a, HIGH(wBullets)
    ld h, a

    jp CheckCurrentEnemyAgainstBullets_PerBullet

As we loop through the bullets, we need to make sure we only check active bullets. Inactive bullets will be skipped.

CheckCurrentEnemyAgainstBullets_PerBullet:

    ld a, [hl]
    cp 1
    jp nz, CheckCurrentEnemyAgainstBullets_Loop

First, we need to check if the current enemy and current bullet are overlapping on the x axis. We’ll get the enemy’s x position in e, and the bullet’s x position in b. From there, we’ll again call our “CheckObjectPositionDifference” function. If it returns a failure (wResult=0), we’ll start with the next bullet.

We add an offset to the x coordinates so they measure from their centers. That offset is half it’s respective object’s width.

CheckCurrentEnemyAgainstBullets_Check_X_Overlap:

    ; Save our first byte address
    push hl

    inc hl

    ; Get our x position
    ld a, [hli]
    add 4
    ld b, a

    push hl

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Start: Checking the absolute difference
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ; The first value
    ld a, b
    ld [wObject1Value], a

    ; The second value
    ld a, [wCurrentEnemyX]
    add 8
    ld [wObject2Value], a

    ; Save if the minimum distance
    ld a, 12
    ld [wSize], a

    call CheckObjectPositionDifference

    
    ld a, [wResult]
    and a
    jp z, CheckCurrentEnemyAgainstBullets_Check_X_Overlap_Fail

    
    pop hl

    jp CheckCurrentEnemyAgainstBullets_PerBullet_Y_Overlap

CheckCurrentEnemyAgainstBullets_Check_X_Overlap_Fail:

    pop hl
    pop hl

    jp CheckCurrentEnemyAgainstBullets_Loop

Next we restore our hl variable so we can get the y position of our current bullet. Once we have that y position, we’ll get the current enemy’s y position and check for an overlap on the y axis. If no overlap is found, we’ll loop to the next bullet. Otherwise, we have a collision.

    
CheckCurrentEnemyAgainstBullets_PerBullet_Y_Overlap:

    ; get our bullet 16-bit y position
    ld a, [hli]
    ld b, a

    ld a, [hli]
    ld c, a

    ; Descale our 16 bit y position
    srl c
    rr b
    srl c
    rr b
    srl c
    rr b
    srl c
    rr b

    ; preserve our first byte addresss
    pop hl
    push hl

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Start: Checking the absolute difference
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ; The first value
    ld a, b
    ld [wObject1Value], a

    ; The second value
    ld a, [wCurrentEnemyY]
    ld [wObject2Value], a

    ; Save if the minimum distance
    ld a, 16
    ld [wSize], a

    call CheckObjectPositionDifference

    pop hl
    
    ld a, [wResult]
    and a
    jp z, CheckCurrentEnemyAgainstBullets_Loop
    jp CheckCurrentEnemyAgainstBullets_PerBullet_Collision

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; End: Checking the absolute difference
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    

If a collision was detected (overlap on x and y axis), we’ll set the current active byte for that bullet to 0. Also , we’ll set the active byte for the current enemy to zero. Before we end the function, we’ll increase and redraw the score, and decrease how many bullets & enemies we have by one.

CheckCurrentEnemyAgainstBullets_PerBullet_Collision:

    ; set the active byte  and x value to 0 for bullets
    xor a
    ld [hli], a
    ld [hl], a

    ld a, [wUpdateEnemiesCurrentEnemyAddress+0]
    ld l, a
    ld a, [wUpdateEnemiesCurrentEnemyAddress+1]
    ld h, a

    ; set the active byte  and x value to 0 for enemies
    xor a
    ld [hli], a
    ld [hl], a
    
    call IncreaseScore
    call DrawScore

    ; Decrease how many active enemies their are
    ld a, [wActiveEnemyCounter]
    dec a
    ld [wActiveEnemyCounter], a

    ; Decrease how many active bullets their are
    ld a, [wActiveBulletCounter]
    dec a
    ld [wActiveBulletCounter], a

    ret

If no collision happened, we’ll continue our loop through the enemy bullets. When we’ve checked all the bullets, we’ll end the function.

CheckCurrentEnemyAgainstBullets_Loop:

    ; increase our counter
    ld a, [wEnemyBulletCollisionCounter]
    inc a
    ld [wEnemyBulletCollisionCounter], a

    ; Stop if we've checked all bullets
    cp MAX_BULLET_COUNT
    ret nc

    ; Increase the  data our address is pointing to
    ld a, l
    add PER_BULLET_BYTES_COUNT
    ld l, a
    ld a, h
    adc 0
    ld h, a