Thursday, July 20, 2017

Hide and seek

You might have noticed that, if you bump your head on the ceiling and fail to collect it the first time around, this Special Stage ring halfway through Angel Island Zone 1 likes to disappear until you land back on the floor proper.


While they're on screen, Special Stage rings DMA their graphics over the area normally taken up by enemy explosions. The explosion graphics aren't reloaded until the ring is deleted, which is probably why unlike almost every other object, the ring deletes itself when you go too far away from it vertically as well as horizontally.

I say almost, because this behavior isn't unknown. Throwaway object such as enemy projectiles, or crumbling platform debris all exhibit it, using some variation on the Sprite_CheckDeleteXY function shown below. Special Stage rings are particularly interesting though, because they're meant to respawn once you scroll them back into range.
Sprite_CheckDeleteXY:
    move.w  $10(a0),d0
    andi.w  #-$80,d0
    sub.w   (Camera_X_pos_coarse_back).w,d0
    cmpi.w  #$280,d0
    bhi.w   Go_Delete_Sprite
    move.w  $14(a0),d0
    sub.w   (Camera_Y_pos).w,d0
    addi.w  #$80,d0
    cmpi.w  #$200,d0
    bhi.w   Go_Delete_Sprite
    jmp     (Draw_Sprite).l
Once per frame, the object manager calculates the Camera_X_pos_coarse_back RAM variable to define the range of objects which is currently active. It's basically the live camera position rounded down to the nearest 128 pixel boundary, as seen below. Each time the camera moves 128 pixels horizontally, the range slides over and new objects are loaded.
loc_1B7F2:
    move.w  (Camera_Y_pos).w,d1
    subi.w  #$80,d1
    andi.w  #$FF80,d1
    move.w  d1,(Camera_Y_pos_coarse_back).w
    move.w  (Camera_X_pos).w,d1
    subi.w  #$80,d1
    andi.w  #$FF80,d1
    move.w  d1,(Camera_X_pos_coarse_back).w
Already we can see a discrepancy, though. The object manager maintains coarse values for both the camera's X and Y positions, but the Sprite_CheckDeleteXY function above uses the live value for the Y position. The result is that unless it's vertically aligned with a 128 pixel boundary, there's a mismatch between the range at which the object deletes itself, and the range at which it is respawned by the object manager.

Which is exactly what afflicts our buddy up there.

       Object deleted at this camera position       Object respawned at this camera position

No comments:

Post a Comment