Actions

Difference between revisions of "SPG:Solid Tiles"

From Sonic Retro

(Corrected "Reacquisition Of The Ground" and added degree angles)
m (Flagged Tiles)
 
(127 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 +
{{SPGPages}}
 
'''Notes:'''
 
'''Notes:'''
 
+
*''The research applies to all four of the [[Sega Mega Drive]] games and [[Sonic CD]]''.
Research applies to all four of the [[Sega Mega Drive|Genesis/Mega Drive]] games, and ''[[Sonic CD]]''.
+
*''The following only describes how the Player object collides and interacts with stage terrain. Solid objects, such as [[Monitor|Monitors]], Jump Through Platforms, and Pushable Blocks each have their own collision routines with the Player and don't necessarily behave exactly the same as the tiles do. For this, refer to [[SPG:Solid_Objects|Solid Objects]]''.
 
+
*''This page describes what Solid Tiles and Sensors are and how they work. For their use in player collision and movement, see [[SPG:Slope Collision|Slope Collision]] (part 1) and [[SPG:Slope Physics|Slope Physics]] (part 2)''.
The following only describes how [[Sonic the Hedgehog|Sonic]] collides and interacts with solid tiles. Solid objects, such as [[Monitor|Monitors]], Moving Platforms, and Blocks each have their own collision routines with Sonic, and don't necessarily behave exactly the same as the tiles do.
+
*''This page is essentially part 1 of 2. This details what Solid Tiles are and how they are used directly. For part 2, detailing how Solid Tiles and collision layers are used to create anything from simple slopes all the way to bigger Sonic set-pieces like loops, go to [[SPG:Solid Terrain|Solid Terrain]].''
 
 
As standard, angles are counter clockwise with 0 facing right.
 
  
 
==Introduction==
 
==Introduction==
  
What are solid tiles? While there are often solid objects in Sonic zones, the zone itself would require far too much object memory if the environment were contructed entirely of solid objects, each with their own 64 bytes of RAM. A clever shortcut is used - the zone is constructed out of tiles, anyway, so all that needs be done is have each tile know whether or not it is solid.
+
What are Solid Tiles? While there are often solid objects in Sonic zones, the zone itself would require far too much object memory if the environment were constructed entirely of solid objects, each with their own ''64'' bytes of RAM. A clever short-cut is used - the zone is constructed out of tiles anyway, so all that needs be done is have each tile know whether or not it is solid.
 
 
You may know that zones are broken down into 128x128 pixel chunks (or 256x256 pixel chunks in ''[[Sonic 1]]'' and ''[[Sonic CD]]''), which are in turn broken into 16x16 pixels tiles, which are again in turn broken into even smaller 8x8 pixel tiles. All of the solidity magic happens with the 16x16 tiles, so those are the only ones we'll be interested in throughout this guide.
 
 
 
It's Sonic's collisions and interactions with these solid tiles that makes up his basic engine. They dictate how he handles floors, walls, ceilings, slopes, and loops. Because this is such a large subject, and so complex, this guide is more proximate than other Sonic Physics Guides, but I've kept speculation to a minimum.
 
 
 
==Standing==
 
 
 
[[Image:SPGStanding.PNG|128px]]
 
 
 
Assuming the ground level to be at a Y position of $02E0, Sonic stands atop it at $02CC. That's 20 pixels above ground level.
 
 
 
==Pushing==
 
 
 
Sonic has to stop when he bumps into walls. This can be achieved by checking for a collision with a line. The sensor line shouldn't be at his Y position, but a little lower - otherwise it won't "notice" shorts steps. It can't be too low down, though, or it will "notice" ramps and curves, which Sonic shouldn't push against. Positioning it at Sonic's Y+4 is good, because there usually aren't stairs lower than 16 pixels high (when there are, they seem to be made out of solid objects, not tiles).
 
 
 
How wide should the sensor line be?
 
 
 
[[Image:SPGPushing.PNG|256px]]
 
 
 
Assuming the wall's left side to be at an X position of $02C0, Sonic can't get closer than $02B5. Assuming the wall's right side to be at an X position of $033F, Sonic can't get closer than $034A. That's 11 pixels difference, in either direction.
 
 
 
Thus the sensor line should be 20 pixels wide, stretching from Sonic's X-10 to X+10. Anytime it detects a solid tile, Sonic should be "popped out", set to the edge of the tile minus (or plus) 11, and his ground speed set to 0. (He can't be popped out by only 10, because then a point at his X+10 would still lie within the edge pixel of the tile. This would register a continuous collision, and he'd stick to the wall.)
 
 
 
Though the tile's edge minus Sonic's position might be 11, there are only 10 free pixels between Sonic's centre and the tile's edge. The eleventh pixel away is the tile's edge itself. So Sonic is effectively only 20 pixels wide.
 
 
 
==Falling Off==
 
 
 
Sonic also has to be able to run off of ledges. It wouldn't do to just keep walking like Wile E. Coyote, not noticing that there's nothing beneath him.
 
 
 
This means Sonic has to check to check for Solid tiles beneath him, as well. This can be achieved with two more sensor lines, pointing downward. One (A) should be on Sonic's left side, at X-9. The other (B) should be on his right, at X+9. They should start at his Y position and extend at least 16 pixels below his feet at ground level, which is at Y+20 (but not too far, or he'll pop down when descending low steps and stairs, which you don't want).
 
 
 
If both the A and B sensors detect no solid tiles, Sonic will "fall" - a flag will be set telling the engine he's now in the air.
 
 
 
You may remember that Sonic is 20 pixels wide when pushing into walls. But these ground sensors are only 18 pixels apart. So Sonic is skinnier by 2 pixels when running off of ledges than when bumping into walls.
 
 
 
==Balancing On Edges==
 
 
 
One nice touch is that Sonic goes into a balancing animation when near to the edge of a ledge. This only happens when he's stopped (his ground speed is 0).
 
 
 
How does the engine know? It's simple - any time only one of the ground sensors is activated, Sonic must be near a ledge. If A is active and B is not, the ledge is to his right. If vice versa, the ledge is to his left.
 
 
 
But if Sonic began balancing the instant one of the sensors found nothing, he'd do it too "early", and it would look silly. So it only happens when only one sensor is active, ''and'' his X position is greater than the edge of the solid tile found by the active sensor.
 
 
 
[[Image:SPGBalancing.PNG|384px]]
 
 
 
Assuming the right edge of the ledge to be $0A5F, Sonic will only start to balance at $0A60. He'll fall off at $0A69, when both sensors find nothing.
 
 
 
In ''[[Sonic 2]]'' and ''[[Sonic CD]]'', if the ledge is the opposite direction than he is facing, he has a second balancing animation.
 
 
 
In ''[[Sonic 2]]'', ''[[Sonic 3]]'', and ''[[Sonic & Knuckles]]'', Sonic has yet a ''third'' balancing animation, for when he's even further out on the ledge. Assuming the same values as above, this would start when he's at $0A66.
 
 
 
'''Note:''' While balancing, certain abilities are not allowed (ducking, looking up, spindashing, etc). In Sonic 3 & Knuckles, the player is still allowed to duck and spindash (but not to look up) when balancing on ground, but not when balancing on an object.
 
 
 
==Slopes And Curves==
 
 
 
''Sonic the Hedgehog'' was one of the first games to use curved surfaces and even entire 360 degree loops. Most other games of the era composed their environments entirely of blocks (and the occasional ramp).
 
 
 
The ability to cope with smoothly shaped environments is one of the fundamental aspects of the Sonic games' novelty and appeal. Unfortunately, it is also perhaps the most difficult and complex aspect to recreate in a fan game.
 
 
 
So how does it work?
 
 
 
===Height Masks===
 
 
 
Any time the A or B sensors find a solid tile, they return the height of that tile.
 
 
 
How is the height of the tile found?
 
 
 
Each tile has a value associated with it that references a mask stored in memory. Each mask is simply an array of 16 height values that range from $00 to $10, and an angle value.
 
 
 
[[Image:SPGHeightMask.PNG]]
 
 
 
This height mask, for example, has the height array $00 00 01 02 02 03 04 05 05 06 06 07 08 09 09 09, and the angle $E8.
 
 
 
Which value of the height array is used? Subtract the tile's X position from the sensor's X position. The result is the index of the height array to use.
 
 
 
If the height value found is $10, then the sensor has to check for another tile above the first one found, and search for that one's height value.
 
 
 
Whichever sensor finds the highest height, Sonic's Y is set to that height minus 20 pixels. His angle is also set to the angle of the solid tile that returned the highest height.
 
 
 
When no solid tile is found by a sensor, foot level (Y+20) is returned by default.
 
 
 
====Bugs Using This Method====
 
 
 
Unfortunately, there are a couple of annoying bugs in the original engine because of this method.
 
 
 
If Sonic stands on a slanted ledge, one sensor will find no tile, and return a height of foot level. This causes Sonic to be set to the wrong position.
 
 
 
[[Image:SPGSlopeBug1.PNG|384px]]
 
 
 
Sonic raises up with the B sensor as he moves right. When B drops off the ledge, Sonic defaults to the level of the A sensor. Then he raises up with the A sensor as he moves further right. So he'll move up, drop down, and move up again as he runs off the ledge.
 
  
There are few areas where this is noticeable, but it's true in all the Genesis/Mega Drive titles, and it's pretty tacky.
+
You may know that zones are broken down into ''128''x''128'' pixel chunks (or ''256''x''256'' pixel chunks in ''[[Sonic 1]]'' and ''[[Sonic CD]]''), which are in turn broken into ''16''x''16'' pixel Blocks, which are again in turn broken into even smaller 8x8 pixel tiles. All of the solidity magic happens with the ''16''x''16'' Blocks, so those are the only ones we will be interested in throughout this guide.  
  
A second form of it occurs when two opposing ramp tiles abut each other, as in some of the low hills in [[Green Hill Zone]] and [[Marble Zone]].
+
The Player object's collisions and interactions with these Solid Tiles are what make up their basic engine. They dictate how the Player handles floors, walls, ceilings, slopes, and loops.  
  
[[Image:SPGSlopeBug2.PNG|384px]]
+
First we will look at how the environment is constructed from tiles, and then the Player's method for detecting their environment.
  
The B sensor starts climbing down the ramp on the right, but Sonic still defaults to the level of the previous ramp found by the A sensor. Because these ramps are usually shallow, this only causes him to dip down in the middle by about 1 pixel.
+
==Solid Tiles==
 +
Solid Tiles are what define the terrain solidity in a stage. The shape of slopes, and the angles of them.
  
But that's not all. Because the highest sensor is the one Sonic gets the angle from, even though it looks like he should be considered to be at the angle of the ramp on the right (because he's closer to it), he'll still have the angle of the ramp on the left. When you jump, he'll jump at that angle, moving backward, not forward like you'd expect.
+
[[Image:SPGSolidTiles.png]] ''Solid Tiles covering the surfaces of Terrain, giving them solidity.''
  
