Actions

SPG:Solid Tiles

From Sonic Retro

Revision as of 15:06, 14 December 2020 by Lapper2 (talk | contribs) (Distance: Extra clarity)

Notes:

  • Following only describes how Sonic collides and interacts with solid tiles. Solid objects, such as 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. For this, refer to Solid Objects.
  • Variables and constants for Sonic and other characters such as xpos and acc will be referenced frequently, they can be found in Basics.
  • The original games use solid tiles, however the ideas and mechanics of Sonic's base collision setup can be adapted (with adjustments) to other engines using sprite masks, line intersections, etc.
  • While 16x16 tiles are "officially" named blocks, they are being referred to as solid tiles here since they are a simple grid pattern of sets of data which can be read simply, as opposed to objects or any other method. "Solid Tiles" and "Blocks" can be used interchangeably in this guide.

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.

Sonic's collisions and interactions with these solid tiles are what make 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 have kept speculation to a minimum.

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

Solid Tiles

Solid tiles are a grid of data blocks, which represent solid areas within each grid cell. This area is defined using height masks.

Height Masks

When checking a solid 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 0px ($00) to 16px ($10) and an angle value.

SPGHeightMask.PNG

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).

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 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.

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, and left, and all behave the same in their respective directions.

SPGSensorAnchors.png The white points represent the anchor positions of Sonic's sensors when uncurled.

In this example, the sensor to Sonic's right points right, and those at Sonic's feet point down.

So, 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.

Sensor Regression & Extension

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 nearby tiles also, until certain conditions are met.

In the case of the A sensor which is pointing down looking for solids below:

Regression:

  • If the anchor point finds a Solid Tile, and if the height array value at the sensor's x of that tile is 16, it will check up by 1 extra Solid Tile. We'll call this the "regression" since it goes back inwards towards Sonic.

If a regression occurs and finds no solid in the second tile, the second tile will be ignored.

Extension:

  • If the anchor point just finds an empty tile (height array value of 0), it will check down by 1 extra Solid Tile. We'll call this the "extension" because it goes outwards, away from Sonic.

If an extension occurs and finds no solid in the second tile, the second tile will be ignored.

If not the extension/regression does not fail, the new tile is the one which is processed.

The regression & extension will occur in the direction of the sensor, be it horizontal or vertical. So sensor F's regression would check an extra tile to the left, and extension would check an extra tile to the right. While sensor D 's regression would check an extra tile below, and extension would check an extra tile above.

The result of this, is the sensor will be able to find the 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. All in the given direction of that sensor.

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 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, a distance of 0 is returned by default.

  • A distance of 0 means the sensor is just touching the solid tile surface and does not need to move.
  • 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 Sonic to keep him attached to the ground even if the ground has sloped away from him a bit. 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.

If the object decides to snap itself to the terrain, it simply has to add distance value from 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.

Summary

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, Sonic will have a gsp of 6.

SPGSensorProcess.gif


Sonic's Sensors

Like any object which wants to collide with tiles, sensors surround Sonic. Throughout this guide these will be drawn as lines, as these are the areas which Sonic will not allow solid tiles to overlap.

SPGSensors.png Sonic's solidity for tiles. When solid tiles overlap these areas, he will be pushed out.

 A and B - Floor collision
 C and D - Ceiling collision (only used mid-air)
 E and F - Wall collision (shifting by 8px depending on certain factors, which will be explained)
 XY - Sonic's xpos and ypos


Since Sonic's collision setup is symmetrical, it makes sense for the game to set up widths and heights using radius values. Sonic has separate radius values for his E and F sensor pair (his Push Radius) which always remains the same, and for his A, B, C and D sensors there is a width radius (his Body Width Radius) and height radius (his Body Height Radius) both of which will change depending on Sonic's state.

Note on sprite alignment:

  • Sonic's sprite is 1 pixel offset to the left when he faces left, which can result in him 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 Solid Objects.

Floor Sensors (A and B)

SPGStandingAnimated.gif

A and B stretch down from Sonic's ypos to his feet at ypos+Body Height Radius.

Movement

Typically, Sonic's Body Width Radius is 9, placing A on Sonic's left side, at xpos-9. While B should be on his right, at xpos+9, 19 pixels wide. His Body Height Radius is usually 19, making him 39 pixels tall in total.

