Actions

Difference between revisions of "SPG:Solid Tiles"

From Sonic Retro

(New info regarding what sensors return when no tile is found (is also a large correction, as previously was stated to be 0 which is incorrect))
m (Flagged Tiles)
 
(23 intermediate revisions by the same user not shown)
Line 2: Line 2:
 
'''Notes:'''
 
'''Notes:'''
 
*''The research applies to all four of the [[Sega Mega Drive]] games and [[Sonic CD]]''.
 
*''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 [[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]]''.
*''The following only describes how the Player object collides and interacts with stage terrain. Solid objects, such as [[Monitor|Monitors]], Moving Platforms, and 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)''.
 +
*''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]].''
  
 
==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 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.
+
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.  
+
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.  
  
the Player's collisions and interactions with these solid tiles are what make up their basic engine. They dictate how they handles floors, walls, ceilings, slopes, and loops.  
+
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.
 
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==
 +
Solid Tiles are what define the terrain solidity in a stage. The shape of slopes, and the angles of them.
  
Solid tiles are a grid of data blocks, which represent solid areas within each grid cell. This area is defined using height masks.
+
[[Image:SPGSolidTiles.png]] ''Solid Tiles covering the surfaces of Terrain, giving them solidity.''
  
===Height Masks===
+
===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]].
  
When checking a solid tile, how is the height of the tile found?
+
===Height Array===
 +
Now for the Solid Tiles themselves. How is a Solid Tile set up? How is the shape of the terrain formatted?
  
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 0px ($00) to 16px ($10) and an angle value.
+
Each Solid Tile is simply an array of ''16'' height values that can range from ''0'' to ''16'' pixels inclusive.
  
[[Image:SPGHeightMask.PNG|link=Special:FilePath/SPGHeightMask.PNG]]
+
[[Image:SPGSolidTileHeightMasks.png]] ''Example of how many different height arrays are needed in order to account for every possible slope.''
  
This height mask, for example, has the height array 0 0 1 2 2 3 4 5 5 6 6 7 8 9 9 9, and the angle 33.75° ($E8).
+
Let's take a closer look at an example Solid Tile.
  
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.
+
[[Image:SPGHeightMask.PNG|link=Special:FilePath/SPGHeightMask.PNG]]
  
If the height value found is 16px ($10), that's the entire tile filled at that X position, so then the sensor has to check for another tile above the first one found, and search for that one's height value.
+
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)''.
  
====Horizontal Axis====
+
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.
Solid Tiles also have another height array (or, well, a width array) for horizontal collisions. This other array represents the same data and creates the exact same shape within the tile. This is only possible because the shapes represented in tiles are usually smooth continuous slopes, which don't extend in one direction then regress back. but either continue sloping in the same direction or stop.
 
  
====Flipping Tiles====
+
This tile's width array has the values (from top to bottom)
You may rightly wonder hows sloped ceilings are possible if the height array starts at one end only. The answer to this is that tiles can be flipped horizontally or vertically. The collision systems take this into account when reading the height data from tiles.
+
<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.
  
==Sensors==
+
===Angles===
 +
Solid Tiles contain a specific angle value which represents the angle of the slope of the surface that the Height Array creates.
  
"Sensors" are simply checks performed by objects which look for solid tiles around them.  
+
[[Image:SPGSolidTileAngles.png]] ''Tile angles (degrees)''
  
An x/y position ('''anchor point''') is checked, and if it finds a solid tile, they will gather information about the tile.
+
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.
Sensors can point down, right, up, and left, and all behave the same in their respective directions.
 
  
[[Image:SPGSensorAnchors.png]] ''The white points represent the '''anchor''' positions of the Player's sensors.''
+
====Flagged Tiles====
 +
[[Image:SPGFlaggedSolidTiles.png]]
  
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.
+
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>
  
So, we know they are points which look for solid tiles they touch. However, this is not the whole picture. If a sensor finds an empty tile 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.
+
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.  
  