===Moving At Angles===
+
===Block Grid===
 +
For the purposes of this guide, the "Solid Tiles" are the actual solidity data used to define the ground shape. But the grid structure they belong to are "Blocks". Each Sonic stage essentially contains a grid of ''16''x''16''px Blocks (grid cells) that fill a stage (or more specifically, fill each predefined terrain "Chunk"). Each of these Blocks references a Solid Tile (or empty Solid Tile) from a list of predefined Solid Tiles which contain height array data that contains the filled (solid) area/shape for that Block. Each Block can also define other properties for it's Solid Tile such as [[SPG:Solid_Tiles#Flipping_Tiles|flipping]] or [[SPG:Solid_Tiles#Flagged_Tiles|flagging]].
  
Well, that's all very well and good for having Sonic move smoothly over terrain with different heights. But's that's not all there is to the engine. Sonic's speed has to be attenuated by angled ground in order to be realistic.
+
===Height Array===
 +
Now for the Solid Tiles themselves. How is a Solid Tile set up? How is the shape of the terrain formatted?
  
There are two ways in which Sonic's speed is affected on angles. The first makes sure that he doesn't traverse a hill in the same amount of time as walking over flat ground of an equal width. The second slows him when going uphill, and speeds him up when going downhill. Let's look at each of these in turn.
+
Each Solid Tile is simply an array of ''16'' height values that can range from ''0'' to ''16'' pixels inclusive.
  
====The Three Speed Variables====
+
[[Image:SPGSolidTileHeightMasks.png]] ''Example of how many different height arrays are needed in order to account for every possible slope.''
  
If Sonic were a simple platformer that required nothing but blocks, you would only need two speed variables: X speed (''Xsp'') and Y speed (''Ysp''), the horizontal and vertical components of Sonic's velocity. Acceleration (''acc''), deceleration (''dec''), and friction (''frc'') are added to ''Xsp''; jump velocity (''jmp'') and gravity (''grv'') are added to ''Ysp'' (when Sonic is in the air).
+
Let's take a closer look at an example Solid Tile.
  
