Sonic collision bugs
From Sonic Retro
The 2D Sonic the Hedgehog games for the Sega Mega Drive have rather complicated collision routines. This article explains these rules in a more high-level view, as well as explaining several collision bugs.
This guide covers the following games, which are referred to by the following abbreviations:
I will be using images captured from modified versions of Gens Re-Recording (which show solidity and hitboxes), to illustrate the concepts. These images have received some light editing. Here is a clean image from the aforementioned version of Gens Re-Recording:
There are 3 kinds of collision which any block (a 2x2 group of 8x8 tiles) can have:
The green-tinted block are top solid blocks. The red-tinted blocks are L/R/B solid blocks; L/R/B stands for "Left/Right/Bottom solid". Blue-tinted blocks are full solid blocks; a full solid block is just a combination of L/R/B solidity with top solidity, whereas a block without any solidity has neither. So whenever I say "top solid" or "L/R/B solid", I mean "top or full solid" or "L/R/B or full solid", unless I say otherwise. There is a bit of an inconsistency with how solidity is used; I will explain more later.
A character (Sonic/Tails/Knuckles/<insert hack character here>) has a collision box, a hitbox, and several ray anchors:
The pink box around Sonic is the collision box. The white box around Sonic is the hitbox. Small purple boxes with black plus signs inside are ray anchors.
The white box around Sonic is his hitbox. The hitbox is for being hurt by Badniks, collecting rings, and so forth. We will not care about it other to note it, so you can ignore it. The pink box around Sonic is his collision box. The collision box is used for colliding with solid and platform objects, as well as terrain; but whereas the collision box is rotated by 90/180/270 degrees in some cases when colliding with terrain, it is never rotated for collision with objects. The collision box and hitbox generally have two different sizes, a larger one for standing and a smaller one for rolling. Knuckles has another different collision box size for gliding/sliding, and another for falling from glide/climbing. S1/S2/S3/S&K/S3&K all have a bug that reverts collision box size to standing box size when the character jumps from a rolling state; SCD fixed it.
For all images, the collision box is always shown unrotated; meaning it will always be collision with objects. For terrain collision we will instead use ray anchors instead of the collision box.
Ray anchors (with one exception, which I will explain later) are placed in specific points on border of the (possibly rotated) collision box. How many of them depends on whether the character is on "free fall" (on air) or "on the ground" (not on air). Each ray anchor casts one sensor ray:
Sensor rays are displayed as horizontal or vertical purple lines starting from the center of ray anchors. Their length can be negative (if the character is inside terrain) or positive (otherwise), ranging from -31 to 31 pixels at most -- the game only checks the character's current block and one block beyond, so you will actually see values in [-(16+Z),16+(15-Z)] range, with Z being the character's position inside the block (that is, in the [0, 15] range). The sensor ray will also save the angle of the terrain it strikes to RAM, so it can be used after.
- Phasing Bug: Due to the limited range of terrain detection, we already have the first collision bug, and that is without any mention on how those things are used in game! Namely: if your speed is high enough, and your position is in the right range, you can skip ("phase through") thinner walls, floors, or ceilings. This is more likely in S1/SCD because they have thinner walls, floors, and ceilings than the other games. S2 generally has thicker walls, floors, and ceilings than S1 and SCD, and S3/S&K/S3&K have a lot thicker walls, floors, and ceilings than S2. S&K/S3&K, in particular, has some levels going really overboard with collision, most notably Death Egg. It is generally hard to trigger this even in S1/SCD because the required speeds are above the speed cap (except for maximum charge Super Peel-Out with Power Sneakers in SCD), so you generally need another bug/glitch to attain the required speed first. This can be mitigated by using wider collision, or by having multiple physics updates per frame with lower speeds in each update. The mitigation will be generally good enough.
A floor (ceiling) check casts 2 sensor rays anchored at the bottom (top) corners of the unrotated collision box. These rays are cast down (up), and stop at the first top solid (L/R/B solid) block found. Reverse gravity generally exchanges the direction of rays cast by ceiling and floor checks, so that they keep being ceiling and floor checks.
A left (right) wall check casts 1 sensor ray anchored at the middle of the left (right) side of the unrotated collision box. These rays are cast left (right), and stop at the first L/R/B solid block found.
When the character is on the ground, I will use the term "walking ground check" to mean something closely related to a floor check: specifically, it can mean:
- A walkable floor check if the character is walking mostly on what would ordinarily be called floor. This works exactly like a floor check.
- A walkable ceiling check if the character is walking mostly on what would be called ceiling. This works like a ceiling check, except that it checks for top solid blocks instead.
- A walkable left/right wall check if the character is walking mostly on what would be called a wall. These are different, in many respects, from ordinary left/right wall checks: a walkable left (right) wall check casts two sensor rays anchored at the left (right) corners of the rotated collision box. These rays are cast left (right), and stop at the first top solid block found.
- Walkability Bug: all walking ground checks look for top solid terrain. This is the inconsistency I mentioned earlier, and it is the second collision bug -- and I haven't even explained how any of this is used in-game! You can see manifestations of this bug on all games by observing that any terrain you are supposed to walk on is top solid or full solid; loops, in particular, are full solid. Another manifestation is when you use debug mode and reverse gravity. You can "land" on some ceilings (because they are L/R/B solid), but you immediately start to fall through them right after (because they are not top solid). It is exploited, in S2, to allow you to detach from walls/ceiling on appropriate locations (particularly in Metropolis Zone). The root cause is that the top solid collision bit is used to check terrain collisions when running, even when the L/R/B solid bit should be used.
- False Landing Bug: The ray anchors for floor detection are further apart when the character is on the ground than when he/she is on the air (except when jumping from a rolling state). The difference is small, but it is enough that you may land on a thin enough strip of terrain, and then immediately fall through it afterwards. Fixes include making the thinnest floor wider, pulling the ray anchors closer together, or adding a third ray anchor in the middle.
There is one more kind of ray anchor to talk about: the zip anchor:
The zip anchor is the small purple box with a white plus signs inside; similar to normal ray anchors, the zip anchor also casts a sensor ray. The zip anchor only exists when a character is on the ground and moving. It is also restricted by angle: it only shows up when the angle is in the range [-90,90) (all games) or when the angle is a multiple of 90 degrees (S&K/S3&K) (note that this is an "or"; either condition works in S&K/S3&K). Its location starts at the center of the character's sprite, add the character's speed (x speed to x position, y speed to y position (flip sign of y speed if under reversed gravity)), then pick one of:
- If walking mostly horizontally right (left) at a very shallow angle, add 8 to y position, and add (subtract) 10 pixels to x position if going right (left). Ray is cast right (left), and stops at the first L/R/B solid block it finds.
- If walking mostly horizontally right (left) but at a steeper angle, add (subtract) 10 pixels to x position if going right (left). Ray is cast right (left), and stops at the first L/R/B solid block it finds.
- if walking mostly vertically down (up), add (subtract) 10 pixels to y position if going down (up). Ray is cast down (up), and stops at the first L/R/B solid block it finds.
That was complicated; but these details do not matter for most of what follows. What you need to understand is that the zip anchor is ahead of the (rotated) collision box, it is a bit up from the floor (allowing the characters to run up steps), it only appears when you are moving, and it is not present at all angles.
A check that looks for a wall starting from the zip anchor will be called a running wall check.
- Bumpability Bug: Note that the zip anchor always checks for L/R/B solid terrain. This means that you will run right through a top solid-only floor when running down, even though you would land on it if you were on air. This is similar to the Walkability Bug, and has the same root cause: the incorrect solidity bit is used in some cases. It does not happen anywhere in any of the games.
- Ghost Wall Bug: Due to the limitations placed on when the zip anchor is used, you will walk through L/R/B walls and floors whenever you are even slightly upside down, or going straight up. This is most notable in plain S3 (without S&K) in Marble Garden 2 due to how easy it was to do it, but happens on all earlier games as well. This was fixed when going at 90, 180 and 270 degrees in S&K/S3&K, but the bug is still present for other angles.
Let's start with the character being on air, since it is easier. There are actually two different ways that collision happens when on air: Knuckles' gliding/sliding, and everything else. They are generally identical, with two exceptions: the first exception is that Knuckles' gliding/sliding collision routine sets flags when you hit terrain and saves to RAM, allowing you to check if a collision happened (so he can attach to a wall, or slide on a floor). The second exception I will mention later.
When you are on-air, collision is checked after the character moves for the frame, and it is based on your broad direction of motion:
- You are moving horizontally left faster than you are moving up or down. Collision checks are, in order:
- You are moving vertically up faster than you are moving left or right. Collision checks are, in order:
- You are moving horizontally right faster than you are moving up or down. Collision checks are, in order:
- You are moving vertically down faster than you are moving left or right. Collision checks are, in order:
- Left wall check with potential ejection.
- Right wall check with potential ejection.
- Floor check with potential landing, unless -1 × (vertical speed, in pixels, +8) is less than the distance either sensor is inside the floor (more below); in this case, you just go through the floor. This exception does not exist for Knuckles' gliding/sliding collision function.
Ejection, in this context, means moving until outside the wall, and setting the corresponding speed to zero. Landing means just that: the character will from now on be considered as being on the ground for all purposes.
Note the asymmetry between moving mostly left and moving mostly right: moving mostly left has an aberrant right wall check, whose purpose I don't know.
- Corner Clip: Note that when you are moving up (not mostly up, just up), if the wall check anchor is above a corner you will go through the solid terrain. One may argue whether or not it is a bug/glitch/whatever; but it seems like it is intentional, so I won't consider it as such. There is a corresponding case for clipping ceiling corners when moving down (not mostly down, just down).
- Fall Through Ledges: When moving mostly down, there was that very strange special case which I said I would explain later. Its purpose is to let a character fall through some ledges he/she has not quite gotten clear of:
- Stair Clip Bug: So called because of its most notable instance in Lava Reef 1, it was originally found by speedrunners. Then a host of similar clips were found by TASers (me included), and I discovered that they were all caused by the same thing. The Stair Clip is a bug that allows you to fall through floors under some conditions by exploiting high enough speed, corner clip, and falling through ledges. It affects all games, but S1 and SCD have no useful instances for speedrunning.
- It is possible to fix the Stair Clip while still allowing a character to fall through ledges like that: you can just check if the floor in question is L/R/B solid; if it is, skip the code that lets you fall through the floor. You even have access to the data easily at the point where you need to check it.
- Instant Stop Landing Bug: When falling from far enough up, you may end up deep enough inside the floor to trigger wall collisions with it. This will kill your horizontal speed, then cause you to land on top of the terrain. This is useful for speedrunning when it can be done consistently because it allows you to instantly start spindashing right after.
- Land on Air Bug: When you jump from a rolling state, the character's collision box reverts back to standing height due to a bug in the jumping code. For Tails, this barely makes a difference; but for Sonic, Knuckles, and hack characters using Sonic's collision box, this makes a 10-pixel difference (5 at the top, 5 at the bottom). The games also incorrectly adjusts the characters Y position to keep it centered, so that the character sinks these 5 pixels inside the ground (less for Tails). There is another bug, though: when the character lands, the games see that the character is rolling, and unconditionally subtracts the 5 pixels of height difference from the character's Y position (again, less for Tails). For Tails, the difference is small enough that he snaps back onto the floor. For the other characters, they detach from the floor and start falling again. However, for a single frame, they are considered to be on the floor, and you can even jump from 5 pixels up.
- Exceptions: SCD does not suffer from this issue: it has fixed the roll jump bug, and so the landing works as expected (though it could be argued it still has the landing bug).
- S3/S&K/S3&K fix the landing bug by storing the size of the character's default (standing) collision box at object initialization, then subtracting the difference between current and standing collision box sizes when the character lands.
Finally, there is a major difference between S&K/S3&K (and, I think, S3) and the other games: for the cases where you are going mostly left (right), S&K/S3&K (/S3?) check all cases of collision mentioned above, while the other games stop at the first collision check where you are inside terrain.
This is about all that there is for terrain collision while on air.
Ground collision is more complicated; for starters, it is done in stages, whereas air collision is done in a single function.
The first stage is done at the function that checks for left/right input for acceleration/deceleration. Right after checking for input, the function performs some checks to see if the character should change to a balancing animation:
- When standing on a solid or platform object, and the character's vertical center line is past either end of the object, the character will lose balance.
- If the character is standing on terrain, sensor rays will be cast. The number and location of the ray anchors depend on the character; but at minimum, a vertical ray is cast from an anchor at the middle of the bottom edge of the character's collision box. Sonic casts more rays to determine which of his animations are to be used, while Tails and Knuckles cast only one sensor ray. These rays have no effect other than to see if the character should use a balancing animation or not.
Note, also, that character must also be still and in mostly level ground to balance.
- Object/Terrain Balancing Bug: When standing at the edge of an object, characters will swap to their balancing animation even if their other "foot" is on solid ground. This is because the game does not check for the presence of a floor in this case. Fixing this is just a matter of adding the missing check.
- Terrain/Object Balancing Bug: Likewise, when standing at the edge of terrain, characters will swap to their balancing animation even if their other "foot" is on a solid or platform object. Basically, the converse of the above bug. Fixing this is more involved: it requires changing object collision code to mark an object as being interacting with the character even when the 'standing on object' flag is unset, as long as the hitbox would overlap. Then, a check could be added to see if there is an object at the right place. Fixing this is probably not worth the effort.
- Object/Object Balancing Bug: When standing at the edge of an object, characters will swap to their balancing animation even if their other "foot" is on another solid/platform object. Fixing this would require something similar to the Terrain/Object Balancing Bug, but the second object would need an additional interaction slot with characters. Fixing this is probably not worth the effort.
After balancing, the function goes on to do a lot more things. Towards the end, it sets X and Y speed based on ground speed/inertia and current ground angle.
At this point, the zip anchor finally becomes relevant. If you are in a situation where the zip anchor exists, it is used at this point to check for "walls" along the way (it looks for ceilings and floors, in addition to walls, depending on your direction of motion). If a wall is found such that the zip anchor is inside a wall, your X or Y speed (as appropriate) is adjusted so that you will move to touch the wall. If you are moving mostly horizontally, your ground speed/inertia is set to zero. This is more colloquially known as bumping into the wall and stopping.
- Wall Grinding Bug: If you are moving mostly vertically and you run into a floor or ceiling, you will not lose your ground speed. You will stay grinding against the wall until you either slow down enough to fall (if moving up), or gain enough speed go through the floor (if moving down).
- Zipping Bug: This is the reason why I called it "zip anchor": this is the favorite bug of speedrunners in this game. Normally, when a character bumps into a wall, the distance inside the wall is subtracted (added) from (to) the speed, in pixels, depending on your direction of motion. Under normal circumstances, you will keep just enough speed to move until your collision box is touching the wall. If you are inside the wall, however, things get interesting: pressing a direction will give you a small amount of speed in that direction (6/256 pixels/frame for non-super characters in the normal games), and will enable the zip anchor. But since you are inside the wall, the sensor ray emitted by the zip anchor will say you are 32 pixels inside the wall (for reasons I am not 100% sure, sometimes it will give only 16 pixels in). So you will end up with a speed of (32 - 6/256) pixels/frame opposite to the direction you pressed. The issue is that the games do not check for overflows or underflows when computing the new speed.
- Vertical Zipping Bug: While a true vertical zip can only occur in S&K/S3&K (because the zip anchor will only appear in a perfectly vertical direction for those games), the conditions needed for a near vertical zip are achievable in S1 and S2 with only collision glitches (and perfectly vertical zips can be achieved in S&K/S3&K with the help of additional glitches).
- Non-Zipping Bug: S3/S&K/S3&K have a flag that disables zipping. This flag is set by the Angel Island 1 hollow tree, and by Flying Battery rotating cylinder meshes. In some circumstances, it is possible to glitch the game into not clearing this flag. You can then freely walk through walls.
- Moving Peel-Out Bug: In SCD you can perform a Moving Peel-Out by starting to charge a peel-out, pausing, holding down + forward, unpausing, quickly pausing, and switching to holding up again. This sets your inertia to the current Super Peel-Out charge, and you start moving at this speed. The Super Peel-Out charging mechanism is in the same function that handles zipping, and above it; and as long as you are charging the Super Peel-Out (pressing up or down + forward count), the function returns before reaching the wall check, allowing you to walk through walls.
- Moving Through Walls Platform Bug: Most moving platform and solid objects directly adjust the character's position, and perform no checks to see if you are being moved inside a wall. Since the zip anchor only exists when the character has nonzero speed, you can stand still on top or to the side of these objects and get dragged/pushed inside walls. The games generally add solid objects (usually, invisible) on walls to prevent this from happening.
- Exception: SCD does not suffer from this issue: its default solid/platform object routine manually performs a running wall check, using the object's X speed instead of the character's. The consequences of this check depend on whether the character is standing on the object, or to its sides: if the character is standing on top of the moving object, he/she will just be ejected out of the wall. Otherwise, the character is killed if he/she is even a single pixel inside the wall. Springs are a partial exception to this exception, and will not crush characters if they are on the air.
After the wall check, there is a rolling check, level bounds check, the character is finally moved according to X/Y speeds, and we arrive at the next part related to collision: adjusting the position and angle of the character so that he/she is not inside terrain.
If the character is standing on an object (the 'standing on object' flag is set), terrain will be ignored at this stage, and you keep whatever angle you had before.
- Slope Glitch: If you manage to despawn some solid or platform objects, they will not always clear the 'standing on object' flag. When you land on terrain, you get the angle from the spot you landed. Since the 'standing on object' flag prevents further updates of the angle, and your position is not also adjusted to keep you on the terrain, you will walk at whatever angle you have, even walking on air or through ground. The "correct" way of fixing the bug is to make sure all solid and platform objects properly clear the 'standing on object' flag on all cases. Another workable (partial) fix is to always clear this flag when a character lands, and move the location where the flag is set in the object collision routine to after the landing code.
- Moving Through Floors Platform Bug: Since being on a platform or solid object disables floor position adjustments, moving platform and solid objects can simply pull you through floors. Since the zip anchor only exists when the character has nonzero speed, you can stand still on top of these objects and get dragged inside walls and floor. The games are really bad at accounting for this case.
- Exceptions: SCD does not suffer from this issue: its default solid/platform object routine manually performs a floor check whenever the object is not moving up. Springs are an exception to this exception, and will not drop characters on the floor.
- In S2, the moving lava floors is also an exception: it checks if the characters should be dropped on the floor, but it is not a normal floor check: it just casts a single sensor ray from the bottom center of the collision box. The checks for both characters are done in the same function, which is called after the normal solid object routine, by the object itself.
- In S&K/S3&K, the falling mushrooms in Mushroom Hill, the elevators in Flying Battery, the tilting block bridge in Death Egg, all call an function identical to that of S2. The rising floor in Marble Garden, and the rising sand in Sandopolis, also call this function to drop each character. It loads the object player 1 is standing on (if any), calls the function, then loads the object player 2 is standing on (if any), and calls the function.
- In S3, the rising floor in Marble Garden does not work like in S3&K, so it is not an exception.
Otherwise, position and angle are adjusted by performing a walking ground check. This check will return 2 distances and 2 angles (1 of each per sensor ray). The games will pick the smaller distance (including sign, it is not absolute distance -- meaning the distance that is deeper inside terrain) and the associated angle. If the new angle is no more than 45 degrees from the old angle, it will be used; otherwise, 45 degrees will be added to the current angle, and it will be rounded to the nearest multiple of 90 degrees.
After this, if the character is less than 14 pixels inside the ground, he/she is ejected out.
- Angle Bug: This causes a bug that leaves the character lodged into the terrain: with the right speed, position, and sloped terrain, the character may end up more 14 pixels or more inside the terrain. This is typically incorrectly attributed to the angle not changing fast enough (and I believed this until I started writing this). Disabling this check fixes the issue, but I have no idea if it causes undesirable side effects, or why this check is there to begin with.
If the character is not touching terrain anymore (because of sloped terrain, or going off a ledge), and if either
max(abs(X or Y speed) + 4, 14) <= distance to ground
(where X speed is used for floor/ceiling, Y speed is used for walls) or the 'stick to convex' flag is set, then the character will be moved back to touching the ground.
- Tube Rejection Bug: When going through spin tunnels, you can sometimes be "rejected": a sudden loss of speed, sometimes to the point that you start going back from where you came. One of the reasons that this happens is that the character is moving fast enough to lose contact with the ground per the above equation. I believe that the zip anchor may also play a part in tube rejection, but I haven't been able to confirm this. I have no fix for this one, partial or otherwise, because I don't know yet all factors involved in causing it.
- Wheel Glitch: The Scrap Brain 2, Metallic Madness, and Carnival Night wheels/discs you can run around on have a huge "capture" area around them which sets the 'stick to convex' flag to true. If there is a solid or platform object inside this area, you can stand on it and walk off its edge to escape from the wheel's influence while keeping the 'stick to convex' flag set. While in this state, whenever you run off a ledge, you will still be considered to be standing on terrain, and you will "fall" 16 pixels/frame towards where the game thinks "ground" is for your current angle.
S&K/S3&K have additional floor, left and right wall checks after this. These checks are enabled only in some cases (moving wall in Hydrocity 2, during earthquakes and rising floors in Marble Garden 1 & 2, rising sands in Sandopolis 2). If the floor check detects that the character is inside a wall, the character is summarily executed; this check only casts a single ray from the bottom middle of the collision box. The left and right wall checks eject the character as normal.
- Moving Through Ceilings Platform Bug: Because there are no sensor rays being cast up when you are on the ground, moving platform and solid objects can push you inside terrain without any issue. The games generally add solid objects (usually, invisible) on walls to crush you in this case, but they do miss some cases.
- Exception: SCD does not suffer from this issue: its default solid/platform object routine manually performs a modified ceiling check whenever the object is moving up. Specifically, it casts a sensor ray from the top center of the collision box. The character is killed if he/she is even a single pixel inside the ceiling. Springs are an exception to this exception, and will not crush characters.
The aforementioned modified versions of Gens Re-Recording with collision and hitbox display.