====Sensor Regression & Extension====
+
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.
So when a sensor check is performed at a sensor's '''anchor point''' it has either found a solid tile, or it hasn't. 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.  
+
===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.
  
Note:
+
===Tile Solidity===
*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).
+
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.  
*The current height array value of a tile at the sensor's X position will be referred to as the ''tile height''.
 
  
 +
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)
  
'''Normal:'''
+
What are Sensors? Well, that's up next.
  
When the '''anchor point''' finds a Solid Tile and the ''tile 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.
+
==Sensors==
  
Otherwise, one of the following 2 things will happen.
+
"Sensors" are simply checks performed by objects which look for Solid Tiles around them.  
  
<div class="large-12 columns" style="padding:0px">
+
An x/y position ('''anchor point''') is checked, and if it finds a Solid Tile, they will gather information about the tile.
<div class="large-6 columns" style="padding:0px">
+
Sensors can point down, right, up, or left, and all behave the same in their respective directions/axis.
'''Regression:'''
 
  
When the '''anchor point''' finds a Solid Tile and the ''tile height'' of the first tile is 16 (meaning the tile is completely filled in that position), it will check up by 1 extra Solid Tile. We'll call this the "regression" since it goes back against the sensor direction.
+
[[Image:SPGSensorAnchors.png]] ''The white points represent the '''anchor''' positions of the Player's Sensors.''
 
If a regression occurs and ''tile height'' of the second tile is 0 (or the tile is empty), it will just default to processing the first tile, as the first tile ''must'' be the terrain surface.
 
</div>
 
<div class="large-6 columns">
 
'''Extension:'''
 
  
When the '''anchor point''' just finds an empty tile (or the ''tile 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.
+
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.
  
If an extension occurs and just finds an empty second tile (or the ''tile 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.  
+
The Sensor will read the height or width array of the Solid Tile in the Block it is checking.
</div>
+
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.
</div>
 
<hr>
 
If a solid tile was found to be processed, it will calculate the distance between that ''tile height'' and the sensor.
 
  
[[Image:SPGSensorDistance.gif]]
+
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.  
  
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.
+
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 regression & extension will occur in the direction of the sensor, be it horizontal or vertical. If the sensor is horizontal, it reads the other height array belonging to the tile, using the sensor's y position. Essentially rotating the entire setup.
+
====Sensor Regression & Extension====
So a right facing sensor's regression would check an extra tile to the left, and extension would check an extra tile to the right. While an upward facing sensor's regression would check an extra tile below, and extension would check an extra tile above.
+
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?
  
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 tiles (the tile the sensor '''anchor point''' is touching plus another).
+
Well, this is easily solved by checking neighbouring tiles if certain conditions are met.  
  
====Reaction====
+
Note:
Once a final suitable tile has been found, information about the tile is returned.
+
*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'''.
  
The information a sensor finds is as follows:
+
=====Normal=====
*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=====
+
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.  
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.  
+
Otherwise, one of the following two things will happen.
  
When no Solid Tile is found by a sensor, the sensor will return a distance between 16 and 32, which is the distance to the end of the second tile checked.
+
=====Regression=====
  
*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).
+
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.
  
*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.
+
=====Extension=====
  
*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. As stated above when describing extension, a distance between 16 and 31 can indicate no tile was found. The game will limit the distances it accepts for collision, which will automatically cancel collision on these distances.
+
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 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.
+
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.  
  
Of course, as stated, this distance can be representative of any 4 directions, depending on the sensor's own angle.
 
  
====Summary====
+
If a Solid Tile was found to be processed, it will calculate the distance between that '''detected_height''' and the Sensor.
Here's a demonstrative animation showing a very simplified process of how the floor sensors detect a tile and be moved upwards. In this case, the Player will have a Ground Speed of 6.
 
  
[[Image:SPGSensorProcess.gif]]
+
[[Image:SPGSensorDistance.gif]]
  
====Visual Depiction====
+
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.
Throughout this guide these sensors will be drawn as lines. But, they are not. Or well, they are, but not quite as shown ahead.
 
  
Sensors will be drawn from the sensor anchor, extending towards the centre of the object. You can imagine it like so - if the exact surface pixels of the ground is within these lines, the Player will be pushed out. 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... This would be impossible to accurately draw over the Player while keeping things understandable and clear. This visualisation is the most easy to visualise way to think about the solidity on a surface level.
+
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.  
  
Just be aware that the line based depictions are for simple illustration purposes only and the endpoints of the lines are the active sensor anchors (which always behave as described).
+
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).
  
==The Player's Sensors==
+
====Reaction====
 +
Once a final suitable tile has been found, information about the tile is returned.
  
Like any object which wants to collide with tiles, sensors surround the Player.
+
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
  
[[Image:SPGSensors.png|link=Special:FilePath/SPGSensors.png]]
+
=====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.
  
  <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> - Floor collision