But when slopes are involved, while Sonic moves along a slope, he's moving both horizontally and vertically. This means that both ''Xsp'' and ''Ysp'' have a non-zero value. Simply adding ''acc'', ''dec'', or ''frc'' to ''Xsp'' no longer works; imagine Sonic was trying to run up a wall - adding to his horizontal speed would be useless, because he needs to move upward.
+
[[Image:SPGHeightMask.PNG|link=Special:FilePath/SPGHeightMask.PNG]]
  
The trick is to employ a third speed variable (as the original engine does), so let's call it Ground speed (''Gsp''). This is the speed of Sonic along the ground, disregarding the angle altogether. ''acc'', ''dec'', and ''frc'' are applied to ''Gsp'', not ''Xsp'' or ''Ysp''.
+
This tile's height array has the values (from left to right)
 +
<syntaxhighlight>0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9</syntaxhighlight>
 +
and has the angle ''33.75° (232)''.
  
While on the ground, ''Xsp'' and ''Ysp'' are derived from ''Gsp'' every step before Sonic is moved. Perhaps a pseudo-code example is in order:
+
Solid Tiles also have another height array (or, well, a ''width'' array) for horizontal collisions. This second array is present for all Solid Tiles, and will be used when any horizontal [[SPG:Solid_Tiles#Sensors|Sensor]] is checking the tile.
  
  Xsp = Gsp*cos(angle);
+
This tile's width array has the values (from top to bottom)
  Ysp = Gsp*-sin(angle);
+
<syntaxhighlight>0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 7, 9, 10, 11, 13, 14</syntaxhighlight>
 
+
This width array represents the same slope, angle, and creates the exact same shape within the tile.
  X += Xsp;
 
  Y += Ysp;
 
  
No matter what happens to the angle, ''Gsp'' is preserved, so the engine always knows what speed Sonic is "really" moving at.
+
===Angles===
 +
Solid Tiles contain a specific angle value which represents the angle of the slope of the surface that the Height Array creates.
  
====Slope Factor====
+
[[Image:SPGSolidTileAngles.png]] ''Tile angles (degrees)''
  
By this point, Sonic ought to be able to handle any hills with an accurate velocity. But he still needs to be slowed when going uphill, and sped up when up going downhill.
+
This angle is used when the Player collides with the Solid Tile and needs to react with slope physics and to allow the Player to rotate along the terrain.
  
Fortunately, this is simple to achieve - with something called the Slope Factor (''slp''). Just add ''slp''*sin(''angle'') to ''Gsp'' at the beginning of every step. The value of ''slp'' is always 0.125 ($0020) when running, but not so when rolling. When Sonic is rolling uphill (the sign of ''Gsp'' is '''not''' equal to the sign of sin(''angle'')), ''slp'' is 0.078125 ($001E). When Sonic is rolling downhill (the sign of ''Gsp'' '''is''' equal to the sign of sin(''angle'')), ''slp'' is 0.3125 ($0050).
+
====Flagged Tiles====
 +
[[Image:SPGFlaggedSolidTiles.png]]
  
'''Note:''' In Sonic 1, it appears that ''slp'' doesn't get added if Sonic is stopped, and in his standing/waiting cycle. But in Sonic 3 & Knuckles, ''slp'' seems to be added even then, so that Sonic can't stand on steep slopes - they'll force him to walk backward down them.
+
Nearly all tiles have a specific angle value which is used to allow the Player to rotate with sloped ground. However, some tiles angle is used as a flag. In the original games, if the angle value of the tile is ''255'', it is a flagged tile. How this angle is reacted to is determined by the object's code. In the case of the Player, they will not set their '''''Ground Angle''''' to the tile's angle as normal, but will instead find a new '''''Ground Angle''''' which is the Player's current '''''Ground Angle''''' snapped to the nearest right angle (0, 90, 180, or 270 degrees). Essentially, the calculation used for degrees would be:
 +
<syntaxhighlight>snapped_angle = (round(Ground Angle / 90) mod 4) * 90;</syntaxhighlight>
  
===Jumping At Angles===
+
This flag applies commonly to full Block Solid Tiles (filled squares), as it is a quick and easy way to allow the Player to walk on all sides of a full Block without needing to flip it around or provide multiple angle values for each side.
  
Jumping is also affected by the angle Sonic is at when he does it. He can't simply set ''Ysp'' to ''jmp'' - he needs to jump away from the angle he's standing on. Instead, both ''Xsp'' and ''Ysp'' must be set to ''jmp'', using cos() and sin() to get the right values.
+
This is also used as a trick to effectively "flatten" the end of ramps. The ramps found in [[Emerald Hill Zone]] end with a diagonal slope - yet Spindashing up it sends you flying directly upwards into the air. How? The trick is that the last tile on that ramp has an angle of ''255'', so is flagged despite not being a flat tile. This snaps the Player to ''90° (192)'' (aka, moving directly upwards) just as they leave the ramp.
  
More pseudo-code:
+
===Flipping Tiles===
 +
You may wonder how sloped ceilings are possible if the height array creates slopes along the tops of Solid Tiles. The answer to this is that Blocks can be flipped horizontally and/or vertically. The collision systems take this into account when reading the height data from that Block's tile.
  
  Xsp -= jmp*sin(angle);
+
The angles of flipped tiles are also adjusted to align correctly after being read.
  Ysp -= jmp*cos(angle);
 
  
==Switching Mode==
+
===Tile Solidity===
 +
Not all tiles behave the same. Some tiles are only solid from the top, some only from the sides and underneath, and some of course are solid from all directions.
  
So Sonic can run over hills and ramps and ledges, and all that's great. But it's ''still'' not enough. He can't make his way from the ground to walls and ceilings without more work.
+
Here are the colours for tile solidity that will be used on this guide:
 +
*<span style="color:#ffffff; font-weight: bold;">⬤</span> Fully Solid (detected by all sensors)
 +
*<span style="color:#0071eb; font-weight: bold;">⬤</span> Top Solid only (only detected by downward facing sensors)
 +
*<span style="color:#00ffa8; font-weight: bold;">⬤</span> Sides & Bottom Solid only (only detected by upward and horizontal sensors)
  
Why not? Well, because the A and B ground sensors check straight downward, finding the height of the ground. There's just no way they can handle the transition to walls when everything is built for moving straight up and down on the Y axis.
+
What are Sensors? Well, that's up next.
  
How can we solve this? By using four different modes of movement. This will take a little explaining.
+
==Sensors==
  
===The Four Modes===
+
"Sensors" are simply checks performed by objects which look for Solid Tiles around them.
  
It seems pretty reasonable to assume that, because Sonic can traverse ground in 360 degrees, the engine handles all 360 degrees in much the same way. But, in fact, the engine splits the angles into four quadrants, greatly simplifying things.
+
An x/y position ('''anchor point''') is checked, and if it finds a Solid Tile, they will gather information about the tile.
 +
Sensors can point down, right, up, or left, and all behave the same in their respective directions/axis.
  
To better understand what I'm talking about, imagine a simpler platformer without full loops, just a few low hills and ramps. All the character would need to do is, after moving horizontally, move up or down until they met the level of the floor. The angle of the floor would then be measured. The angle would be used to attenuate ''Gsp'', but nothing more. The character would still always move horizontally, and move straight up and down to adhere to floor level.
+
[[Image:SPGSensorAnchors.png]] ''The white points represent the '''anchor''' positions of the Player's Sensors.''
  
This is much like how Sonic does things. Only, when the angle gets too steep, Sonic switches "quadrant", moving from Floor mode to Right Wall mode (to Ceiling mode, to Left Wall mode, and back around to Floor mode, etc). At any one time, in any one mode, Sonic behaves like a simpler platformer. The magic happens by combining all four modes, and cleverly switching between them smoothly.
+
In this example, the Sensor to the Player's mid right points right, and those at the Player's feet point down. Their direction dictates in which direction they are looking for a surface. A Sensor pointing right is looking for the leftmost edge of solid terrain nearby, a Sensor pointing down is looking for the topmost edge of solid terrain nearby.
  
So how and when does Sonic switch mode?
+
The Sensor will read the height or width array of the Solid Tile in the Block it is checking.
 +
Which value of the array is used? For a vertical Sensor, subtract the tile's X position from the Sensor's X position. For a horizontal Sensor (using the width array), subtract the tile's Y position from the Sensor's Y position. The result is the index of the array to use.
  
When in Floor mode, and an angle steeper than $E0 is detected, the engine switches into Right Wall mode. Everything is basically the same, only the sensors check to the right instead of downward, and Sonic is moved to "floor" level horizontally instead of vertically.
+
So, now we know Sensors are points which look for Solid Tiles they touch, and that they can check the height or width array value of tiles at specific positions to get the floor surface position.  
  
Now that he's in Right Wall mode, if an angle shallower than $E0 is detected, the engine switches back into Floor mode.
+
However, this is not the whole picture. If a Sensor finds an empty Block or the array value of the tile found by the Sensor is 16 (a full Block amount), then it's likely that the surface of the solid terrain is actually found within an adjacent tile instead.
  
The other transitions work in exactly the same way.
+
====Sensor Regression & Extension====
 +
So when a Sensor check is performed at a Sensor's '''anchor point''' it has either found a Solid Tile in that Block, or the Block is empty. If it has, what if the height value found is ''16'' and isn't actually the surface of the terrain? Or if it hasn't, what if there's a Solid Tile nearby?
  
You might rightly ask where the ground sensors are when in Right Wall mode. They're in exactly the same place, only rotated 90 degrees. Sensor A is now at Sonic's Y+9 instead of X-9. Sensor B is now at Sonic's Y-9, instead of X+9. Instead of vertical sensor lines, they are now horizontal, stretching 16 pixels beyond his foot level (which is now 20 pixels "below" him, at X+20).
+
Well, this is easily solved by checking neighbouring tiles if certain conditions are met.  
  
Yes, because the sensors move so far, it is possible for Sonic to be "popped" out to a new position in the step in which he switches mode. However, this is hardly ever more than a few pixels and really isn't noticeable at all during normal play.
+
Note:
 +
*The following is an example in the case of a Sensor which is pointing down looking for solids below (like a floor Sensor while standing upright).
 +
*The current height or width array value of a tile at the Sensor's X/Y position will be referred to as the '''detected_height'''.
  
One more thing: I said that solid tiles were made of height arrays. Operative word: ''height''. How do they work when in Right Wall mode? Well, rather gobsmackingly, it turns out that in the original engine, each solid tile has ''two'' complementary height arrays, one used for when moving horizontally, the other for when moving vertically.
+
=====Normal=====
  
What about Left Wall and Ceiling mode? Wouldn't there need to be ''four'' height arrays? No, because tiles of those shapes simply use normal height arrays, just inverted. When in Ceiling mode, Sonic knows that the height value found should be used to move him down and not up.
+
When the '''anchor point''' finds a Solid Tile and the '''detected_height''' of the first tile is between ''1'' and ''15'' (inclusive), the game can be sure the surface of the terrain has been found without needing to check extra tiles at all.  
  
With these four modes, Sonic can go over all sorts of shapes. Inner curves, outer curves, you name them. Here are some example images with their angle values to help give you some idea of what I've been talking about:
+
Otherwise, one of the following two things will happen.
  
[[Image:SPGInnerCurve.PNG|256px]] [[Image:SPGInnerCurveChart.PNG|144px]]
+
=====Regression=====
  
[[Image:SPGOuterCurve.PNG|256px]] [[Image:SPGOuterCurveChart.PNG|144px]]
+
When the '''anchor point''' finds a Solid Tile and the '''detected_height''' of the first tile is 16 (meaning the tile is completely filled in that position), it will check up by one extra Solid Tile. We'll call this the "regression" since it goes back against the Sensor direction.
 +
 +
If a regression occurs and '''detected_height''' of the second tile is 0 (or the Block is empty), it will just default to processing the first tile, as the first tile ''must'' be the terrain surface.
  
===Falling Off Of Walls And Ceilings===
+
=====Extension=====
  
When in Right Wall, Left Wall, or Ceiling mode, Sonic will fall any time absolute ''Gsp'' falls below 2.5 ($0280) (''Gsp'' is set to 0 at this time, but ''Xsp'' and ''Ysp'' are unaffected, so Sonic will continue his trajectory through the air). This happens even if there is ground beneath him.
+
When the '''anchor point''' just finds an empty Block (or the '''detected_height''' of the first tile is ''0''), it will check down by 1 extra Solid Tile. We'll call this the  "extension" because it goes outwards, in the direction of the Sensor.
  
====Sliding Back Down====
+
If an extension occurs and just finds an empty second Block (or the '''detected_height''' of the second tile is ''0''), the game knows that no terrain or terrain surface has been found, and will return the distance to the end of the second tile checked.
  
When Sonic falls off in the manner described above, the [[SPG:Springs and Things#Horizontal Control Lock|horizontal control lock]] timer is set to $1E (it won't begin to count down until Sonic lands back on the ground). While this timer is non-zero and Sonic is on the ground, it prevents the player from adjusting Sonic's speed with the left or right buttons. The timer counts down by 1 every step, so the lock lasts about half a second. During this time only ''slp'' and the speed Sonic fell back on the ground with are in effect, so Sonic will slip back down the slope.
 
  
==The Air State==
+
If a Solid Tile was found to be processed, it will calculate the distance between that '''detected_height''' and the Sensor.
  
Any time Sonic is in the air, he doesn't have to worry about angles, ''Gsp'', ''slp'', or any of that jazz. All he has to do is move using ''Xsp'' and ''Ysp'' until he detects the ground, at which point he re-enters the ground state.
+
[[Image:SPGSensorDistance.gif]]
  
But Sonic does have an unusual set of collision sensors while moving through the air, and it's worth going over them in detail.
+
In the above example, a downward facing Sensor moves down through 3 Solid Tiles. We can see the tiles it checks, and the distance it returns at each position. We can also see if it is performing extension or regression. You can see this means the Sensor can effectively act as a line, it can regress or extend up to 32 pixels to find the nearest surface.
  
===Air Sensors===
+
The regression & extension will occur in the direction of the Sensor, be it horizontal or vertical. So a right facing Sensor's regression would check an extra Block to the left, and extension would check an extra Block to the right. While an upward facing Sensor's regression would check an extra Block below, and extension would check an extra Block above.
  
====Horizontal Sensor====
+
With all this, a Sensor can always locate the nearest open surface (and the tile containing that surface) of the terrain within a range of ''2'' Blocks (the Block the Sensor '''anchor point''' is touching plus another).
  
Sonic has to be able to bump into walls, just like when he's on the ground. So there's a horizontal sensor line, originating at his Y position, and stretching from X-10 to X+10. Anytime it's colliding with a solid tile, Sonic's "popped" out to the edge of the tile plus/minus 11, just like on the ground.
+
====Reaction====
 +
Once a final suitable tile has been found, information about the tile is returned.
  
The thing is, this sensor line is wider than his vertical A and B sensors that detect the ground (we'll be getting to those in a minute). This means it's possible for the horizontal sensor line to detect a block that's Sonic is falling past, even if he has no ''Xsp'' at all. This causes him to slip around solids when he hits them at their extreme edges.
+
The information a Sensor finds is as follows:
 +
*The distance from the Sensor pixel to the surface of the Solid Tile found (in the Sensor's direction)
 +
*The angle of the tile found
 +
*The tile ID
  
So when Sonic detects a collision with his horizontal sensor line, his ''Xsp'' is only set to 0 if he moving in the direction of the wall, not away from it. If it were otherwise, he'd stick to walls as he fell past them.
+
=====Distance=====
 +
The distance to the Solid Tile surface (found by the Sensor) is the most important piece of information dictating how an object will react to Solid Tiles. It's not the distance to the tile, it's the distance to the ''edge of the solid area within the tile'', precisely.
  
====Vertical Sensors====
+
The distance can either be ''0'', negative, or positive.
  
Sonic's A and B sensors are much the same in the air as they are on the ground. The difference is, when they detect a solid tile, Sonic isn't immediately set to the height found in the tile minus 20. Instead, he's only set to it if he's ''already'' lower than that height. Otherwise he'd suck to the floor when he got close to it.
+
When no Solid Tile is found by a Sensor, the Sensor will return a distance between 16 and 32 (inclusive), which is the distance to the end of the second tile checked. This is the potentially "no-tile-found" distance range.  
  
Since the sensors stretch so far below his feet, what stops them from detecting the ground again in the step in which he jumps, never letting him leave the floor? Simple. He ignores them unless ''Ysp'' is 0 or greater, so he'll only detect the ground while moving down.
+
*''Note: It doesn't necessarily mean no tile has been found if the distance is in this range, but if no tile was found the distance will definitely be within this range so the game typically chooses to treat it as such just in case.''
  
Because Sonic can move both up and down while in the air, there have to be two more sensors checking upward (C and D), so that he can bump into ceilings and curves that are above him. (C and D are perfect mirror images of A and B - they have the same X position and length, they just point up instead of down.) Sonic will detect ceilings and be pushed out of them whether he's moving up or down, unlike floors which are only detected when moving down. It ''is'' possible to hit a "ceiling" (which is just the bottom side of a block) while moving down - by pressing toward a wall with a gap in it, or jumping toward the side of an upper curve.
+
In future, if the guide references a tile not being found, this means either the distance is out of the range the object wants, or the distance is between 16 and 31.  
  
===Jumping "Through" Floors===
+
*A distance of 0 means the Sensor is just touching the Solid Tile surface and the object will not need to move (but may still choose to collide).
  
There are some ledges that Sonic can jump up "through". These are often in the hilly, green zones such as [[Green Hill Zone]], [[Emerald Hill Zone]], [[Palmtree Panic Zone]], and so on. The solid tiles that make up these ledges are flagged by the engine as being a certain type that should only be detected by Sonic's A and B sensors. They're totally ignored by C and D, as well as the horizontal sensor line.
+
*A negative distance means the surface found is closer to the object than the Sensor position. Negative distances are almost always reacted to when colliding, because it indicates that the object is inside the solid and can be pushed out.
  
===Reacquisition Of The Ground===
+
*Positive distances mean the surface found is further away from the object than the Sensor position. Since this means the object isn't actually touching the tile, it's rarely used - but not never. A notable example of it's use is by floor Sensors of various objects, including the Player, to keep them attached to the ground even if the ground has sloped away from them a bit as they move. Objects usually employ a limit to how far an object can be "pulled" down to a solid they aren't actually touching. This will be detailed further down for the Player.  
''Xsp'' and ''Ysp'' are derived from ''Gsp'' when Sonic is on the ground. When he falls or otherwise leaves the ground, ''Xsp'' and ''Ysp'' are already the proper values for him to continue his trajectory through the air. But when Sonic lands back on the ground, ''Gsp'' must be calculated from the ''Xsp'' and ''Ysp'' that he has when it happens.
 
You might think that they'd use cos() and sin() to get an accurate value, but not so. In fact, something much more basic happens, and it's different when hitting into a curved ceiling as opposed to landing on a curved floor, so I'll cover them separately.
 
  
====When Moving Downward====
+
If the object decides to snap itself to the terrain, it simply has to add the distance value to it's position. Or subtract, depending on the Sensor's direction. A Sensor pointing left may return a negative distance if it is inside a solid, but the object would have to subtract the distance in order to move to the right, out of it.
When the floor angle detected is in the range of $F0~$FF (and their mirror images, $00~$0F), ''Gsp'' is set to the value of ''Xsp''. In degrees, this angle range is effectively 0 to 22.5, and those mirrored.
 
  
When the angle is in the range of $E0~$EF ($10~$1F), ''Gsp'' is set to ''Xsp'' but only if absolute ''Xsp'' is greater than ''Ysp''. Otherwise, ''Gsp'' is set to ''Ysp''*0.5*-sign(sin(''angle'')). In degrees, this angle range is effectively 22.5 to 45, and those mirrored.
+
Of course, as stated, this distance can be representative of any 4 directions, depending on the Sensor's own angle.
  
When the angle is in the range of $C0~$DF ($20~$3F), ''Gsp'' is set ''Xsp'' but only if absolute ''Xsp'' is greater than ''Ysp''. Otherwise, ''Gsp'' is set to ''Ysp''*-sign(sin(''angle'')). In degrees, this angle range is effectively 45 to 90, and those mirrored.
+
====Typical Sensor Usage====
 +
Sensors are utilised by many different objects, including the Player, to align themselves or "collide" with Solid Tiles. A Sensor will typically be cast with an anchor positioned at the edge of the object, pointing outwards. For example, positioned at the right of an object at <code>'''''X Position''''' + '''''Width Radius'''''</code> pointing right, or positioned at the bottom of an object at <code>'''''Y Position''''' + '''''Height Radius'''''</code> pointing down.  
  
====When Moving Upward====
+
Most objects will only use 1 or 2 Sensors, usually pointing down looking for the floor, though some will also use additional Sensors to check for walls. The Player object is more complex and can sometimes use up to 5 Sensors at once depending on their state. How the Player uses Sensors is described in [[SPG:Slope_Collision#The_Player.27s_Sensors|Slope Collision]].
When the ceiling angle detected is in the range of $A0~BF ($40~$5F), Sonic reattaches to the ceiling, and ''Gsp'' is set to ''Ysp''*-sign(sin(''angle'')).
 
  
When the angle is in the range of $60~9F, Sonic hits his head like any ceiling, and doesn't reattach to it. ''Ysp'' is set to 0, and ''Xsp'' is unaffected.
+
In the example of a Sensor at the bottom, if terrain is found nearby the distance found from this Sensor can be used to reposition the object to align perfectly with the floor. For example if the Sensor returned a distance of ''-10'', the Sensor is 10 pixels inside of the Terrain, and the object would simply have to add ''-10'' to their '''''Y Position''''' to align perfectly with the surface.
 +
Objects typically won't react to positive distances where the Sensor is outside of the terrain (but not always, typically positive Sensor distances are used for alignment while an object is already on the ground to ensure they remain "stuck").  
  
==Reference: Converting Angles==
+
=====Distance Rejection=====
 +
Objects will reject distances too "large" in either direction (positive or negative).
  
The Genesis/Mega Drive games use angles in hex, $00 through $FF, meaning that there are only 256 divisions of a circle, not 360 like we're used to. Worse, the direction is anti-clockwise compared to other languages like GML, so $20 isn't 45° like it should be - it's 315°.
+
When casting Sensors, objects check the returned distances it accepts for collision. For example, checking if a distance is <code>>= 16</code> or <code><= -16</code> and if it is, not colliding. This will filter out potentially "no-tile-found" distances. This avoids a need for the game to remember if a tile was found after the Sensor was cast, it can just remember the distance value. Though typically the game opts for a distance of ''14'' as the limit (<code>< -14</code> or <code>> 14</code>) as this usually means either the surface of the Solid Tile is too far away to matter anyway.
  
In order to convert the original hex angles into angles you can use in GML, use this calculation (rendered here in pseudo-code):
+
====Visual Depiction====
 +
Sensors will be drawn from the Sensor anchor, extending towards the centre of the object. This demonstrates the direction of the Sensor, and also, given the typical use of Sensors, the approximate "solid" areas of the object using the Sensor.
  
  return (256-hex_angle)*1.40625;
+
You can imagine it vaguely like so - if the very edge pixels of the surface of the ground is within these lines, the object would collide and be pushed out of the Solid Tile. It is of course not quite this simple in reality. As shown in the previous visualisation of a Sensor, the "line" portion can extend up to 32 pixels in either direction, all depending on where the Sensor anchor currently sits within it's tile... and sometimes objects will align themselves even when the Sensor returns positive distances and aren't even touching the Solid Tile... and objects will reject all sorts of different varying distances Sensors return depending on what the Sensor is being used for... This would be impossible to accurately draw over the Player while keeping things understandable and clear. This visualisation is a simplified and rough estimation.
  
==Notes==
+
Just be aware that the line based depictions are for simple illustration purposes only and the endpoints of the lines are the active Sensor anchor positions (which always behave as described).
* Sonic can only brake ("screech to a halt") in Floor mode.
 
* Sonic can't jump when there is a low ceiling above him. If there is a collision detected with a sensor line stretching from Sonic's X-9 to X+9, at Y-25, Sonic won't bother jumping at all.
 
* Sonic has a different height at different times. When he's standing, running, falling, or springing from a springboard, he's 40 pixels tall. His Y position is always his centre, so that's why he stands 20 pixels above the ground (and 20 pixels below ceilings when he hits into them, etc). However, when he's jumping or rolling, he's only 30 pixels tall, and he sets 15 pixels above the ground (and 15 pixels below ceiling, etc). In the step in which Sonic rolls or jumps, the engine adds 5 to his Y position so that even though he gets shorter and his centre changes position, his bottom point will remain unchanged. 5 also has to be subtracted from Y when he unrolls, or lands from a jump. The camera system also has to keep this offset in mind, otherwise the view will jump when Sonic changes height.
 
* Sonic's A, B, C, and D sensors are described in this guide as being at X-9 and Y+9. This is only true when walking, falling, springing, and so on - any time he's not spinning. When Sonic is rolling or jumping, they are at X-7 and X+7. However, his horizontal sensor line remains the same whether curled up or not.
 
  
 
[[Category:Sonic Physics Guide]]
 
[[Category:Sonic Physics Guide]]

Latest revision as of 05:00, 17 June 2023

Sonic Physics Guide
Collision
Physics
Gameplay
Presentation
Special

Notes:

  • The research applies to all four of the Sega Mega Drive games and Sonic CD.
  • The following only describes how the Player object collides and interacts with stage terrain. Solid objects, such as Monitors, Jump Through Platforms, and Pushable Blocks each have their own collision routines with the Player and don't necessarily behave exactly the same as the tiles do. For this, refer to Solid Objects.
  • This page describes what Solid Tiles and Sensors are and how they work. For their use in player collision and movement, see Slope Collision (part 1) and Slope Physics (part 2).
  • This page is essentially part 1 of 2. This details what Solid Tiles are and how they are used directly. For part 2, detailing how Solid Tiles and collision layers are used to create anything from simple slopes all the way to bigger Sonic set-pieces like loops, go to Solid Terrain.

Introduction

What are Solid Tiles? While there are often solid objects in Sonic zones, the zone itself would require far too much object memory if the environment were constructed entirely of solid objects, each with their own 64 bytes of RAM. A clever short-cut is used - the zone is constructed out of tiles anyway, so all that needs be done is have each tile know whether or not it is solid.

You may know that zones are broken down into 128x128 pixel chunks (or 256x256 pixel chunks in Sonic 1 and Sonic CD), which are in turn broken into 16x16 pixel Blocks, which are again in turn broken into even smaller 8x8 pixel tiles. All of the solidity magic happens with the 16x16 Blocks, so those are the only ones we will be interested in throughout this guide.

The Player object's collisions and interactions with these Solid Tiles are what make up their basic engine. They dictate how the Player handles floors, walls, ceilings, slopes, and loops.

First we will look at how the environment is constructed from tiles, and then the Player's method for detecting their environment.

Solid Tiles

Solid Tiles are what define the terrain solidity in a stage. The shape of slopes, and the angles of them.

SPGSolidTiles.png Solid Tiles covering the surfaces of Terrain, giving them solidity.

Block Grid

For the purposes of this guide, the "Solid Tiles" are the actual solidity data used to define the ground shape. But the grid structure they belong to are "Blocks". Each Sonic stage essentially contains a grid of 16x16px Blocks (grid cells) that fill a stage (or more specifically, fill each predefined terrain "Chunk"). Each of these Blocks references a Solid Tile (or empty Solid Tile) from a list of predefined Solid Tiles which contain height array data that contains the filled (solid) area/shape for that Block. Each Block can also define other properties for it's Solid Tile such as flipping or flagging.

Height Array

Now for the Solid Tiles themselves. How is a Solid Tile set up? How is the shape of the terrain formatted?

Each Solid Tile is simply an array of 16 height values that can range from 0 to 16 pixels inclusive.

SPGSolidTileHeightMasks.png Example of how many different height arrays are needed in order to account for every possible slope.

Let's take a closer look at an example Solid Tile.

SPGHeightMask.PNG

This tile's height array has the values (from left to right)

0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9

and has the angle 33.75° (232).

Solid Tiles also have another height array (or, well, a width array) for horizontal collisions. This second array is present for all Solid Tiles, and will be used when any horizontal Sensor is checking the tile.

This tile's width array has the values (from top to bottom)

0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 7, 9, 10, 11, 13, 14

This width array represents the same slope, angle, and creates the exact same shape within the tile.

Angles

Solid Tiles contain a specific angle value which represents the angle of the slope of the surface that the Height Array creates.

SPGSolidTileAngles.png Tile angles (degrees)

This angle is used when the Player collides with the Solid Tile and needs to react with slope physics and to allow the Player to rotate along the terrain.

Flagged Tiles

SPGFlaggedSolidTiles.png

Nearly all tiles have a specific angle value which is used to allow the Player to rotate with sloped ground. However, some tiles angle is used as a flag. In the original games, if the angle value of the tile is 255, it is a flagged tile. How this angle is reacted to is determined by the object's code. In the case of the Player, they will not set their Ground Angle to the tile's angle as normal, but will instead find a new Ground Angle which is the Player's current Ground Angle snapped to the nearest right angle (0, 90, 180, or 270 degrees). Essentially, the calculation used for degrees would be:

snapped_angle = (round(Ground Angle / 90) mod 4) * 90;

This flag applies commonly to full Block Solid Tiles (filled squares), as it is a quick and easy way to allow the Player to walk on all sides of a full Block without needing to flip it around or provide multiple angle values for each side.

This is also used as a trick to effectively "flatten" the end of ramps. The ramps found in Emerald Hill Zone end with a diagonal slope - yet Spindashing up it sends you flying directly upwards into the air. How? The trick is that the last tile on that ramp has an angle of 255, so is flagged despite not being a flat tile. This snaps the Player to 90° (192) (aka, moving directly upwards) just as they leave the ramp.

Flipping Tiles

You may wonder how sloped ceilings are possible if the height array creates slopes along the tops of Solid Tiles. The answer to this is that Blocks can be flipped horizontally and/or vertically. The collision systems take this into account when reading the height data from that Block's tile.

The angles of flipped tiles are also adjusted to align correctly after being read.

Tile Solidity

Not all tiles behave the same. Some tiles are only solid from the top, some only from the sides and underneath, and some of course are solid from all directions.

Here are the colours for tile solidity that will be used on this guide:

  • Fully Solid (detected by all sensors)
  • Top Solid only (only detected by downward facing sensors)
  • Sides & Bottom Solid only (only detected by upward and horizontal sensors)

What are Sensors? Well, that's up next.

Sensors

"Sensors" are simply checks performed by objects which look for Solid Tiles around them.

An x/y position (anchor point) is checked, and if it finds a Solid Tile, they will gather information about the tile. Sensors can point down, right, up, or left, and all behave the same in their respective directions/axis.

SPGSensorAnchors.png The white points represent the anchor positions of the Player's Sensors.

In this example, the Sensor to the Player's mid right points right, and those at the Player's feet point down. Their direction dictates in which direction they are looking for a surface. A Sensor pointing right is looking for the leftmost edge of solid terrain nearby, a Sensor pointing down is looking for the topmost edge of solid terrain nearby.

The Sensor will read the height or width array of the Solid Tile in the Block it is checking. Which value of the array is used? For a vertical Sensor, subtract the tile's X position from the Sensor's X position. For a horizontal Sensor (using the width array), subtract the tile's Y position from the Sensor's Y position. The result is the index of the array to use.

So, now we know Sensors are points which look for Solid Tiles they touch, and that they can check the height or width array value of tiles at specific positions to get the floor surface position.

However, this is not the whole picture. If a Sensor finds an empty Block or the array value of the tile found by the Sensor is 16 (a full Block amount), then it's likely that the surface of the solid terrain is actually found within an adjacent tile instead.

Sensor Regression & Extension

So when a Sensor check is performed at a Sensor's anchor point it has either found a Solid Tile in that Block, or the Block is empty. If it has, what if the height value found is 16 and isn't actually the surface of the terrain? Or if it hasn't, what if there's a Solid Tile nearby?

Well, this is easily solved by checking neighbouring tiles if certain conditions are met.

Note:

  • The following is an example in the case of a Sensor which is pointing down looking for solids below (like a floor Sensor while standing upright).
  • The current height or width array value of a tile at the Sensor's X/Y position will be referred to as the detected_height.
Normal

When the anchor point finds a Solid Tile and the detected_height of the first tile is between 1 and 15 (inclusive), the game can be sure the surface of the terrain has been found without needing to check extra tiles at all.

Otherwise, one of the following two things will happen.

Regression

When the anchor point finds a Solid Tile and the detected_height of the first tile is 16 (meaning the tile is completely filled in that position), it will check up by one extra Solid Tile. We'll call this the "regression" since it goes back against the Sensor direction.

If a regression occurs and detected_height of the second tile is 0 (or the Block is empty), it will just default to processing the first tile, as the first tile must be the terrain surface.

Extension

When the anchor point just finds an empty Block (or the detected_height of the first tile is 0), it will check down by 1 extra Solid Tile. We'll call this the "extension" because it goes outwards, in the direction of the Sensor.

If an extension occurs and just finds an empty second Block (or the detected_height of the second tile is 0), the game knows that no terrain or terrain surface has been found, and will return the distance to the end of the second tile checked.


If a Solid Tile was found to be processed, it will calculate the distance between that detected_height and the Sensor.

SPGSensorDistance.gif

In the above example, a downward facing Sensor moves down through 3 Solid Tiles. We can see the tiles it checks, and the distance it returns at each position. We can also see if it is performing extension or regression. You can see this means the Sensor can effectively act as a line, it can regress or extend up to 32 pixels to find the nearest surface.

The regression & extension will occur in the direction of the Sensor, be it horizontal or vertical. So a right facing Sensor's regression would check an extra Block to the left, and extension would check an extra Block to the right. While an upward facing Sensor's regression would check an extra Block below, and extension would check an extra Block above.

With all this, a Sensor can always locate the nearest open surface (and the tile containing that surface) of the terrain within a range of 2 Blocks (the Block the Sensor anchor point is touching plus another).

Reaction

Once a final suitable tile has been found, information about the tile is returned.

The information a Sensor finds is as follows:

  • The distance from the Sensor pixel to the surface of the Solid Tile found (in the Sensor's direction)
  • The angle of the tile found
  • The tile ID
Distance

The distance to the Solid Tile surface (found by the Sensor) is the most important piece of information dictating how an object will react to Solid Tiles. It's not the distance to the tile, it's the distance to the edge of the solid area within the tile, precisely.

The distance can either be 0, negative, or positive.

When no Solid Tile is found by a Sensor, the Sensor will return a distance between 16 and 32 (inclusive), which is the distance to the end of the second tile checked. This is the potentially "no-tile-found" distance range.

  • Note: It doesn't necessarily mean no tile has been found if the distance is in this range, but if no tile was found the distance will definitely be within this range so the game typically chooses to treat it as such just in case.

In future, if the guide references a tile not being found, this means either the distance is out of the range the object wants, or the distance is between 16 and 31.

  • A distance of 0 means the Sensor is just touching the Solid Tile surface and the object will not need to move (but may still choose to collide).
  • A negative distance means the surface found is closer to the object than the Sensor position. Negative distances are almost always reacted to when colliding, because it indicates that the object is inside the solid and can be pushed out.
  • Positive distances mean the surface found is further away from the object than the Sensor position. Since this means the object isn't actually touching the tile, it's rarely used - but not never. A notable example of it's use is by floor Sensors of various objects, including the Player, to keep them attached to the ground even if the ground has sloped away from them a bit as they move. Objects usually employ a limit to how far an object can be "pulled" down to a solid they aren't actually touching. This will be detailed further down for the Player.

If the object decides to snap itself to the terrain, it simply has to add the distance value to it's position. Or subtract, depending on the Sensor's direction. A Sensor pointing left may return a negative distance if it is inside a solid, but the object would have to subtract the distance in order to move to the right, out of it.

Of course, as stated, this distance can be representative of any 4 directions, depending on the Sensor's own angle.

Typical Sensor Usage

Sensors are utilised by many different objects, including the Player, to align themselves or "collide" with Solid Tiles. A Sensor will typically be cast with an anchor positioned at the edge of the object, pointing outwards. For example, positioned at the right of an object at X Position + Width Radius pointing right, or positioned at the bottom of an object at Y Position + Height Radius pointing down.

Most objects will only use 1 or 2 Sensors, usually pointing down looking for the floor, though some will also use additional Sensors to check for walls. The Player object is more complex and can sometimes use up to 5 Sensors at once depending on their state. How the Player uses Sensors is described in Slope Collision.

In the example of a Sensor at the bottom, if terrain is found nearby the distance found from this Sensor can be used to reposition the object to align perfectly with the floor. For example if the Sensor returned a distance of -10, the Sensor is 10 pixels inside of the Terrain, and the object would simply have to add -10 to their Y Position to align perfectly with the surface. Objects typically won't react to positive distances where the Sensor is outside of the terrain (but not always, typically positive Sensor distances are used for alignment while an object is already on the ground to ensure they remain "stuck").

Distance Rejection

Objects will reject distances too "large" in either direction (positive or negative).

When casting Sensors, objects check the returned distances it accepts for collision. For example, checking if a distance is >= 16 or <= -16 and if it is, not colliding. This will filter out potentially "no-tile-found" distances. This avoids a need for the game to remember if a tile was found after the Sensor was cast, it can just remember the distance value. Though typically the game opts for a distance of 14 as the limit (< -14 or > 14) as this usually means either the surface of the Solid Tile is too far away to matter anyway.

Visual Depiction

Sensors will be drawn from the Sensor anchor, extending towards the centre of the object. This demonstrates the direction of the Sensor, and also, given the typical use of Sensors, the approximate "solid" areas of the object using the Sensor.

You can imagine it vaguely like so - if the very edge pixels of the surface of the ground is within these lines, the object would collide and be pushed out of the Solid Tile. It is of course not quite this simple in reality. As shown in the previous visualisation of a Sensor, the "line" portion can extend up to 32 pixels in either direction, all depending on where the Sensor anchor currently sits within it's tile... and sometimes objects will align themselves even when the Sensor returns positive distances and aren't even touching the Solid Tile... and objects will reject all sorts of different varying distances Sensors return depending on what the Sensor is being used for... This would be impossible to accurately draw over the Player while keeping things understandable and clear. This visualisation is a simplified and rough estimation.

Just be aware that the line based depictions are for simple illustration purposes only and the endpoints of the lines are the active Sensor anchor positions (which always behave as described).