However while rolling or jumping, his Body Width Radius becomes 7, placing A at xpos-7. With B at xpos+7, 15 pixels wide. This is to ensure Sonic doesn't appear too far away from the floor at steep angles while in curled up. Crouching does not affect his Body Width Radius.

Here's an example of that in action:

SPGWidthRadiusChange.gif

While rolling or jumping (and otherwise generally curled up), Sonic's Body Height Radius becomes smaller at a value of 14, making him 29 pixels tall. Because of this, in the step in which Sonic rolls or jumps or otherwise becomes shorter, the engine adds 5 to his ypos so that his bottom point will remain unchanged despite him getting shorter and his center changing position. 5 also has to be subtracted from ypos 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.

Method

Assuming the ground level to be at a Y position of 736 ($02E0), while standing Sonic is atop it at a ypos of 716 ($02CC), which is 20 pixels above ground level.

Floor sensors are a special case, there are 2 sensors and they need to detect slopes - so greater depth will be explored after establishing where these sensors are. 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. Effectively whichever sensor finds closer ground to Sonic's position. The sensor that wins, is the distance and angle used.

Once the winning distance is found, it can be used to reposition sonic.

As with all sensors, Sonic is moved using the distance value returned by the sensor, adding it to his current position in this case.

If the floor is further from Sonic than the sensor, the distance is greater than 0. While most objects will not react to a positive distance for most sensors, floor sensors typically do. When a positive distance is found for floor sensors, it will be added to Sonic's position and Sonic will be pulled and snapped down to the floors surface. This only occurs when Sonic is already previously grounded. Other sensors, like those for walls, will not pull sonic towards them, only push him out.

However, there is a limit to this. In Sonic 1, if the distance value is less than -14 or greater than 14, Sonic won't be moved. In Sonic 2 onward however the positive limit depends on Sonic's current speed - in this case, (for when Sonic is on the floor) if the distance is greater than

 minimum(absolute(xsp)+4, 14)
 

then he won't be moved. So the faster Sonic moves, the further the floor below him can be accepted. The -14 limit remains the same.

Note: when I say Sonic "isn't moved", it means he effectively hasn't found floor and won't be grounded.

Ledges

Sonic 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 him.

If both sensor A and B detect no solid tiles, Sonic will "fall" - a flag will be set telling the engine he is now in the air.

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 is stopped (his gsp is 0).

How does the engine know? It is 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 and vice versa.

But if Sonic began balancing the instant one of the sensors found nothing, he would do it too "early", and it would look silly. So it only happens when only one sensor is active, and xpos is greater than the edge of the solid tile found by the active sensor.

SPGBalancingAnimated.gif

Assuming the right edge of the ledge to be an X position of 2655 ($0A5F), Sonic will only start to balance at an xpos of 2656 ($0A60) (edge pixel+1). He'll fall off at an xpos 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 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 is at an xpos of 2662 ($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 (not to look up, though) when balancing on the ground but not when balancing on an object.

Ceiling Sensors (C and D)

SPGHitCeiling.gif

Sonic's C and D sensors are always an exact mirror image of Sonic's floor sensors, they have the same X positions and length but are flipped upside down.

They perform in the exact same way simply up instead of down.

However, they aren't active at the same times.

Method

Sonic will detect ceilings and be pushed out of them whether he is 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 both with a high absolute xsp.


Wall Sensors (E and F)

SPGPushingAnimated.gif

E spans from Sonic's xpos to his left at xpos-Push Radius, while F spans from Sonic's xpos to his right at xpos+Push Radius.

Movement

Push Radius is always 10, stretching E to Sonic's left side, at xpos-10. While F stretches to his right, at xpos+10, giving Sonic a total width of 21 pixels when pushing.

Sensors E and F Spend most of their time at Sonic's ypos however while Sonic's ang is 0 (on totally flat ground) both wall sensors will move to his ypos+8 so that he can push against low steps and not just snap up ontop of them.

The horizontal sensor lines are always positioned at ypos while airborne.

Method

