Optimized In Air Collision - RetroKoH/S1Fixed GitHub Wiki
(Original Guide by Devon)
Source: Sonic Retro thread
Commit: 2a5ca76
This change is one that came about from a discussion Devon and myself had regarding in-air terrain detection, and the way it was handled in Orbinaut Framework on Game Maker. He noted that it completely omitted any manner of angle calculation, and subsequently realized that the same behavior could be applied to the actual Sonic engine. This guide, written by him and edited by myself, is based on his findings and developments.
Sonic handles airborne collision detection by calculating the general direction that he is moving, and then jumping to the appropriate code for handling collision for that specific direction. If he's moving downwards, he would check collision with the walls and the floor. If moving upwards, he would check collision with the walls and ceiling. If moving left or right, he would check for walls ahead of him, and then check for ceilings and floors. It would determine general direction by taking the angle he is moving at, using CalcAngle with his X and Y speeds as the parameters, and the output angle value determines the direction he is moving.
This works, but we can use a less intensive calculation to handle this by directly checking the speed values ourselves, and using them to determine Sonic's direction depending on if he is moving more horizontally than vertically, along with the direction of the larger speed value. We know this, because the angle values that separate the different directions are all angles where the absolute values of X and Y are the same.
Here is a chart showing the new logic with the conditions that should be checked:
Go to _incObj/Sonic Floor.asm and replace this:
Sonic_Floor:
move.l #v_collision1&$FFFFFF,(v_collindex).w ; MJ: load first collision data location
cmpi.b #$C,(v_top_solid_bit).w ; MJ: is second collision set to be used?
beq.s .first ; MJ: if not, branch
move.l #v_collision2&$FFFFFF,(v_collindex).w ; MJ: load second collision data location
.first:
move.b (v_lrb_solid_bit).w,d5 ; MJ: load L/R/B soldity bit
move.w obVelX(a0),d1
move.w obVelY(a0),d2
jsr (CalcAngle).l
move.b d0,(v_unused3).w
subi.b #$20,d0
move.b d0,(v_unused4).w
andi.b #$C0,d0
move.b d0,(v_unused5).w
cmpi.b #$40,d0
beq.w loc_13680
cmpi.b #$80,d0
beq.w loc_136E2
cmpi.b #$C0,d0
beq.w loc_1373E
with this:
Sonic_Floor:
move.l #v_collision1&$FFFFFF,(v_collindex).w ; MJ: load first collision data location
cmpi.b #$C,(v_top_solid_bit).w ; MJ: is second collision set to be used?
beq.s .first ; MJ: if not, branch
move.l #v_collision2&$FFFFFF,(v_collindex).w ; MJ: load second collision data location
.first:
move.b (v_lrb_solid_bit).w,d5 ; MJ: load L/R/B soldity bit
move.w obVelX(a0),d0
move.w obVelY(a0),d1
bpl.s .airCol_PosY ; If it's positive, branch
cmp.w d0,d1 ; Are we moving towards the left?
bgt.w loc_13680 ; If so, branch
neg.w d0 ; Are we moving towards the right?
cmp.w d0,d1
bge.w loc_1373E ; If so, branch
bra.w loc_136E2 ; We are moving upwards
.airCol_PosY:
cmp.w d0,d1 ; Are we moving towards the right?
blt.w loc_1373E ; If so, branch
neg.w d0 ; Are we moving towards the left?
cmp.w d0,d1
ble.w loc_13680 ; If so, branch