+
The distance can either be ''0'', negative, or positive.
  <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> - Ceiling collision (only used mid-air)
 
  <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> - Wall collision (shifting by 8px depending on certain factors, which will be explained)
 
  XY - the Player's X Position and Y Position
 
  
 +
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 Player's collision setup is symmetrical, it makes sense for the game to set up widths and heights using radius values. The Player has separate radius values for their <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> sensor pair (their '''Push Radius''') which always remains the same,  and for their <span style="color:#00f000; font-weight: bold;">A</span>, <span style="color:#38ffa2; font-weight: bold;">B</span>, <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> sensors there is a Width Radius and Height Radius both of which will change depending on the Player's state. For these sizes see [[SPG:Characters|Characters]].
+
*''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.''
  
Note on sprite alignment:
+
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.  
* The Player's sprite is 1 pixel offset to the left when they faces left, which can result in them appearing to be 1px inside a tile when pushing leftwards. Amusingly, this offset will appear corrected when pushing most objects thanks to their hitboxes sticking out 1px further on their right and bottom (due to their origins being off-centre by 1 in X and Y). So while tiles are collided with accuracy, it will appear the opposite in-game. More about object collision in [[SPG:Solid_Objects|Solid Objects]].
 
  
 +
*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).
  
===Sensor Activation===
+
*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.
  
Before jumping into exactly where these sensors are how these sensors make Sonic react to the floor, it's best to know when they are actually being used.
+
*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.  
  
====While Grounded====
+
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.
  
Floor Sensors <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> are always active while grounded, and will actively search for new floor below the Player's feet.  
+
Of course, as stated, this distance can be representative of any 4 directions, depending on the Sensor's own angle.
  
When grounded, Wall Sensors <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> only activate when the Player is walking in that direction. For example, while standing still the Player isn't checking with their wall sensors at all, but while Ground Speed is positive, the Player's <span style="color:#ff38ff; font-weight: bold;">E</span> sensor is inactive, and while Ground Speed is negative, the Player's <span style="color:#ff5454; font-weight: bold;">F</span> sensor is inactive.
+
====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.  
  
However this is not always the case, both wall sensors simply don't appear when the Player's Ground Angle is outside of a 0 to 90 and 270 to 360 (or simply -90 to 90) degree range, meaning when you running around a loop, the wall sensors will vanish for the top half of the loop. In S3K however these sensors will also appear when the Player's Ground Angle is a multiple of 90 in addition to the angle range.
+
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]].
  
While grounded Ceiling Sensors <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> are never active, and the Player won't check for collision with solid tiles above them while on the floor.
+
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").  
  
====While Airborne====
+
=====Distance Rejection=====
 +
Objects will reject distances too "large" in either direction (positive or negative).
  
While in the air, all sensors play a part to find ground to reattach the Player to. But rather than have all active at once and risk lag, only 4-5 will be active at any given time.
+
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.
  
As you move, the game will check the angle of your motion (X Speed and Y Speed) through the air. It will then pick a quadrant by rounding to the nearest 90 degrees. (this is different to the Mode, this is simply a measurement of if you are going mostly left, right up or down). The quadrant can be more easily found by simply comparing the X Speed and Y Speed and finding which is larger or smaller.
+
====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.
  if absolute X Speed is larger then or equal to absolute Y Speed then
 
    if X Speed is larger than 0 then
 
      the Player is going mostly right
 
    else
 
      the Player is going mostly left
 
  else
 
    if Y Speed is larger than 0 then
 
      the Player is going mostly down
 
    else
 
      the Player is going mostly up
 
 
 
Depending on the quadrant, different sensors will be active.
 
 
 
When going '''mostly right''', the Player's <span style="color:#ff5454; font-weight: bold;">F</span> sensor will be active, along with both <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> floor sensors and the <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> ceiling sensors.
 
 
 
When going '''mostly left''', it is the exact same as going right, but the <span style="color:#ff38ff; font-weight: bold;">E</span> wall sensor instead.
 
 
 
When going '''mostly up''', both the <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> ceiling sensors and the <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> wall sensors are active.
 
 
 
When going '''mostly down''', it is the same as going up, but the <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> floor sensors are active instead of the ceiling sensors.
 
 
 
 
 
 
 
=== Floor Sensors (<span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span>) ===
 
 
 
[[Image:SPGStandingAnimated.gif|link=Special:FilePath/SPGStandingAnimated.gif]]
 
 
 
<span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> sit at their feet at Y Position + Height Radius.
 
 
 
====A and B Movement====
 
These sensors are <span style="color:#00f000; font-weight: bold;">A</span> on the Player's left side, at X Position - Width Radius, Y Position + Y Radius. While <span style="color:#38ffa2; font-weight: bold;">B</span> should be on their right, at X Position + Width Radius, Y Position + Height Radius.
 
 
 
These radius values change depending on the character and action (see [[SPG:Characters|Characters]]).
 
 
 
====A and B Method====
 
 
 
Floor sensors are a special case, there are 2 sensors and they need to detect slopes. Both sensors behave the same and search for a Solid Tile.  The smaller distance is the sensor that wins. For example, -10 is a smaller distance than 5. The sensor that wins is the distance and angle used (and it's found tile is the one referenced).
 
 
 
Once the winning distance is found, it can be used to reposition the Player. The result is the Player will stand atop the floor at the floor's surface Y level - (the Player's Height Radius + 1)
 
 
 
======A and B Distance Limits======
 
As we know, sensors return a distance to the nearest surface, up to an extreme maximum of 32 pixels. If the Player's floor sensors are within 32 pixels of the floor, the game may know the floor is there but we might not just want them to snap down right away. The game will test the distance found and react appropriately.
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
'''While grounded:'''
 
 
 
In Sonic 1, if the distance value is less than -14 or greater than 14, the Player won't collide.
 
In Sonic 2 onward however the positive limit depends on the Player's current speed - in this case, (for when the Player is on the floor) if the distance is greater than
 
 
 
  minimum(absolute(X Speed)+4, 14)
 
 
 
then they won't collide. So the faster the Player moves, the greater the distance the Player can be from the floor while still being pulled back down to it.
 
The -14 limit remains the same.
 
 
 
If the Player was in a sideways mode, such as on a wall, it would use Y Speed instead.
 
</div>
 
<div class="large-6 columns">
 
'''While airborne:'''
 
 
 
While airborne, if the winning distance value is greater than or equal 0 (meaning the sensor isn't overlapping the floor yet) the Player won't collide.
 
 
 
If still colliding after that check, the game will then check the following:
 
 
 
When moving '''mostly down''', both sensor's distances are larger than or equal to '''-(YSpeed + 8)'''. If either of them are, the player will collide, if neither of them are, the Player will not collide.  This behaviour is most noticeable with jump through (top solid) tiles when jumping from underneath.
 
 
 
When moving '''mostly left''' or '''mostly right''', the Player will collide if the Player's Y Speed is greater than or equal to 0.
 
</div>
 
</div>
 
 
 
=====Ledges=====
 
 
 
The Player has to be able to run off of ledges. It would not do to just keep walking like Wile E. Coyote, not noticing that there is nothing beneath them.
 
 
 
If both sensor <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> detect no solid tiles, the Player will "fall" - a flag will be set telling the engine they is now in the air.
 
 
 
=====Balancing On Edges=====
 
 
 
One nice touch is that the Player goes into a balancing animation when near to the edge of a ledge. This only happens when they is stopped (their Ground Speed is 0).
 
 
 
How does the engine know? It is simple - any time only one of the ground sensors is activated, the Player must be near a ledge. If <span style="color:#00f000; font-weight: bold;">A</span> is active and <span style="color:#38ffa2; font-weight: bold;">B</span> is not the ledge is to their right and vice versa.
 
 
 
But if the Player began balancing the instant one of the sensors found nothing, they would do it too "early", and it would look silly. So it only happens when only one sensor is active, and X Position is greater than the edge of the solid tile. This is checked with an extra downward sensor at the Player's X Position and Y Position + Height Radius. All this extra sensor does is check if there is floor at their X Position, is it not related to collision or physics.
 
 
 
[[Image:SPGBalancingAnimated.gif|link=Special:FilePath/SPGBalancingAnimated.gif]]
 
 
 
Assuming the right edge of the ledge to be an X position of 2655 ($0A5F), the Player will only start to balance at an X Position of 2656 ($0A60) (edge pixel+1). they'll fall off at an X Position of 2665 ($0A69) (edge pixel+10) when both sensors find nothing.
 
 
 
In ''[[Sonic 2]]'' and ''[[Sonic CD]]'', if the ledge is the opposite direction than they is facing, they has a second balancing animation.
 
 
 
In ''[[Sonic 2]]'', ''[[Sonic 3]]'', and ''[[Sonic & Knuckles]]'', the Player has yet a ''third'' balancing animation, for when they are even further out on the ledge. Assuming the same values as above, this would start when they is at an X Position of 2662 ($0A66).
 
 
 
'''Note:''' While balancing, certain abilities are not allowed (ducking, looking up, spindash, etc). In the Player 3 & Knuckles, the player is still allowed to duck and spindash (not to look up, though) when balancing on the ground but not when balancing on an object.
 
 
 
=== Ceiling Sensors (<span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span>) ===
 
 
 
[[Image:SPGHitCeiling.gif|link=Special:FilePath/SPGHitCeiling.gif]]
 
 
 
the Player's <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> sensors are always an exact mirror image of the Player's floor sensors, they have the same X positions but are flipped upside down and face upwards. They perform in the exact same way, competing against eachother, simply up instead of down.
 
 
 
However, they aren't active at the same times as the floor sensors, only while airborne.
 
 
 
====C and D Method====
 
 
 
When these sensors find a ceiling, much like the floor sensors the sensor which finds the smallest distance will win.  The sensor that wins is the distance and angle used (and it's found tile is the one referenced).
 
This winning distance can then be subtracted from the Player's position.
 
 
 
=====C and D Distance Limits=====
 
Distance limits here work in the same way as the floor sensors while airborne.
 
 
 
 
 
=== Wall Sensors (<span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span>) ===
 
 
 
[[Image:SPGPushingAnimated.gif|link=Special:FilePath/SPGPushingAnimated.gif]]
 
 
 
<span style="color:#ff38ff; font-weight: bold;">E</span> sits at their left at X Position-'''Push Radius''', while <span style="color:#ff5454; font-weight: bold;">F</span> sits at their right at X Position+'''Push Radius'''.
 
 
 
====E and F Movement====
 
 
 
'''Push Radius''' is always 10, placing <span style="color:#ff38ff; font-weight: bold;">E</span> to the Player's left side, at X Position - 10. While <span style="color:#ff5454; font-weight: bold;">F</span> is to their right, at X Position + 10, giving the Player a total width of 21 pixels when pushing.
 
 
 
Sensors <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> Spend most of their time at the Player's Y Position however while the Player's Ground Angle is 0 (on totally flat ground) both wall sensors will move to their Y Position + 8 so that they can push against low steps and not just snap up ontop of them.
 
 
 
The horizontal sensors are always positioned at Y Position while airborne.
 
 
 
You may remember that sensors <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> are only 19 pixels apart but the Player is 21 pixels wide when pushing into walls. This means that the Player is skinnier by 2 pixels when running off of ledges than when bumping into walls.
 
 
 
That's not to say these sensors don't move though. They do, and a lot.
 
As noted in [[SPG:Main Game Loop|Main Game Loop]] wall collision (while grounded) actually takes place before the Player's position physically moves anywhere, so they wont actually be in a wall when they tries to collide with it. The game accounts for this by actually adding their X Speed and Y Speed to the sensor's position, this is where the sensor ''would'' be if the Player had moved yet.
 
 
 
====E and F Method====
 
 
 
Assuming the wall's left side to be at an X position of 704 ($02C0), the Player cannot get closer than an X Position of 693 ($02B5). Assuming the wall's right side to be at an X position of 831 ($033F), the Player cannot get closer than an X Position of 842 ($034A). Thus the distance between both sensors inclusive should be 21 pixels, stretching from the Player's X Position-10 to X Position+10.
 
 
 
When the Player collides with a wall, this will set their Ground Speed to 0 if they is moving in the direction of the wall, not away from it.
 
 
 
The distance value found by the sensor in it's given direction is used to stop the Player at a wall.
 
 
 
=====E and F Distance Limits=====
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
'''While Grounded:'''
 
The distances found by the wall sensors are used slightly differently while grounded.
 
 
 
Naturally, the game will ignore a positive distance because they will not collide. If the sensor's distance is negative, this means that when the Player's position actually does change, they will be inside the wall.
 
 
 
In this case, because the sensor is actually out in front of the Player (where they will be after they moves) instead of using the distance to reposition the Player by directly changing their position, the game smartly uses the fact that the Player has still yet to move within the current frame. All it has to do is add the distance to the Player's X Speed (if moving right, or ''subtract'' the distance from the Player's X Speed if moving left. This would be done to Y Speed if in wall mode). This results in the Player moving when their position changes, right up to the wall, but no further. In the next frame, because Ground Speed has been set to 0 the Player will have stopped just like in any other situation.
 
 
 
</div>
 
<div class="large-6 columns">
 
'''While Airborne:'''
 
 
 
Like normal, if the distance is negative and the sensor is inside the wall, they will collide. The game will ignore a positive distance.
 
</div>
 
</div>
 
 
 
===Extra===
 
the Player cannot jump when there is a low ceiling above them. If there is a collision detected with sensors at the Player's X Position-9 and X Position+9, at Y Position-25, the Player won't bother jumping at all.
 
 
 
 
 
===Summary===
 
 
 
Here's a handmade visualisation of how sensors interact with solid tiles (here highlighted in bright blue, green, and cyan). You can notice how the sensors are pushing the Player from the ground tiles, and is overall rather simple. The <span style="color:#ff38ff; font-weight: bold;">E</span> and <span style="color:#ff5454; font-weight: bold;">F</span> sensors lower when on flat ground. You can also notice the sensors snap in 90 degree rotations resulting in four modes, this is covered in [[SPG:Slope Physics|Slope Physics]].
 
 
 
[[Image:SPGCollisionDemo.gif|link=Special:FilePath/SPGCollisionDemo.gif]] ''Keep in mind, while on the ground the upper <span style="color:#00aeef; font-weight: bold;">C</span> and <span style="color:#fff238; font-weight: bold;">D</span> sensors would not exist, and while gsp is positive the left wall sensor would also not appear. These sensors are only included for illustration purposes.''
 
 
 
====Bugs Using This Method====
 
 
 
Unfortunately, there are a couple of annoying bugs in the original engine because of this method.
 
 
 
If the Player stands on a slanted ledge, one sensor will find no tile and return a height of foot level. This causes the Player to be set to the wrong position.
 
 
 
[[Image:SPGSlopeBug1Animated.gif|link=Special:FilePath/SPGSlopeBug1Animated.gif]]
 
 
 
The Player raises up with sensor <span style="color:#38ffa2; font-weight: bold;">B</span> sensor as they moves right. When <span style="color:#38ffa2; font-weight: bold;">B</span> drops off the ledge, the Player defaults to the level of sensor <span style="color:#00f000; font-weight: bold;">A</span>. Then they raises up with sensor <span style="color:#00f000; font-weight: bold;">A</span> as they moves further right. So they will move up, drop down, and move up again as they runs off the ledge.
 
 
 
There are only a few areas where this is noticeable, but it applies to all Mega Drive titles and is pretty tacky.
 
 
 
The second form of it occurs when two opposing ramp tiles abut each other, as in some of the low hills in [[Green Hill Zone (the Player the Hedgehog 16-bit)|Green Hill Zone]] and [[Marble Zone]].
 
 
 
[[Image:SPGSlopeBug2Animated.gif|link=Special:FilePath/SPGSlopeBug2Animated.gif]]
 
 
 
Sensor <span style="color:#38ffa2; font-weight: bold;">B</span> starts climbing down the ramp on the right, but the Player still defaults to the level of the previous ramp found by sensor <span style="color:#00f000; font-weight: bold;">A</span>. Because these ramps are usually shallow, this only causes them to dip down in the middle by about 1 pixel.
 
 
 
But that is not all. Because the highest sensor is the one the Player gets the angle from, even though it looks like they should be considered to be at the angle of the ramp on the right (because they is closer to it), they will still have the angle of the ramp on the left. When you jump, they will jump at that angle, moving backward, not forward like you would expect.
 
 
 
==Jumping "Through" Floors==
 
  
There are some ledges that the Player can jump up "through". These are often in the hilly, green zones such as [[Green Hill Zone (Sonic the Hedgehog 16-bit)|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 the Player's <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> sensors. They are ignored entirely by C and D as well as the wall sensors E and F. Finally, sensor <span style="color:#00f000; font-weight: bold;">A</span> and <span style="color:#38ffa2; font-weight: bold;">B</span> (mostly) only detect the floor when the Player is moving downwards (but always while on the ground). So with a slightly shorter jump, you will see the Player 'pop' upwards onto a jump through surface once they begin to fall.
+
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).
* Find information on how the Player's momentum and slope handling work in the [[SPG:Slope_Physics|Slope Physics]] guide.
 
  
 
[[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).