Assuming the wall's left side to be at an X position of 704 ($02C0), Sonic cannot get closer than an xpos of 693 ($02B5). Assuming the wall's right side to be at an X position of 831 ($033F), Sonic cannot get closer than an xpos of 842 ($034A). Thus both sensor lines combined should be 21 pixels wide, stretching from Sonic's xpos-10 to xpos+10.

Any time it detects a solid tile, Sonic should be "popped out", set to the edge of the solid (using the sensor's returned distance), and his gsp set to 0. (He cannot be popped out by only 10, because then a point at xpos+10 would still lie within the edge pixel of the tile. This would register a continuous collision, and he would stick to the wall.) This will also set his gsp to 0 if he is moving in the direction of the wall, not away from it. If it were otherwise, he would stick to walls if he tried to move away.

Though the tile's edge minus Sonic's xpos might be 11, there are 10 free pixels between Sonic's xpos and the tile's edge. The eleventh pixel away is the tile's edge itself. This would be the same for a tile on the left. So Sonic is effectively 21 pixels wide when including xpos.

You may remember that sensors A and B are only 19 pixels apart but Sonic is 21 pixels wide when pushing into walls. This means that Sonic is skinnier by 2 pixels when running off of ledges than when bumping into walls.

Sensor Activation

Knowing where the sensors are and what they do is only half the job since they are only sometimes active. This depends while you are grounded or airborne.

While Grounded

Floor Sensors A and B are always active while grounded, and will use their 1 tile extension to actively search for new floor below Sonic's feet.

When grounded, Wall Sensors E and F only activate when Sonic is walking in that direction. For example, while standing still Sonic isn't checking with his wall sensors at all, but while gsp is positive, Sonic's E sensor is inactive, and while gsp is negative, Sonic's F sensor is inactive.

However this is not always the case, both wall sensors simply don't appear when Sonic's ang 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 Sonic's 'ang' is a multiple of 90 in addition to the angle range.

While grounded Ceiling Sensors C and D are never active, and Sonic won't check for collision with solid tiles above himself while on the floor.

While Airborne

While in the air, all sensors play a part to find ground to reattach Sonic to. But rather than have all active at once and risk lag, only 2-3 will be active at any given time.

As you move, the game will check the angle of your motion (xsp and ysp) 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 xsp and ysp and finding which is larger or smaller.

 if absolute xsp is larger then or equal to absolute ysp then
   if xsp is larger than 0 then
     Sonic is going mostly right
   else
     Sonic is going mostly left
 else
   if ysp is larger than 0 then
     Sonic is going mostly down
   else
     Sonic is going mostly up

Depending on the quadrant, different sensors will be active.

When going mostly right, Sonic's F sensor will be active, along with both A and B floor sensors and the C and D ceiling sensors.

When going mostly left, it is the exact same as going right, but the E wall sensor instead.

When going mostly up, both the C and D ceiling sensors and the E and F wall sensors are active.

When going mostly down, it is the same as going up, but the A and B floor sensors are active instead of the ceiling sensors.

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 sensor lines are pushing Sonic from the ground tiles, and is overall rather simple. The E and F sensors lower when on flat ground. You can also notice the sensor lines snap in 90 degree rotations resulting in four modes, this will be covered further down the guide.

SPGCollisionDemo.gif Keep in mind, while on the ground the upper C and D 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 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.

SPGSlopeBug1Animated.gif

Sonic raises up with sensor B sensor as he moves right. When B drops off the ledge, Sonic defaults to the level of sensor A. Then he raises up with sensor A as he moves further right. So he will move up, drop down, and move up again as he 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 and Marble Zone.

SPGSlopeBug2Animated.gif

Sensor B starts climbing down the ramp on the right, but Sonic still defaults to the level of the previous ramp found by sensor A. Because these ramps are usually shallow, this only causes him to dip down in the middle by about 1 pixel.

But that is 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 is closer to it), he will still have the angle of the ramp on the left. When you jump, he will jump at that angle, moving backward, not forward like you would expect.

Notes

  • Find information on how Sonic's slope physics work at Slope Physics
  • Sonic cannot jump when there is a low ceiling above him. If there is a collision detected with a sensor line stretching from Sonic's xpos-9 to xpos+9, at ypos-25, Sonic won't bother jumping at all.