don't click here

Interesting collision/physics observations in Sonic 1/2/3

Discussion in 'Engineering & Reverse Engineering' started by Lapper, Aug 17, 2024.

  1. Lapper

    Lapper

    Lappering Tech Member
    1,782
    1,016
    93
    England
    Sonic Studio, Sonic Physics Guide, Kyle & Lucy, Freedom Planet 2
    It does indeed, it uses the same code as Oil Ocean, but modified a bit.
    Code (ASM):
    1.  
    2. ; Sonic 3 sinking mud code
    3. ObjMGZMud_Main:
    4.         ; check player 1
    5.         tst.w    (Debug_placement_mode).w
    6.         bne.w    ObjMGZMud_End
    7.  
    8.         ; Check if the player is standing on the platform
    9.         lea    (Player_1).w,a1
    10.         btst    #p1_standing_bit,$2A(a0)
    11.         bne.s    ObjMGZMud_CheckKillChar1
    12.  
    13.         ; Here, the player is not standing on the platform.
    14.         ; The submersion value is updated
    15.         cmpi.b    #$30,mud_char1submersion(a0) ;48 ($30) is the maximum submersion value
    16.         bcc.s    loc_31D5E
    17.         addq.b    #2,mud_char1submersion(a0) ;If the player is not standing on the platform, bring it up 2 pixels per frame (platforms recovery speed is double that of S2)
    18.  
    19. ; This is new and was not present in sonic 2, extra checks for whether it should skip killing Sonic
    20. loc_31D5E:
    21.         btst    #p1_standing_bit,$2A(a1)
    22.         beq.s    ObjMGZMud_CheckSupportChar1
    23.         movea.w    $42(a1),a2
    24.         cmpi.l    #loc_31D3E,(a2)
    25.         bne.s    ObjMGZMud_CheckSupportChar1
    26.         move.b    mud_char1submersion(a2),mud_char1submersion(a0)
    27.         bra.s    ObjMGZMud_CheckSupportChar1
    28. ; ---------------------------------------------------------------------------
    29.  
    30. ;loc_31D7A:
    31. ObjMGZMud_CheckKillChar1:
    32.         ; Here, the player is standing on the platform
    33.         ; Test the submersion, if this value is 0 Sonic will suffocate
    34.         tst.b    mud_char1submersion(a0)
    35.         beq.s    ObjMGZMud_SuffocateCharacter
    36.  
    37.         ; If the value isn't 0 and if the player is standing on the platform, drag it down 1 pixel per frame
    38.         subq.b    #1,mud_char1submersion(a0)
    39.  
    40. ;loc_31D84:
    41. ObjMGZMud_CheckSupportChar1:
    42.         ; Stop the character from falling past the mud by creating a "fake" solid platform
    43.         moveq    #0,d1
    44.         move.b    7(a0),d1     ; Inputs platform's width radius
    45.         moveq    #0,d3
    46.         move.b    mud_char1submersion(a0),d3     ; Inputs submersion value as the height radius
    47.         moveq    #p1_standing_bit,d6
    48.         move.w    $10(a0),d4  ; Inputs platform's x position
    49.         jsr    (sub_1BFC2).l ; Platform solidity - targeted at a single character, this will be repeated for the other
    50.  
    Forgive the messiness of this code example, the S3K disassembly I have on hand is unlabelled and uncommented so I did some quick manual name changes in this example, forgive me if anything is inconsistent!!
    But you get the idea


    Ah, I was wrong when I said they crush you horizontally! Got my wires crossed (maybe it was Mania that has horz crushing, I'd have to check). They do not even in Sonic 2, they are just there to stop you going through them. I have corrected the original post!

    upload_2024-9-1_19-28-36.png
     
    Last edited: Sep 1, 2024
    • Informative Informative x 3
    • Like Like x 2
    • List
  2. penBorefield

    penBorefield

    Badniks, attack! Member
    382
    66
    28
    Basement
    Conquering the world
    Don't forget Azure Lake and Desert Palace.
     
    Last edited: Sep 2, 2024
  3. RetroKoH

    RetroKoH

    Member
    1,735
    118
    43
    S1Fixed: A successor to ReadySonic
    You and me both... I am have severe Mandela Effect feelings right now.
     
  4. Brainulator

    Brainulator

    Regular garden-variety member Member
    Something interesting about that object: as I mentioned before, it seems like in the Sonic 1 prototype we have, the code that handles the GHZ wall's (or kageb, according to Sonic 2 Nick Arcade) is also used for spikes, the door (object $2A, which was redone in the final game), and the glass pillars in Marble Zone. Furthermore, as mentioned earlier in that thread, the Nick Arcade prototype just makes the GHZ wall use the normal solid object routine. You can see the effects using debug mode: Sonic and Tails can stand on it... although, from the looks of things, they're standing a few pixels above it. I imagine this may cause differences in actual gameplay, but then again, GHZ's collision in that build is messed up...
     
    • Informative Informative x 3
    • List
  5. Tiberious

    Tiberious

    Yeah, I'm furry. Got a problem? Banned
    I notice that pink box is not touching both green boxes, and that's probably why it's not killing you. I know I distinctly remember being killed by a bad jump into the latter half of the Mega Mack climb often enough in my earlier playthroughs. What happens if you jump in that situation?
     
  6. Lapper

    Lapper

    Lappering Tech Member
    1,782
    1,016
    93
    England
    Sonic Studio, Sonic Physics Guide, Kyle & Lucy, Freedom Planet 2
    Yeah! The fake GHZ wall has too large a height radius (8 extra pixels on the top and bottom) for its collision
    Code (ASM):
    1.  
    2. Edge_Solid:    ; Routine 2
    3.         move.w    #$13,d1                    ; width = 19 ($13)
    4.                                         ; this is correct  [8 (actual width) + 10 (player push radius) + 1]
    5.  
    6.         move.w    #$28,d2                    ; height = 40 ($28)
    7.                                         ; this is incorrect, the sprite's height is actually 32
    8.  
    9.         bsr.w    Edge_SolidWall
    10.  
    I'm not sure why this is, possibly to ensure they always overlap and push Sonic out to the sides right up to the terrain corner!

    So yes if the collision is instead normal and Sonic can land on it, then the extended height will make it even easier for Sonic to walk on them by accident


    In that case, both objects there are in fact pushing sonic out to the side, it just so happens the block on the left does so last, and pushes Sonic into the one on the right before the frame is rendered. Each frame. when the left block is about to push Sonic out, he's both inside it and was just pushed left by the block on the right, but he doesn't get crushed.

    Regarding the topic overall, I had the same memories also. However, I did tests and looked at the code in Sonic 1 and 2 and I didn't see any way to crush horizontally. I was surprised to realise this because I did assume it was the case - but I don't think there's any horizontal crushing. My memory of being crushed horizontally is still strong so forgive me being slightly hesitant to even confirm it haha.

    I think horizontal crushing was introduced in Sonic Mania, as I couldn't even crush myself against the chemical plant walls in the Remake but in Mania this behaviour is immediately obvious.
    If someone knows better and I am missing something please correct me, I look closely at the object collision but I can imagine some edge case or seperate code slipping my grasp.



    Also here's some more solid object deep dive

    Sloped Objects Are Cheap Tricks
    Most solid objects in classic Sonic games are flat, just box shaped. These will use nice and simple AABB collision checks for acting solid to the player, pretty much everything I've been talking about in previous posts.

    This one is more geared to those who might not already be extremely familiar with how sloped objects work but I'll also be diving into some code to explain why certain quirks occur.

    Some objects have special sloped surfaces
    upload_2024-9-15_13-39-17.png

    These can be fully solid objects or platforms, they both work the same way.
    This applies to objects such as grassy platforms, seesaws, chemical plant springboards, the moving slopes in hill top, and much more.


    Each sloped object provides itself with some custom height array data.
    [​IMG]

    For example, the height array for first object's slope is
    Code (Text):
    1. [32 32 32 32 32 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 48 48 48 48 48]
    These values represent the distance of each part of the slope's surface from the centre. But NOT each pixel, as each value in the slope array data represents two pixels, aka it is "compressed" by half.

    Here's a sample of the code for sloped objects, such as the Marble zone platforms, when the height array is read
    Code (ASM):
    1.  
    2. ; ---------------------------------------------------------------------------
    3. ; Solid    object with slope array subroutine (MZ grass platforms)
    4. ;
    5. ; input:
    6. ;    d1 = object width / 2
    7. ;    d2 = object height / 2
    8. ;    a2 = address of heightmap/slope array data
    9. ;
    10. ; output:
    11. ;    d4 = collision type: 1 = side collision; -1 = top/bottom collision
    12. ;    d5 = x distance of Sonic from nearest left/right edge
    13. ;    a1 = address of OST of Sonic
    14. ;    uses d0, d1, d2, d3, a2
    15. ; ---------------------------------------------------------------------------
    16.  
    17. SolidObject_Heightmap:
    18.         ; Initial checks
    19.         lea    (v_ost_player).w,a1
    20.         tst.b    ost_render(a0)
    21.         bpl.w    Solid_NoCollision            ; branch if object is off screen
    22.  
    23.         ; First, the normal x position checks
    24.         move.w    ost_x_pos(a1),d0
    25.         sub.w    ost_x_pos(a0),d0
    26.         add.w    d1,d0                    ; d0 = x pos of Sonic on object
    27.         bmi.w    Solid_NoCollision            ; branch if Sonic is outside left edge
    28.         move.w    d1,d3
    29.         add.w    d3,d3                    ; d3 = full width of object
    30.         cmp.w    d3,d0
    31.         bhi.w    Solid_NoCollision            ; branch if Sonic is outside right edge
    32.  
    33.         ; Get relative x position of Sonic along the object's surface
    34.         move.w    d0,d5                    ; d5 = x pos of Sonic from the object's left edge
    35.  
    36.         ; Flip d5 if the object is flipped (and thus the opposite height array value will need to be read)
    37.         btst    #render_xflip_bit,ost_render(a0)    ; is object horizontally flipped?
    38.         beq.s    .no_xflip                ; if not, branch
    39.         not.w    d5
    40.         add.w    d3,d5                    ; x pos of Sonic on object is xflipped
    41.  
    42.     .no_xflip:
    43.  
    44.         ; Here we get the current height value for Sonic is on the object.
    45.         ; At this point, d5 = x pos of Sonic on object, xflipped if needed
    46.         lsr.w    #1,d5                    ; halve the value of x pos of Sonic on object (because the height array is compressed, this will allow the correct height value to be accessed)
    47.         moveq    #0,d3
    48.         move.b    (a2,d5.w),d3            ; get heightmap value based on Sonic's position on platform
    49.         sub.b    (a2),d3                    ; subtract baseline
    50.         move.w    ost_y_pos(a0),d5        ; d5 = object y position
    51.         sub.w    d3,d5                    ; d5 = y pos of sloped floor where Sonic is
    52.         move.b    ost_height(a1),d3        ; d3 = sonic height rad
    53.         ext.w    d3                        ;
    54.         add.w    d3,d2                    ; d2 = combined Sonic height radius + object height radius
    55.  
    Essentially, the height array is read at an index equal to half the relative X Position of the player and the left edge of the sloped object, this gets us the surface position, and this is then used for the rest of the collision.
    SPGSlopeTerrainArray.gif

    Slope Skewing
    Sloped objects are functionally identical to normal solid objects, moreso than you might even think. Entirely in fact.

    Sloped objects still only use flat AABB collision - so the sloped effect is created with an extremely simple and effective trick.
    The entire object's surface effectively "shifts" vertically up or down to line up with the slope at the current point in the height array that Sonic is over. This vertical shifting also occurs when Sonic passes over it the object but isn't standing on it, it's a global shift for all states of object collision.

    So it's a normal solid object, with normal solid object collision, only difference is it's solidity will shift up and down dynamically. It's about as much of an illusion as it gets!

    It's not just the surface that gets affected by the slope, however. Due to this way the way the object collision works after the sloped surface position has been calculated, the slope array essentially skews the entire object, not just the surface.

    Here's a visual interpretation.
    [​IMG][​IMG]
    In these images, each pixel column of the solidity box is shifted vertically to where it will be when Sonic's X position is overlapping it.

    Here's an animated example with the diagonal spring (because it's pretty dramatic here)
    [​IMG]
    On the left is the intended interpretation, on the right is what is "actually" happening (also applies if Sonic was in the air)

    Because the entire object has to shift to meet the position of the slope, this has the unfortunate side effect of well, I'm not even sure what to call this. Look for yourself.
    Sonic2SpringUnderside.gif

    In fact, if you put one too low and try walking under it, it'll crush you- because it's as if the Spring's solidity lowers down onto you as you pass under.
    Sonic2SpringCrush.gif

    Ideally, the bottom of a sloped object would remain flat... however this just simply wasn't accounted for. When the new surface position is calculated, the rest of the object code just assumes the object remains the same height from there.

    Code (ASM):
    1.  
    2.     ; This is the code immediately after the previous snippet, this will be the initial y position check against sonic
    3.     ; Note: d5 currently = y pos of sloped surface where Sonic is
    4.  
    5.         move.b    ost_height(a1),d3        ; d3 = sonic height radius
    6.         ext.w    d3                        ;
    7.         add.w    d3,d2                    ; d2 = combined Sonic height radius + object height radius (the normal object height radius)
    8.  
    9.         move.w    ost_y_pos(a1),d3        ; d3 = sonic y
    10.         sub.w    d5,d3                    ; d3 = sonic y - slope surface y (difference between sonic y and slope surface y, negative if above)
    11.         addq.w    #4,d3                    ; d3 = add 4 to difference
    12.         add.w    d2,d3                    ; d3 = difference + combined y radius (y dist of Sonic's feet from slope surface y)
    13.         bmi.w    Solid_NoCollision        ; branch if Sonic is above the surface
    14.  
    15.         ; Now it will check if Sonic is underneath, important to note is this is ALL based on the previously calculated surface position
    16.         move.w    d2,d4                    ; d2/d4 = combined Sonic height radius + object height radius (the normal object height radius)
    17.         add.w    d4,d4                    ; d4 = combined Sonic full height + object full height
    18.         cmp.w    d4,d3                    ; check if distance from the surface of the object is greater than this combined height (the normal combined height of the object)
    19.         bcc.w    Solid_NoCollision        ; branch if Sonic is below object
    20.         bra.w    Solid_Collision
    21.  
    Essentially once the surface position of the object is calculated the object will just continues to use the normal height of the object to figure out where the "bottom" is, effectively skewing the entire thing. Ideally, a new bottom position would be calculated independently of the surface position, but this is what we get!

    Interestingly though, the code I just showed is separate to that used in normal object collision, so some more substantive changes could have been made to these checks.
    However, there may be good reasons why they kept it how it is - such as to avoid other unintended issues with small or uneven object sizes, plus the rest of the code that follows would also require some changes.

    Slope Collision Is... Off
    The collision with the slopes on sold objects is different to the slopes of terrain tiles. Okay, that's pretty obvious... but I want to highlight a specific difference with how this positions sonic.

    Rather than Sonic standing atop slope perched on one of his sides as he would on the terrain, his centre position will be aligned with the slope, and one of his sides will be "inside" it. They could have emulated terrain collision by reading 2 different values of the array at either side of Sonic, and choosing the highest one, but no one ever notices this as sloped objects are usually grassy.
    upload_2024-9-15_13-35-34.png

    Slope Fidelity Is Low
    As mentioned above, slopes object height arrays are "compressed" by half, meaning they store the height for every other pixel and this will be "stretched out" when read.

    Due to this "compression" of the slope data, object slopes can only contain a change in elevation once every 2 pixels along the surface.
    Most slopes only end up being shallow (only going up by 1 pixel for every 2 along the surface), but 45 degree angles become blocky, as they need to be 2 pixels up for every 2 pixels across.
    [​IMG]
    These are rare though, and obviously you can't even walk here on a spring. This does occur in some other places but is usually not an issue at all.

    Slope Physics (or lack therof)
    I don't really have any fancy code or info to show here as it's pretty self explanatory. As you can probably already guess these have no angle data and don't even attempt to emulate it. Sonic's angle will remain 0 while atop any object.
    [​IMG]

    Objects also don't calculate Sonic's Y Speed up and down slopes as is the case on Terrain, so a "slope jump" from an object won't be any different to a normal jump. The only reason a slope jump works is because the jump speed is added to Sonic's existing speeds on the ground, so if there's no Y Speed to add to, you get normal jumps.
     
    Last edited: Sep 16, 2024
    • Informative Informative x 9
    • Like Like x 3
    • List