Actions

Difference between revisions of "SPG:Solid Objects"

From Sonic Retro

(Formatting and consistency)
(Making solid object explanation easier to follow, and changing "Sonic" to "The Player")
Line 5: Line 5:
 
==Introduction==
 
==Introduction==
  
There are many objects in Sonic games and they interact with Sonic in many different ways and are a very different beast than Solid Tiles. Object-player collision doesn't work the same way as [[SPG:Solid_Tiles|Solid Tiles]]. This guide will go over the collision of various objects found in the Sonic trilogy.
+
There are many objects in Sonic games and they interact with the Player in many different ways and are a very different beast than Solid Tiles.
 +
 
 +
There are 2 main ways objects interact with the Player. Hitboxes like rings and checkpoints, and solidity like item boxes or push blocks. Yes, both of these are completely seperate.
 +
 
 +
We'll go over hitbox interactions first.
  
 
==Object Hitboxes==
 
==Object Hitboxes==
  
Objects such as rings, enemies, and bumpers have a hitbox, which is a general area that will trigger some kind of reaction with Sonic when both their hitboxes overlap. Object hitboxes are centered on the object's X and Y Positions.  
+
Objects such as rings, enemies, and bumpers have a hitbox, which is a general area that will trigger some kind of reaction with the Player when both their hitboxes overlap. Object hitboxes are centered on the object's X and Y Positions.  
  
Sometimes objects which seem solid (like bosses or bumpers) actually only have a hitbox, and when they overlap, simply push Sonic in the other direction. As a general rule, any seemingly solid object that Sonic cannot stand on is most likely actually using a hitbox rather than real solidity.
+
Note:
 +
*Sometimes objects which seem solid (like bosses or bumpers) actually only have a hitbox, and when they overlap, simply push the Player in the other direction. As a general rule, any seemingly solid object that the Player cannot stand on is most likely actually using a hitbox rather than ''real'' solidity.
  
 
===Hitbox Reaction===
 
===Hitbox Reaction===
  
If Sonic's hitbox touches an object's hitbox, some kind of reaction will occur. Usually this is totally specific to the object, like collecting a ring or bursting a balloon. Though, the object can set specific flags which change the "type" of reaction they will have. The two most consistent reaction types are as follows:
+
If the Player's hitbox touches an object's hitbox, some kind of reaction will occur. Usually this is totally specific to the object, like collecting a ring or bursting a balloon. Though, the object can set specific flags which change the "type" of reaction they will have. The two most consistent reaction types are as follows:
  
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
====Hurt Hitboxes====
 
====Hurt Hitboxes====
While these aren't solid, any contact with them will immediately give damage to Sonic. Examples are the spikes on the GHZ log bridge, the spikes under a MZ Spike Trap, and certain projectiles.
+
While these aren't solid, any contact with them will immediately give damage to the Player. Examples are the spikes on the GHZ log bridge, the spikes under a MZ Spike Trap, and certain projectiles.
 
</div>
 
</div>
 
<div class="large-6 columns">
 
<div class="large-6 columns">
Line 30: Line 35:
 
Ahead, objects with hurt hitboxes will be coloured differently.
 
Ahead, objects with hurt hitboxes will be coloured differently.
  
===Sonic's Hitbox===
+
===The Player's Hitbox===
  
In order to interact with other object's hitboxes, Sonic needs his own.
+
In order to interact with other object's hitboxes, the Player needs their own.
  
 
[[Image:SPGHitBoxes.png]]
 
[[Image:SPGHitBoxes.png]]
 +
The hitbox for Sonic.
  
Sonic's hitbox is much like that of any other object. It sits at Sonic's X Position and Y Position. It has a width radius of 8, and its height radius is always 3 pixels shorter than Sonic's Height Radius, making it 17 X 33 pixels in size while standing.
+
The Player's hitbox is much like that of any other object. It sits at the their X Position and Y Position. It has a width radius of 8, and its height radius is always 3 pixels shorter than the Player's Height Radius, making it 17 X 33 pixels in size while standing.
  
When crouching, Sonic's hitbox needs to shrink. Problem is, Sonic's position and Height Radius don't actually change at all while crouching. So to tackle this it manually updates the hitbox's size and position while Sonic crouches, where 12px is added to the hitbox's Y position, and the hitbox's height radius is set to 10.
+
When crouching, obviously the Player's hitbox needs to shrink. Problem is, the Player's position and Height Radius don't actually change at all while crouching. So to tackle this the game manually updates the hitbox's size and position while the Player crouches, where 12px is added to the hitbox's Y position, and the hitbox's height radius is set to 10.
  
 
===Quirks With Hitboxes===
 
===Quirks With Hitboxes===
  
Because these hitboxes aren't even numbered in size, and because object origins don't lay perfectly centered between pixels (rather they are 1px left and down) most hitboxes will also appear 1px too big on the right side and the bottom side. This is simply how things work in-game and for that reason won't be ignored. Sprites like rings are even-numbered sizes (such as 16 X 16) so an anomaly like the following can (and does, always) occur.
+
Because these hitboxes aren't even numbered in size, and because object origins don't lay perfectly centred between pixels (see [[SPG:Basics#Sizes|Basics]]) most hitboxes will also appear 1px too big on the right side and the bottom side. This is simply how things work in-game and for that reason won't be ignored. Sprites like rings are even-numbered sizes (such as 16 X 16) so an anomaly like the following can (and does, always) occur.
  
 
[[Image:SPGRingTest.gif]]
 
[[Image:SPGRingTest.gif]]
Line 51: Line 57:
 
This is the case with any object's hitboxes.
 
This is the case with any object's hitboxes.
  
The same is true for solid object boxes, so Sonic will push against objects 1px further away when facing leftwards than he will tiles.
 
  
 
==Solid Objects==
 
==Solid Objects==
  
As stated above, object collision is totally separate from tile collision. Sonic does not collide with objects using his solid tile sensors, instead, special calculations are used to check if Sonic's general shape is inside an object's solid box, and push him out.
+
Object-player collision doesn't work the same way as [[SPG:Solid_Tiles|Solid Tiles]]. The Player does not collide with objects using his solid tile sensors, instead, special calculations are used to check if the Player's general shape is inside an object's solid box, and push him out.
 
 
This all occurs after Sonic's code has been executed for that frame, including his tile collision and movement. Since objects run their code after Sonic, it's the job of the objects to push Sonic out of themselves.
 
  
''Note: These collisions deal with floored positions (integer values), ignoring any fractional/subpixel amount.''
+
This all occurs after the Player's code has been executed for that frame, including their tile collision '''and''' movement. Since objects run their code '''after''' the Player, it's the job of the objects to push the Player out of themselves. Like say the Player is running towards a solid block object with some medium speed. When their position changes at the end of their code, they will move inside the solid object. Then soon afterwards on the same frame the solid object runs its code, checks for the Player and acts solid accordingly, pushing the Player out.
  
 
===General Solid Object Collision===
 
===General Solid Object Collision===
  
Solid object collision does away entirely with hitboxes and instead uses the ''actual'' size of the objects. The Width Radius and Height Radius. For normal objects that is, Sonic will use his Height Radius for this too, but horizontally he of course needs to use his Push Radius instead.
+
Solid object collision does not involve the object hitboxes and instead uses the ''actual'' size of the objects. The Width Radius and Height Radius. The Player will use their Height Radius for this too, but horizontally they of course use their Push Radius instead.
  
 
The following is long. It is written in a way very close to how the original game has it coded because accuracy requires it. To orient yourself, a brief overview of the long process below goes as follows:
 
The following is long. It is written in a way very close to how the original game has it coded because accuracy requires it. To orient yourself, a brief overview of the long process below goes as follows:
* Sonic will check if he is overlapping the object.
+
* The Player will check if he is overlapping the object.
* Sonic will decide which side of the object he is nearest to n both axis (either left or right and either top or bottom).
+
* The Player will decide which side of the object he is nearest to n both axis (either left or right and either top or bottom).
 
* Then check how close in pixels he is to being outside of the object on that side (distance to left or right and distance to top or bottom).
 
* Then check how close in pixels he is to being outside of the object on that side (distance to left or right and distance to top or bottom).
 
* The game then decides whether he's closer to a horizontal side to be pushed out on the x axis or a vertical side to be pushed out on y axis.
 
* The game then decides whether he's closer to a horizontal side to be pushed out on the x axis or a vertical side to be pushed out on y axis.
Line 74: Line 77:
 
Now, let's get into the details.
 
Now, let's get into the details.
  
The first thing the object collision code does is check if Sonic is standing on the object (using a flag which is set if he lands on one). If he is, it will skip straight to checking if Sonic has walked off the edges rather than general object collision. Otherwise, it will continue as follows.
+
The first thing the object collision code does is check if the Player is standing on the object. The Player has a flag which determines if they are standing an object, which is set upon landing on one. If they are, it will skip straight to checking if the Player has walked off the edges rather than general object collision (which we will go into detail about further down in [[SPG:Solid Objects#Standing On Solid Objects|Standing On Solid Objects]]). Otherwise, it will continue as follows.
 +
 
  
 
====Checking For An Overlap====
 
====Checking For An Overlap====
To check for an overlap the game needs to know the boundaries that Sonic's positions need to be within.
 
  
Horizontally, the object combines its own Width Radius with Sonic's Push Radius and adds 1px extra (so Push Radius + 1).
+
First thing that needs to happen is the game needs to know if the Player is even touching the object to begin with.
 +
 
 +
Both the Player and the solid object are of course rectangles, but it would be costly to check if 2 rectangles overlap each frame. Instead, a lot of calculations are saved because checks if a single position (the Player's position) is within one rectangle. This is achieved by combining the Player's current Push and Height Radius values with the object's Width and Height Radius values to form this new rectangle.
  
Vertically, it very similar. The object combines its own Height Radius with Sonic's current Height Radius to get a combined radius. 1px isn't added here, but it is (kind of) later after a collision has occurred.
+
Horizontally, the object combines its own Width Radius with the Player's Push Radius and adds 1px extra (so Push Radius + 1). The extra pixel is added because the final position the Player pushes at is the Players Push Radius + 1 away from the object's side edge.
  
''Note: Objects don't always use their real Height Radius when acting solid, and will just pass in a hard-coded value. Sometimes this value is slightly different than you'd expect, like 17 instead of 16. Point being, it's worth checking each individual object for the specific values they use. Generally speaking, the above applies to most.''
+
Vertically, it very similar. The object combines its own Height Radius with the Player's current Height Radius to get a combined radius. 1px isn't added here, but it is (kind of) later after a collision has occurred.
  
Here's a demonstration of how these new radiuses relate to Sonic's size (while standing in this case) for a block.
+
Here's a demonstration of how these new radiuses relate to the Player's size (while standing in this case) for a block.
  
 
[[Image:SPGSolidObjectOverlap.gif]]
 
[[Image:SPGSolidObjectOverlap.gif]]
  
From this point, when I refer to the object's combined radiuses I will call them '''combined X radius''' and '''combined Y radius'''.
+
From this point, when I refer to the object's combined radiuses I will call them '''combined X radius''' and '''combined Y radius''', and I will refer to the entire box as the '''combined box'''.
 +
I will also refer to '''combined X diameter''' (which is combined X radius * 2) and '''combined Y diameter''' (which is combined Y radius * 2).
  
Now all the game needs to worry about is Sonic's X Position and Y Position being within this new box, it no longer needs to worry about what Sonic's sizes are.
+
Now all the game needs to worry about is the Player's X Position and Y Position being within this new '''combined box''', it no longer needs to worry about what the Player's sizes are at all.
  
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 +
 +
 
=====Horizontal Overlap=====
 
=====Horizontal Overlap=====
  
The game will calculate the difference between Sonic's X Position and the object's left side.
+
The game will calculate the difference between the Player's X Position and the left edge of the '''combined box'''.
 +
 
 +
  left_difference = (the Player's X Position - object's X Position) + combined_x_radius
  
  left_difference = sonic's X Position - object's X Position
+
Then, it will check if this new difference value has passed the left or right boundaries of the '''combined box''', and exit the object collision if it has.
  left_difference += combined_x_radius //add combined radius
 
  
Then, it will check if this new difference value has passed the boundaries, and exit the object collision if it has.
+
  // the Player is too far to the left to be touching?
 +
  if (left_difference < 0) exit object collision
  
   //sonic is too far to the left to be touching
+
   // the Player is too far to the right to be touching?
  if (left_difference < 0) exit;
+
   if (left_difference > combined_x_diameter) exit object collision
 
 
  object_x_diameter = combined_x_radius * 2 //get object's x diameter
 
 
 
  //sonic is too far to the right to be touching
 
   if (left_difference > object_x_diameter) exit
 
  
If no exit occurred, Sonic is overlapping on the X axis, and it will continue. The game will remember this '''left difference'''.  
+
If no exit occurred, the Player is overlapping on the X axis, and it will continue. The game will remember this '''left difference'''.  
 
</div>
 
</div>
 
<div class="large-6 columns">
 
<div class="large-6 columns">
 +
 +
 
=====Vertical Overlap=====
 
=====Vertical Overlap=====
  
Then for vertical overlap, it calculates the difference between Sonic's Y Position and the object's top side.
+
Then for vertical overlap, it calculates the difference between the Player's Y Position and the top edge of the '''combined box'''.
  
   top_difference = sonic's Y Position - object's Y Position
+
   top_difference = (the Player's Y Position - object's Y Position) + 4 + combined_y_radius
  top_difference += 4 //add 4
 
  top_difference += combined_y_radius //add combined radius
 
  
The game also allows Sonic to be slightly above the object by 4 pixels and still overlap, extending the top of the object 4 pixels for extra overlap. This is likely just in case the object moves down slightly or the object is slightly lower than a previous ledge Sonic was standing on. The game does this by effectively pretending Sonic is 4px lower than he really is when checking the y overlap. This is subtracted later.
+
The game also allows the Player to be slightly above the object by 4 pixels and still overlap, extending the top of the object 4 pixels for extra overlap. This is likely just in case the object moves down slightly or the object is slightly lower than a previous ledge the Player was standing on. The game does this by effectively pretending the Player is 4px lower than they really are when checking the y overlap. If the object is lower than the Player, top_difference would be negative before '''combined Y radius''' is added, so it is achieved by simply adding 4 to the distance. This is subtracted later.
  
Then, it will check if this new difference value has passed the boundaries, and exit the object collision if it has.
+
Then, it will check if this new difference value has passed the top or bottom boundaries of the '''combined box''', and exit the object collision if it has.
  
   //sonic is too far above to be touching
+
   // the Player is too far above to be touching
   if (top_difference < 0) exit;
+
   if (top_difference < 0) exit object collision
 
 
  object_y_diameter = combined_y_radius * 2 //get object's y diameter
 
 
    
 
    
   //sonic is too far down to be touching
+
   // the Player is too far down to be touching
   if (top_difference > object_y_diameter) exit
+
   if (top_difference > combined_y_diameter) exit object collision
  
If no exit occurred, Sonic is overlapping on the y axis, and it will continue. The game will remember this '''top difference'''.  
+
If no exit occurred, the Player is overlapping on the y axis, and it will continue to the next step. The game will remember this '''top difference'''.  
 
</div>
 
</div>
 
</div>
 
</div>
 +
 +
The reason the game does it in this fashion rather than just checking between -radius and +radius for example is to preserve calculations needed. It has been done in such a way that it now has 2 variables it can keep using, left_difference and top_difference.
 +
  
 
====Finding The Direction of Collision====
 
====Finding The Direction of Collision====
If Sonic is found to be touching the object, the game will then decide whether he is to be popped out the top or bottom, or the left or right of the object. The game will compare Sonic's position to the object's position to determine which side he is on.  
+
If the Player is found to be touching the object, the game will then decide whether they are to be popped out the top or bottom, or the left or right of the object. The game will compare the Player's position to the object's position to determine which side they are on.  
  
To do this, the game will first determine which side Sonic is in comparison with the object's position.  
+
To do this, the game will first determine which side the Player is in comparison with the object's position.  
  
If Sonic's X Position is greater than the object's X position, he's on the right, otherwise, he's on the left. If Sonic's Y Position is greater than the object's Y position, he's on the bottom, otherwise, he's on the top.
+
If the Player's X Position is greater than the object's X position, they're on the right, otherwise, they're on the left. If the Player's Y Position is greater than the object's Y position, they're on the bottom, otherwise, they're on the top.
  
After the side is determined for each axis, the game will calculate a distance to the nearest edge.  
+
After the side is determined for each axis, the game will calculate a distance to the ''nearest'' edge.  
  
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-12 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 
<div class="large-6 columns" style="padding:0px">
 +
 +
 
=====Horizontal Edge Distance=====
 
=====Horizontal Edge Distance=====
If Sonic is on the left, the edge distance is simply equal to the '''left difference''' which is the distance to the left side and will be a positive number.  
+
If the Player is on the left, the edge distance is simply equal to the '''left difference''' which is the distance to the left side of the '''combined box''' and will be a positive number.  
 +
 
 +
  x_distance = left_difference
  
If Sonic is on the right, the distance will be flipped around.
+
If the Player is on the right, the distance will be flipped around like so:
  
 
   x_distance = left_difference - object_x_diameter
 
   x_distance = left_difference - object_x_diameter
 
    
 
    
This is effectively the distance to the object's right side and will be a negative number.  
+
This is effectively the distance to the right side of the '''combined box''' and will be a negative number.  
  
Whichever side it is, we will call this new distance the '''x distance'''.
+
Whichever side it is, we will call this new distance the '''x distance''', and the game knows which side, left or right, the Player is based on the sign (+/-) of this value.
 
</div>
 
</div>
 
<div class="large-6 columns">
 
<div class="large-6 columns">
 
=====Vertical Edge Distance=====
 
=====Vertical Edge Distance=====
It is the same along the y axis. If Sonic is on the top, the edge distance is simply equal to the '''top difference''' which is the distance to the left side and will be a positive number.  
+
It is the same along the y axis. If the Player is on the top, the edge distance is simply equal to the '''top difference''' which is the distance to the top side of the '''combined box''' and will be a positive number.  
 +
 
 +
  y_distance = top_distance
  
If Sonic is on the bottom, the distance will be flipped around (and that extra 4px from before will be subtracted).
+
If the Player is on the bottom, the distance will be flipped around (and that extra 4px from before will be subtracted).
  
   top_difference -= 4
+
   y_distance = top_difference - 4 - object_y_diameter
  y_distance = top_difference - object_y_diameter
 
 
    
 
    
This is effectively the distance to the objects bottom side and will be a negative number.  
+
This is effectively the distance to the bottom side of the '''combined box''' and will be a negative number.  
  
Whichever side it is, we will call this the '''y distance'''.
+
Whichever side it is, we will call this the '''y distance''', and the game knows which side, top or bottom, the Player is based on the sign (+/-) of this value.
  
''Note: If Sonic is on the top, the extra 4px isnt subtracted yet. It will be subtracted upon landing.''
+
''Note: You may have noticed that if the Player is on the top, the extra 4px isn't subtracted yet. It will be subtracted upon landing on top.''
 
</div>
 
</div>
 
</div>
 
</div>
 +
  
 
=====Choosing The Direction=====
 
=====Choosing The Direction=====
Finally, with all of this information, the game can decide which way Sonic should be popped out.  
+
Finally, with all of this information, the game can decide which way the Player should be popped out. Either vertically or horizontally.
  
It does this by finding which side Sonic is nearer to, which makes sense.
+
It does this by finding which side the Player is nearer to, which makes sense.
  
 
   if (absolute(x distance) > absolute(y distance))
 
   if (absolute(x distance) > absolute(y distance))
Line 196: Line 208:
 
[[Image:SPGSolidObjectNearerSide.png]]
 
[[Image:SPGSolidObjectNearerSide.png]]
  
The horizontal axis is favoured just a little more than the vertical, which is simply due to Sonic not being square himself. Keep in mind this exact pattern is only valid for an object of this exact size and while Sonic is standing.
+
The horizontal axis is favoured just a little more than the vertical, which is simply due to Sonic's Width and Height Radius not being square. Keep in mind this exact pattern is only valid for an object of this exact size and while Sonic is standing.
 +
 
 +
From there, the game can easily tell which way to pop out the Player on either axis depending on the sign (+/-) of the distance value. When colliding vertically, the game knows that Player is on top if the '''y distance''' is positive, and underneath if the '''y distance''' is negative. Same goes for left and right and the '''x distance'''.
  
From there, the game can easily tell which way to pop out Sonic on either axis depending on the sign of the distance value. When colliding vertically, the game knows that Sonic is on top if the '''y distance''' is positive, and underneath if the '''y distance''' is negative. Same goes for left and right and the '''x distance'''.
 
  
====Popping Sonic Out====
+
====Popping The Player Out====
Once a collision has occurred and the game had decided the direction Sonic then needs to be "popped out" of the object so that he is no longer in it, and his speeds need to be changed. But where does it put Sonic? Well, there's also an even greater use for the '''x distance''' or '''y distance'''. They are the exact distance Sonic needs to move to exit the object, but reversed. So when he is popped out, they will simply be subtracted from his position.
+
Once a collision has occurred and the game had decided the direction the Player then needs to be "popped out" of the '''combined box''' so that their position is no longer within it. But where does it put the Player? Well, there's also an even greater use for the '''x distance''' or '''y distance'''. They are the exact distance the Player needs to move to exit the object, but reversed. So when they are popped out, they will simply be subtracted from their position.
  
 
=====Popped Left and Right=====
 
=====Popped Left and Right=====
Popping Sonic out left or right will simply reset his speeds and position, and set him to pushing if he is grounded.
+
Popping the Player out left or right will simply reset his speeds and position, and set him to pushing if he is grounded.
  
There are a couple of conditions. The game will only bother popping Sonic out if absolute '''y distance''' is greater than 4. The game will also only bother touching Sonic's speeds if '''x distance''' is not 0.
+
There are a couple of conditions. The game will only bother popping the Player out horizontally if absolute '''y distance''' is greater than 4. If not, it will exit.
  
If the game does decide to affect Sonic's speeds, this also depends on a few factors. If Sonic is on the left and the game has decided he needs to be popped out to the object's left side, it will only stop Sonic's speeds if Sonic is moving right (X Speed > 0), towards the object. The same is true for colliding with the right, but if Sonic is moving to the left (X Speed < 0). Basically, he must be moving towards the object. When his speeds are stopped, X Speed and Ground Speed are set to 0.
+
If the game does decide to affect the Player's speeds, this also depends on a few factors. If the Player is on the left and the game has decided they need to be popped out to the object's left side, it will only stop the Player's speeds if they are is moving right (X Speed > 0), towards the object. The same is true for colliding with the right, but if the Player is moving to the left (X Speed < 0). Basically, he must be moving towards the object. When his speeds are stopped, X Speed and Ground Speed are set to 0.
  
Regardless, '''x distance''' will be subtracted from Sonic's position, popping Sonic out of the object.
+
Regardless, '''x distance''' will be subtracted from the Player's position, popping the Player out of the object.
  
A few other things happen behind the scenes, such as the object is told it is being pushed, and Sonic is told he is pushing.
+
A few other things happen behind the scenes, such as the object is told it is being pushed, and the Player is told they are pushing.
  
 
=====Popped Downwards=====
 
=====Popped Downwards=====
If Sonic bumps the bottom of an object, the game will check if Sonic is moving vertically (Y Speed is not 0). If not, the game then checks if Sonic is standing on the ground, and if he is, kills him from crushing, then exits.  
+
If the Player bumps the bottom of an object, the game will check if the Player is moving vertically (Y Speed is not 0). If not, the game then checks if the Player is standing on the ground, and if they are, kills them from crushing, then exits. This is why you can see secret crushing objects in ceilings, as when the Player touches them while standing on anything he will be crushed as described. Only objects can do this.
  
Otherwise, the game checks if 'Y Speed' is less than 0. If not, it will exit as Sonic is moving down and away from the object.  
+
Otherwise, the game checks if 'Y Speed' is less than 0. If not, it will exit as the Player is moving down and away from the object.  
  
 
Finally, if the '''y distance''' is smaller than 0, the game will subtract '''y distance''' from his Y Position and set his Y Speed to 0.  
 
Finally, if the '''y distance''' is smaller than 0, the game will subtract '''y distance''' from his Y Position and set his Y Speed to 0.  
  
 
=====Popped Upwards=====
 
=====Popped Upwards=====
If the game decides Sonic is to be popped out upwards, Sonic will land on the object.
+
If the game decides the Player is to be popped out upwards, they will land on the object.
  
Before it does this, it checks if '''y distance''' is less than 16. If it is, the game will exit the landing code.
+
Before it does this, it checks if '''y distance''' is larger than or equal than 16. If it is, the game will exit the landing code.
  
 
Then the game subtracts the 4px it added earlier from '''y distance'''.
 
Then the game subtracts the 4px it added earlier from '''y distance'''.
  
Next, it will scrap the '''combined X radius''' we were using before, and find a new X radius, this being the actual X radius of the object, not combined with anything at all. So 16 in the case of a push block for example. It will then compare Sonic's position using this radius.
+
Next, it will completely forget about the '''combined X radius''' we were using before, and use the actual X radius of the object, not combined with anything at all. So 16 in the case of a push block for example. It will then compare the Player's position using this radius.
  
  action_radius = 16 //an example
+
First it will get a distance from the Player's X Position to the object's right edge.
  action_diameter = action_radius*2
 
 
    
 
    
   x_comparison = action_radius plus object's X position
+
   x_comparison = object's X Position + object's X Radius - the Player's X Position
  x_comparison -= sonic's X Position
 
 
    
 
    
   //if sonic is too far to the right
+
Then it will check this comparison to tell if the Player is within the x boundaries.
   if (x_comparison is less than 0)
+
 
  {
+
   // if the Player is too far to the right
    exit;
+
   if (x_comparison is less than 0) exit landing
  }
 
 
    
 
    
   //if sonic is too far to the left
+
   // if the Player is too far to the left
   if (x_comparison is greater than or equal to action_diameter)
+
   if (x_comparison is greater than or equal to action_diameter) exit landing
  {
 
    exit;
 
  }
 
 
    
 
    
This means Sonic will exit the landing and will just slip off the side keep falling if his X Position isn't directly above the object, which is actually quite strange as it's as if Sonic is only 1 pixel thick.
+
This means the Player will exit the landing and will just slip off the side keep falling if their X Position isn't directly above the object, which is actually quite strange as it's as if the Player is only 1 pixel thick. You may wish to keep using the combined radius here instead.
  
The last check is if Sonic's Y Speed is negative, he wont land and it will exit.
+
The last check is if the Player's Y Speed is negative, they wont land and it will exit.
  
Finally, if it reaches this far, he will land. From this point, it's rather simple.
+
Finally, if the code has reached this far, the Player will land. From this point, it's rather simple.
  
The game subtracts '''y distance''' from Sonic's Y Position. It also subtracts an extra 1px afterwards to align him correctly.
+
The game subtracts '''y distance''' from the Player's Y Position. It also subtracts an extra 1px afterwards to align them correctly (which is why that extra 1px was added to the combined_x_radius in the first place!).
  
Then it resets Sonic to be grounded, similarly to normal [[SPG:Solid_Tiles#Reacquisition_Of_The_Ground|Reacquisition Of The Ground]] but simply sets Sonic's Y Speed to 0, and his ''ang'' to 0. Also, the game will set a flag telling the game Sonic is on the object.  
+
Then it resets the Player to be grounded, similarly to normal [[SPG:Solid_Tiles#Reacquisition_Of_The_Ground|Reacquisition Of The Ground]] but simply sets the Player's Y Speed to 0, and his ''ang'' to 0. Also, the game will set a flag telling the game the Player is on the object.  
 
   
 
   
Finally, Sonic's Ground Speed is set to equal his X Speed.
+
Finally, the Player's Ground Speed is set to equal their X Speed.
  
 
====Specifics====
 
====Specifics====
  
As mentioned in [[SPG:Basics|Basics]], Sonic's collisions with tiles and objects only concern themselves with Sonic's floored position (his pixel position), and the same applies to the object itself. So, upon the point of contact, Sonic's floored X Position finds himself overlapping the object. He is then pushed out by this difference. Since this difference only accounts for the distance between floored values, it's a whole number. Meaning if Sonic was 1px inside the object's right side while he has an X Position of 1.75, after being pushed out he'd have an X Position of 2.75, as a rough example.  
+
As mentioned in [[SPG:Basics|Basics]], the Player's collisions with tiles and objects only concern themselves with the Player's floored position (their pixel position), and the same applies to the object itself. So, upon the point of contact, the Player's floored X Position finds itself overlapping the '''combined box'''. He is then pushed out by this difference. Since this difference only accounts for the distance between floored values, it's a whole number. Meaning if the Player was 1px inside the object's right side while he has an X Position of 1.75, after being pushed out he'd have an X Position of 2.75, as a rough example.  
  
So after being popped out, if Sonic keeps trying to walk towards it, he has to cover the rest of the distance of the pixel he's currently in before his pixel position overlaps the object again. This amounts to contact being made every 4 frames or so.
+
So after being popped out, if the Player keeps trying to walk towards it, he has to cover the rest of the distance of the pixel he's currently in before his pixel position overlaps the object again. This amounts to contact being made every 4 frames or so.
  
 
===Standing On Solid Objects===
 
===Standing On Solid Objects===
 
Unlike tiles, which are an organised simple grid of data that can be easily checked each frame, objects are more expensive to check for.  
 
Unlike tiles, which are an organised simple grid of data that can be easily checked each frame, objects are more expensive to check for.  
  
So when standing on top of an object, rather than check beneath Sonic each frame to ensure he's still touching it and to move him with it, the game sets a '''standing-on-object flag''' which will effectively glue Sonic to an object when he lands on it.  
+
So when standing on top of an object, rather than check beneath the Player each frame to ensure he's still touching it and to move him with it, the game sets a '''standing-on-object flag''' which will effectively glue the Player to an object when he lands on it.  
  
The flag's job is making him stick to the object's surface and stay grounded, even though he's not touching any Solid Tiles (as far as his tile sensors are concerned, Sonic is in the air while standing on an object). This flag will only be unset when walking off the edge of an object or jumping/getting hurt.
+
The flag's job is making him stick to the object's surface and stay grounded, even though he's not touching any Solid Tiles (as far as his tile sensors are concerned, the Player is in the air while standing on an object). This flag will only be unset when walking off the edge of an object or jumping/getting hurt.
  
 
====Walking Off The Edges====
 
====Walking Off The Edges====
If Sonic is standing on an object, the game will only check if Sonic has walked off of it.
+
If the Player is standing on an object, the object will only check if the Player has walked off of it.
  
 
First, it calculates a distance to the object's left side.
 
First, it calculates a distance to the object's left side.
  
   x_left_distance = sonic's X Position - the object's X //get the position difference
+
   x_left_distance = (the Player's X Position - the object's X) + combined X radius //get the position difference
  x_left_distance += combined X radius //add the combined X radius
 
 
      
 
      
Sonic will have walked off the edge if this distance is less than 0 or is greater than or equal to ('''combined X radius''' * 2). When this happens, the '''standing-on-object flag''' is unset and Sonic is no longer grounded.
+
The Player will have walked off the edge if this distance is less than 0 or is greater than or equal to ('''combined X radius''' * 2). When this happens, the '''standing-on-object flag''' is unset and the Player is no longer grounded.
  
 
====Moving On Platforms====
 
====Moving On Platforms====
After all checks are complete and if Sonic is still on it, the game handles moving Sonic with the object and keeping him stuck to it.
+
After all checks are complete and if the Player is still on it, the game handles moving the Player with the object and keeping them stuck to it.
 +
 
 +
====Things to Note====
 +
As mentioned when describing hitboxes, they are uneven and odd sized compared to the sprite. Using the method described above - the same is true for solid object boxes, so the Player will push against objects 1px further away when facing leftwards than he will tiles.
  
 
===Bugs Using This Method===
 
===Bugs Using This Method===
Line 290: Line 299:
  
 
====Slipping====
 
====Slipping====
As mentioned, since landing on the top of objects doesn't measure using the same radius as the rest of object collision, bizarrely this means if you jump down towards the corner of an object, you'll slip right off the sides because it exits the landing code if Sonic's position isn't right above the object. This appears to be deliberate as the smaller radius is very explicitly used, but doesn't add any benefit as far as I can tell.
+
As mentioned, since landing on the top of objects doesn't measure using the same radius as the rest of object collision, bizarrely this means if you jump down towards the corner of an object, you'll slip right off the sides because it exits the landing code if the Player's position isn't right above the object. This appears to be deliberate as the smaller radius is very explicitly used, but doesn't add any benefit as far as I can tell.
  
 
[[Image:SPGObjectBugSlipping2.gif]]
 
[[Image:SPGObjectBugSlipping2.gif]]
  
The way the object collision code is executed, being from inside each object in order, there's effectively a priority system in place. If two objects want to push Sonic two conflicting ways, the one who executes their solid object code last will win out. The result of this, and partly thanks to the edge slipping mentioned above, Sonic can very easily slip between two objects which haven't been placed perfectly touching next to each other.
+
The way the object collision code is executed, being from inside each object in order, there's effectively a priority system in place. If two objects want to push the Player two conflicting ways, the one who executes their solid object code last will win out. The result of this, and partly thanks to the edge slipping mentioned above, the Player can very easily slip between two objects which haven't been placed perfectly touching next to each other.
  
 
[[Image:SPGObjectBugSlipping1.gif]]
 
[[Image:SPGObjectBugSlipping1.gif]]
  
Sonic will collide on top with both spikes, but his position isn't directly over either of them, so he will slip down the sides. Next, both spikes will try and push him with their sides, but only the last spike to do so will actually result in a net position change.
+
The Player will collide on top with both spikes, but his position isn't directly over either of them when landing, so he will slip down the sides. Next, both spikes will try and push him with their sides, but only the last spike to do so will actually result in a net position change.
  
====Bottom Overalap====
+
====Bottom Overlap====
When the vertical overlap is being checked, the game pretends Sonic is 4px lower than he actually is. This allows 4px of extra "grip" to the top of objects, however  it also effectively removes 4px from underneath them. When jumping up into an object, Sonic will be able to enter it by around 4px before being popped out. Though, this is hard to notice during normal gameplay.
+
When the vertical overlap is being checked, the game pretends the Player is 4px lower than they actually are. This allows 4px of extra "grip" to the top of objects, however  it also effectively removes 4px from underneath them. When jumping up into an object, the Player will be able to enter it by around 4px before being popped out. Though, this is hard to notice during normal gameplay.
  
 
[[Image:SPGObjectBugBottom.gif]]
 
[[Image:SPGObjectBugBottom.gif]]
 +
 +
This can be corrected by accounting for the added 4px when checking overlap and calculating distances with the bottom of the object.
  
 
====False Object Standing Flag====
 
====False Object Standing Flag====
 
This final bug is less of a design flaw and more of a major bug.
 
This final bug is less of a design flaw and more of a major bug.
  
If for some reason the object you are standing on is deleted or otherwise unloaded, and the game fails to reset the '''standing-on-object flag''' you can then start walking through the air. This is because the flag is telling the game that Sonic is still grounded even though there's no longer any object to be grounded to. Because Sonic's grounded, he won't fall. Additionally, he also won't be able to walk off the object's sides as the object isn't even there to check for it.
+
If for some reason the object you are standing on is deleted or otherwise unloaded, and the game fails to reset the '''standing-on-object flag''' you can then start walking through the air. This is because the flag is telling the game that the Player is still grounded even though there's no longer any object to be grounded to. Because the Player's grounded, he won't fall. Additionally, they also won't be able to walk off the object's sides as the object isn't even there to check for it.
  
 
==Object Specific Collision==
 
==Object Specific Collision==
Line 316: Line 327:
 
===Objects That Collide===
 
===Objects That Collide===
  
Some objects like walking enemies, pushable blocks, and item monitors all have to land on and stick to solid ground. They typically do this by casting a single downward sensor, much like Sonic does, at their central bottom point. The same applies to wall collision.
+
Some objects like walking enemies, pushable blocks, and item monitors all have to land on and stick to solid ground. They typically do this by casting a single downward sensor, much like the Player does, at their central bottom point. The same applies to wall collision. The way that objects use tile collision varies greatly and will be described for each individual Game Object.
  
 
===Sloped Objects===
 
===Sloped Objects===
Line 326: Line 337:
 
[[Image:SPGSlopedObjects.png]]
 
[[Image:SPGSlopedObjects.png]]
  
This height array is relative to the object's Y Position, and is centred on it's X Position. The game stores these height arrays compressed at half the size, as shown above. This is possible because the slopes never need to be steeper than a step of 2 pixels, so the game simply "stretches out" the array information when the array is read.
+
The array for these objects are
 +
  32 32 32 32 32 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 48 48 48 48 48
 +
and
 +
  32 32 32 32 32 32 32 32 32 32 32 32 32 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 32 32 32 32 32 32 32 32 32 32 32 32 32
 +
 
 +
This height array is relative to the object's Y Position, and is centred on it's X Position. Each value is the distance from the Y Position to the object's surface. Effectively a "fake" Height Radius for the top surface at every x position along the object.
 +
 
 +
The game stores these height arrays compressed at half the size, as shown above. This is possible because the slopes never need to be steeper than a step of 2 pixels, so the game simply "stretches out" the array information when the array is read. There are exceptions to this however, if the arrays step up in 2s (like 12 14 16) when stretched out the game will interpolate between these values to a 45 degree slope. This happens for the diagonal springs, for example.
 +
 
 +
When a sloped object is acting solid to the Player, instead of using the y position of the surface of the solid box using the object's Height Radius, it instead reads the value from the array, and from there on as far as the Player is concerned, the object's Height Radius is as high as the array tells it. This continuously happens as the Player passes over the object, resulting in smooth motion.
  
When a sloped object is acting solid to sonic, instead of using the y position of the top of the solid box, it instead reads the value from the array, and from there as far as the game is concerned, the object is as high as the array tells it. This continuously happens as Sonic passes over the object, resulting in smooth motion.
+
As you can see there is some extra information to the sides, this is simply because the Player can be standing on an object while their X Position isn't directly over it. This could be solved by just using the first or last height array value if Sonic's X Position is outside of the object boundary.
  
 
====Differences To Tiles====
 
====Differences To Tiles====
  
There are no real angle values because the array is height information only, and no sensors are being used here. This means that Sonic will have an angle value of 0 as he walks on a sloped object, and won't jump off or be affected by slope physics at all. In addition, Sonic will be slightly "deeper" into the slopes than he would on solid tiles. This is because his centre point is always snapped to the slope, rather than one of his side floor sensors. It's most likely for these reasons that objects do not have angles steeper than what is shown above.
+
There are no real angle values because the array is height information only, and no sensors are being used here. This means that the Player will have an angle value of 0 as he walks on a sloped object, and won't jump off or be affected by slope physics at all. In addition, the Player will be slightly "deeper" into the slopes than they would on solid tiles. This is because his centre point is always snapped to the slope, rather than one of their side floor sensors. It's most likely for these reasons that objects do not have angles steeper than what is shown above.
  
 
===Jump Through Platforms===
 
===Jump Through Platforms===
  
Jump through platforms are small objects which are only solid from the top. Since all Sonic can do with platforms is land on them, they use their own code to check for just that, and in a more scrutinised way.
+
Jump through platforms are small objects which are only solid from the top. Since all the Player can do with platforms is land on them, they use their own code to check for just that, and in a more scrutinised way.
  
First, it will check if Sonic's Y Speed is less than 0. If it is, it will exit the collision. This means it will only check for overlap with Sonic while he is moving down or staying still. This is why Sonic can jump right up through it.
+
First, it will check if the Player 's Y Speed is less than 0. If it is, it will exit the collision. This means it will only check for overlap with the Player while they are moving down or staying still. This is why the Player can jump right up through it.
  
 
====Horizontal Overlap====
 
====Horizontal Overlap====
Line 349: Line 369:
 
The game calculates the platform's surface Y coordinate by subtracting the Height Radius from the Y Position.
 
The game calculates the platform's surface Y coordinate by subtracting the Height Radius from the Y Position.
  
Then Sonic's bottom Y is calculated by adding his Height Radius to his Y Position. It also adds 4 to this bottom Y for much the same reason as the normal solid object collision, it allows Sonic to collide even when he's 4 pixels above.
+
Then the Player's bottom Y is calculated by adding their Height Radius to their Y Position. It also adds 4 to this bottom Y for much the same reason as the normal solid object collision, it allows the Player to collide even when they're 4 pixels above.
  
The first check is if the platform's surface Y is greater than Sonic's bottom Y. If it is, it will exit as the platform is too low.
+
The first check is if the platform's surface Y is greater than the Player's bottom Y. If it is, it will exit as the platform is too low.
  
Next, it will check a distance between Sonic's bottom and the platform's surface (platform's surface Y minus Sonic's bottom Y). If the distance is less than -16 or is greater than or equal to 0, it will exit as Sonic is too low.
+
Next, it will check a distance between the Player's bottom and the platform's surface (platform's surface Y minus the Player's bottom Y). If the distance is less than -16 or is greater than or equal to 0, it will exit as the Player is too low.
  
If it reaches past all those checks, Sonic will land.
+
If it reaches past all those checks, the Player will land.
  
====Popping Sonic Out====
+
====Popping The Player Out====
  
The distance from before is added to Sonic's Y Position, plus an extra 3px. After this the normal landing-on-object things occur, such as setting his speeds and '''standing-on-object flag'''.
+
The distance from before is added to the Player's Y Position, plus an extra 3px. After this the normal landing-on-object things occur, such as setting his speeds and '''standing-on-object flag'''.
  
 
====Walking Off Edges====
 
====Walking Off Edges====
  
Platforms also use a different walking off edges code to normal Solid Objects. And since it's up to objects what width radius they want to use, things can get a little inconsistent. It's mentioned above that objects add Sonic's radius to get a combined radius. This actually isn't always the case. Sometimes objects will just provide their unaltered width radius which is the case with platforms. This means not only will Sonic fall through the corners of platforms like any other object, but he will also walk off them just as easily, way sooner than he really should, unlike the normal object collision.
+
Platforms also use a different walking off edges code to normal Solid Objects. And since it's up to objects what width radius they want to use, things can get a little inconsistent. It's mentioned above that objects add the Player's radius to get a combined radius. This actually isn't always the case. Sometimes objects will just provide their unaltered width radius which is the case with most platforms. This means not only will the Player fall through the corners of platforms like any other object, but he will also walk off them just as easily, way sooner than he really should as if they are only 1px in total width, unlike the normal object collision.
  
This was probably missed because Sonic doesn't need to push against these platforms, so it's much harder to notice if Sonic's Push Radius hasn't been applied.  
+
This was probably missed because the Player doesn't need to push against these platforms, so it's much harder to notice if the Player's Push Radius hasn't been applied.  
  
After this of course, Sonic is still standing on it, so the game handles updating Sonic's position on the object and moving him if the object is moving.
+
After this of course, the Player is still standing on it, so the game handles updating the Player's position on the object and moving him if the object is moving.
  
 
Worthy of note, is that many objects share the platform's "walking off edges" code.
 
Worthy of note, is that many objects share the platform's "walking off edges" code.
  
Sloped objects, like those that fall in Green Hill, and these platforms also provide the actual X radius rather than a combined one. There's also a likely reason for this too.
 
  
These have slope data, which is a height array, and the value returned from this is based on Sonic's X Position. So Sonic stands on them, and his current height on the platform is decided based on his X Position relative to the object. If Sonic's Push Radius was added to the x radius used for Sonic to walk off the edges, and if Sonic could then hang off the edges slightly, the current height of the object's slope would be invalid causing odd results like Sonic being sent somewhere weird vertically due to an invalid height being read. This could be solved by just using the first or last height array value if Sonic's X Position is outside of the platform boundary.
 
  
 
''Note: The code itself isn't the issue, the issue is moreso that the objects can far more easily pass in a radius that isn't combined when they use this because the general solid object code also uses the radius for pushing and for walking off, which requires it to be combined.''
 
''Note: The code itself isn't the issue, the issue is moreso that the objects can far more easily pass in a radius that isn't combined when they use this because the general solid object code also uses the radius for pushing and for walking off, which requires it to be combined.''
Line 379: Line 397:
 
===Pushable Blocks===
 
===Pushable Blocks===
  
Pushable blocks (specifically the type found in Marble Zone) are essentially normal solid objects, except for the fact when you are pushing them move. They move rather slowly, and you might assume that it sets the block and Sonic's speeds to some value like 0.3, but this is not the case.
+
Pushable blocks (specifically the type found in Marble Zone) are essentially normal solid objects, except for the fact when you are pushing them move. They move rather slowly, and you might assume that it sets the block and the Player's speeds to some value like 0.3, but this is not the case.
  
 
The block actually moves 1 entire pixel whenever you touch it from the side. But that sounds much faster than they actually move right? Well, in practice the block will only move once around every 3 frames. And the reason for this is rather technical to say the least and requires that you properly emulate the way the original game's positions work.
 
The block actually moves 1 entire pixel whenever you touch it from the side. But that sounds much faster than they actually move right? Well, in practice the block will only move once around every 3 frames. And the reason for this is rather technical to say the least and requires that you properly emulate the way the original game's positions work.
  
 
====Upon Contact====
 
====Upon Contact====
When Sonic has contacted the push block, Sonic has been popped out, and his speeds have been set to 0, the push block will then do some extra things. If Sonic pushed to the left, both Sonic and the block will move 1 pixel to the left, Sonic's X Speed is set to 0 and Ground Speed is set to -0.25. If he pushed to the right, both Sonic and the block will move 1 pixel to the right, Sonic's X Speed is set to 0 and Ground Speed is set to 0.25.
+
When the Player has contacted the push block, the Player has been popped out, and his speeds have been set to 0, the push block will then do some extra things. If the Player pushed to the left, both the Player and the block will move 1 pixel to the left, the Player's X Speed is set to 0 and Ground Speed is set to -0.25. If they pushed to the right, both the Player and the block will move 1 pixel to the right, the Player's X Speed is set to 0 and Ground Speed is set to 0.25.
  
After being popped out Sonic is no longer touching the object. When this happens, Sonic's pixel position has been altered, but his subpixel position remains the same. So if Sonic was half a pixel into the object before, he's now half a pixel outside of it. Before he makes contact with the object again, he needs to cover this subpixel distance. This would normally take around 4 frames for a static wall, but here it instead takes 2-3 frames because he is given a headstart when his Ground Speed is set to .25.
+
After being popped out the Player is no longer touching the object. When this happens, the Player's pixel position has been altered, but their subpixel position remains the same. So if the Player was half a pixel into the object before, they're now half a pixel outside of it. Before they make contact with the object again, they needs to cover this subpixel distance. This would normally take around 4 frames for a static wall, but here it instead takes 2-3 frames because they are given a headstart when their Ground Speed is set to .25.
  
 
Because the mechanics of movement within 256 subpixels are difficult to explain or visually demonstrate, here's what a few frames of pushing a pushable block to the right would look like:
 
Because the mechanics of movement within 256 subpixels are difficult to explain or visually demonstrate, here's what a few frames of pushing a pushable block to the right would look like:
  
 
   Frame 0:
 
   Frame 0:
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
     X Position: 2669.97265625 -- added X Speed to X Position. Sonic's subpixel position (.972) is very close to entering the next pixel, which is where he will collide again.
+
     X Position: 2669.97265625 -- added X Speed to X Position. the Player's subpixel position (.972) is very close to entering the next pixel, which is where he will collide again.
 
      
 
      
 
   Frame 1:
 
   Frame 1:
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.390625 -- added acc to Ground Speed
 
     Ground Speed: 0.390625 -- added acc to Ground Speed
 
     X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
     X Position: 2670.36328125 -- added X Speed to X Position. Sonic's X pixel has changed
+
     X Position: 2670.36328125 -- added X Speed to X Position. the Player's X pixel has changed
 
      
 
      
     -- Sonic makes contact with push block and is popped out to the left.
+
     -- the Player makes contact with push block and is popped out to the left.
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     X Speed: 0 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0 -- X Speed set to Ground Speed (on flat ground)
 
     X Position: 2669.36328125 -- 1 subtracted from X Position
 
     X Position: 2669.36328125 -- 1 subtracted from X Position
 
      
 
      
     -- The push block runs its own code and both are moved to the right by 1 pixel, and Sonic's Ground Speed is set.
+
     -- The push block runs its own code and both are moved to the right by 1 pixel, and the Player's Ground Speed is set.
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
 
     X Speed: 0 -- X Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Position: 2670.36328125 -- 1 added to X Position
 
     X Position: 2670.36328125 -- 1 added to X Position
  
At this point, Sonic has just pushed the block and has been moved out of it, then along with it. The fractional part of his position is currently .363 , just left of halfway through the pixel.
+
At this point, the Player has just pushed the block and has been moved out of it, then along with it. The fractional part of their position is currently .363 , just left of halfway through the pixel.
  
 
   Frame 2 (1 frame since last push):
 
   Frame 2 (1 frame since last push):
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.296875 -- added acc to Ground Speed
 
     Ground Speed: 0.296875 -- added acc to Ground Speed
 
     X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
Line 421: Line 439:
 
    
 
    
 
   Frame 3 (2 frames since last push):
 
   Frame 3 (2 frames since last push):
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
     X Position: 2671.00390625 -- added X Speed to X Position. Sonic's X pixel has changed
+
     X Position: 2671.00390625 -- added X Speed to X Position. the Player's X pixel has changed
 
      
 
      
     -- Sonic makes contact with push block and is popped out to the left.
+
     -- the Player makes contact with push block and is popped out to the left.
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Position: 2670.00390625 -- 1 subtracted from X Position
 
     X Position: 2670.00390625 -- 1 subtracted from X Position
 
      
 
      
     -- Sonic makes contact with push block and both are moved to the right by 1 pixel.
+
     -- the Player makes contact with push block and both are moved to the right by 1 pixel.
     -- This only took 2 frames, because Sonic's subpixel was positioned just right on the previous push, which is very rare.
+
     -- This only took 2 frames, because the Player's subpixel was positioned just right on the previous push, which is very rare.
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
 
     X Speed: 0 -- X Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Position: 2671.00390625 -- 1 added to X Position
 
     X Position: 2671.00390625 -- 1 added to X Position
  
Sonic has just pushed the block again, and has been moved out of it, then along with it. It took 2 frames. This time, the fractional part of his position is currently .003 , the very left of the pixel. This means he has farther to travel to reach the block again.
+
The Player has just pushed the block again, and has been moved out of it, then along with it. It took 2 frames. This time, the fractional part of their position is currently .003 , the very left of the pixel. This means they have farther to travel to reach the block again.
  
 
   Frame 4 (1 frame since last push):
 
   Frame 4 (1 frame since last push):
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.296875 -- added acc to Ground Speed
 
     Ground Speed: 0.296875 -- added acc to Ground Speed
 
     X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
Line 446: Line 464:
 
    
 
    
 
   Frame 5 (2 frames since last push):
 
   Frame 5 (2 frames since last push):
     -- Sonic gains speed along the floor naturally
+
     -- the Player gains speed along the floor naturally
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     Ground Speed: 0.34375 -- added acc to Ground Speed
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
Line 452: Line 470:
 
    
 
    
 
   Frame 6 (3 frames since last push):
 
   Frame 6 (3 frames since last push):
     -- Sonic gains speed along the floor naturally and moves his position
+
     -- the Player gains speed along the floor naturally and moves his position
 
     Ground Speed: 0.390625 -- added acc to Ground Speed
 
     Ground Speed: 0.390625 -- added acc to Ground Speed
 
     X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
 
     X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
     X Position: 2672.03515625 -- added X Speed to X Position. Sonic's X pixel has changed
+
     X Position: 2672.03515625 -- added X Speed to X Position. the Player's X pixel has changed
 
      
 
      
     -- Sonic makes contact with push block and is popped out to the left.
+
     -- the Player makes contact with push block and is popped out to the left.
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     Ground Speed: 0 -- Ground Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Speed: 0 -- X Speed set to 0
 
     X Position: 2671.03515625 -- 1 subtracted from X Position
 
     X Position: 2671.03515625 -- 1 subtracted from X Position
  
     -- Sonic makes contact with push block and both are moved to the right by 1 pixel.
+
     -- the Player makes contact with push block and both are moved to the right by 1 pixel.
 
     -- This time, it took 3 frames, which is far more common.
 
     -- This time, it took 3 frames, which is far more common.
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
 
     Ground Speed: 0.25 -- Ground Speed set to 0.25
Line 468: Line 486:
 
     X Position: 2672.03515625 -- 1 added to X Position
 
     X Position: 2672.03515625 -- 1 added to X Position
  
Sonic has just pushed the block again, and has been moved out of it, then along with it. This time it took 3 frames thanks to his subpixel/fractional positions being allowed to wrap around and never reset. This 3 frame delay is the most common and is effectively the push speed.
+
The Player has just pushed the block again, and has been moved out of it, then along with it. This time it took 3 frames thanks to his subpixel/fractional positions being allowed to wrap around and never reset. This 3 frame delay is the most common and is effectively the push speed.
  
 
The reverse would have the exact same timings. It seems they deliberately controlled this delay by adding .25 to his Ground Speed.  
 
The reverse would have the exact same timings. It seems they deliberately controlled this delay by adding .25 to his Ground Speed.  
  
If you simply want to ''roughly'' copy it without the specifics or nuances of this system or you are using different object collision, just make a timer which triggers a movement every 3 frames while Sonic is pushing.
+
If you simply want to ''roughly'' copy it without the specifics or nuances of this system or you are using different object collision, just make a timer which triggers a movement every 3 frames while the Player is pushing.
  
 
To see what happens to a push block once it is pushed off a ledge, see [[SPG:Game_Objects#Pushable_Blocks|Game Objects]].
 
To see what happens to a push block once it is pushed off a ledge, see [[SPG:Game_Objects#Pushable_Blocks|Game Objects]].
Line 478: Line 496:
 
===Item Monitor===
 
===Item Monitor===
  
Item Monitors, as you may have noticed, are not always solid. While you can stand on them you can also go right through them while jumping or rolling. The game is actually checking what Sonic is doing and changing how the Item Monitor will react.
+
Item Monitors, as you may have noticed, are not always solid. While you can stand on them you can also go right through them while jumping or rolling. The game is actually checking what the Player is doing and changing how the Item Monitor will react.
  
 
While not curled up in a ball, the Item Monitor acts as solid. The Item Monitor's hitbox isn't accessible at this time.
 
While not curled up in a ball, the Item Monitor acts as solid. The Item Monitor's hitbox isn't accessible at this time.
  
While curled, however, the item box will no longer have any solidity at all, and its hitbox is active and accessible. The hitbox collision is what destroys the Item Box and bounces Sonic off (Details in [[SPG:Rebound#Monitors|Monitors Rebound]]).
+
While curled, however, the item box will no longer have any solidity at all, and its hitbox is active and accessible. The hitbox collision is what destroys the Item Box and bounces the Player off (Details in [[SPG:Rebound#Monitors|Monitors Rebound]]).
  
However, there is an exception. If Sonic is moving up (Y Speed < 0) while curled, the Item Box will in fact still act solid. Additionally, if Sonic's Y Position-16 is smaller than the item box's Y Position, the item box will bounce up with a Y Speed of -1.5 knocking the Item Box upwards, and Sonic's Y Speed will be reversed.
+
However, there is an exception. If the Player is moving up (Y Speed < 0) while curled, the Item Box will in fact still act solid. Additionally, if the Player's Y Position-16 is smaller than the item box's Y Position, the item box will bounce up with a Y Speed of -1.5 knocking the Item Box upwards, and the Player's Y Speed will be reversed.
  
 
[[Category:Sonic Physics Guide|Solid Objects]]
 
[[Category:Sonic Physics Guide|Solid Objects]]

Revision as of 14:41, 2 April 2021

Notes:

  • Research applies to all four of the Mega Drive games, and Sonic CD. If there are any varying differences between the games, this will be covered below.
  • Variables and constants for Sonic and other characters such as X Position and acc will be referenced frequently, they can be found in Basics.

Introduction

There are many objects in Sonic games and they interact with the Player in many different ways and are a very different beast than Solid Tiles.

There are 2 main ways objects interact with the Player. Hitboxes like rings and checkpoints, and solidity like item boxes or push blocks. Yes, both of these are completely seperate.

We'll go over hitbox interactions first.

Object Hitboxes

Objects such as rings, enemies, and bumpers have a hitbox, which is a general area that will trigger some kind of reaction with the Player when both their hitboxes overlap. Object hitboxes are centered on the object's X and Y Positions.

Note:

  • Sometimes objects which seem solid (like bosses or bumpers) actually only have a hitbox, and when they overlap, simply push the Player in the other direction. As a general rule, any seemingly solid object that the Player cannot stand on is most likely actually using a hitbox rather than real solidity.

Hitbox Reaction

If the Player's hitbox touches an object's hitbox, some kind of reaction will occur. Usually this is totally specific to the object, like collecting a ring or bursting a balloon. Though, the object can set specific flags which change the "type" of reaction they will have. The two most consistent reaction types are as follows:

Hurt Hitboxes

While these aren't solid, any contact with them will immediately give damage to the Player. Examples are the spikes on the GHZ log bridge, the spikes under a MZ Spike Trap, and certain projectiles.

Badnik Hitboxes

These are very similar to hurt hitboxes, with the obvious difference being that while rolling, you won't take damage and will instead destroy the enemy.

Ahead, objects with hurt hitboxes will be coloured differently.

The Player's Hitbox

In order to interact with other object's hitboxes, the Player needs their own.

SPGHitBoxes.png The hitbox for Sonic.

The Player's hitbox is much like that of any other object. It sits at the their X Position and Y Position. It has a width radius of 8, and its height radius is always 3 pixels shorter than the Player's Height Radius, making it 17 X 33 pixels in size while standing.

When crouching, obviously the Player's hitbox needs to shrink. Problem is, the Player's position and Height Radius don't actually change at all while crouching. So to tackle this the game manually updates the hitbox's size and position while the Player crouches, where 12px is added to the hitbox's Y position, and the hitbox's height radius is set to 10.

Quirks With Hitboxes

Because these hitboxes aren't even numbered in size, and because object origins don't lay perfectly centred between pixels (see Basics) most hitboxes will also appear 1px too big on the right side and the bottom side. This is simply how things work in-game and for that reason won't be ignored. Sprites like rings are even-numbered sizes (such as 16 X 16) so an anomaly like the following can (and does, always) occur.

SPGRingTest.gif

Rings can be collected from one direction sooner than the other, you can try it yourself via debug mode. As long as the sprite of the ring is 4px out from the tiles on each side, you'll experience this inconsistency. A Ring's hitbox is defined as a radius of 6, but this results in a box with a width of 13 rather than 12. Because all sprites like this are an even size, but their hitbox must be odd, the box cannot be perfectly set on the sprite and will be larger to the left and bottom.

This is the case with any object's hitboxes.


Solid Objects

Object-player collision doesn't work the same way as Solid Tiles. The Player does not collide with objects using his solid tile sensors, instead, special calculations are used to check if the Player's general shape is inside an object's solid box, and push him out.

This all occurs after the Player's code has been executed for that frame, including their tile collision and movement. Since objects run their code after the Player, it's the job of the objects to push the Player out of themselves. Like say the Player is running towards a solid block object with some medium speed. When their position changes at the end of their code, they will move inside the solid object. Then soon afterwards on the same frame the solid object runs its code, checks for the Player and acts solid accordingly, pushing the Player out.

General Solid Object Collision

Solid object collision does not involve the object hitboxes and instead uses the actual size of the objects. The Width Radius and Height Radius. The Player will use their Height Radius for this too, but horizontally they of course use their Push Radius instead.

The following is long. It is written in a way very close to how the original game has it coded because accuracy requires it. To orient yourself, a brief overview of the long process below goes as follows:

  • The Player will check if he is overlapping the object.
  • The Player will decide which side of the object he is nearest to n both axis (either left or right and either top or bottom).
  • Then check how close in pixels he is to being outside of the object on that side (distance to left or right and distance to top or bottom).
  • The game then decides whether he's closer to a horizontal side to be pushed out on the x axis or a vertical side to be pushed out on y axis.
  • He will then be pushed out towards that side on that axis by the distance he overlaps.

Now, let's get into the details.

The first thing the object collision code does is check if the Player is standing on the object. The Player has a flag which determines if they are standing an object, which is set upon landing on one. If they are, it will skip straight to checking if the Player has walked off the edges rather than general object collision (which we will go into detail about further down in Standing On Solid Objects). Otherwise, it will continue as follows.


Checking For An Overlap

First thing that needs to happen is the game needs to know if the Player is even touching the object to begin with.

Both the Player and the solid object are of course rectangles, but it would be costly to check if 2 rectangles overlap each frame. Instead, a lot of calculations are saved because checks if a single position (the Player's position) is within one rectangle. This is achieved by combining the Player's current Push and Height Radius values with the object's Width and Height Radius values to form this new rectangle.

Horizontally, the object combines its own Width Radius with the Player's Push Radius and adds 1px extra (so Push Radius + 1). The extra pixel is added because the final position the Player pushes at is the Players Push Radius + 1 away from the object's side edge.

Vertically, it very similar. The object combines its own Height Radius with the Player's current Height Radius to get a combined radius. 1px isn't added here, but it is (kind of) later after a collision has occurred.

Here's a demonstration of how these new radiuses relate to the Player's size (while standing in this case) for a block.

SPGSolidObjectOverlap.gif

From this point, when I refer to the object's combined radiuses I will call them combined X radius and combined Y radius, and I will refer to the entire box as the combined box. I will also refer to combined X diameter (which is combined X radius * 2) and combined Y diameter (which is combined Y radius * 2).

Now all the game needs to worry about is the Player's X Position and Y Position being within this new combined box, it no longer needs to worry about what the Player's sizes are at all.


Horizontal Overlap

The game will calculate the difference between the Player's X Position and the left edge of the combined box.

 left_difference = (the Player's X Position - object's X Position) + combined_x_radius

Then, it will check if this new difference value has passed the left or right boundaries of the combined box, and exit the object collision if it has.

 // the Player is too far to the left to be touching?
 if (left_difference < 0) exit object collision
 // the Player is too far to the right to be touching?
 if (left_difference > combined_x_diameter) exit object collision

If no exit occurred, the Player is overlapping on the X axis, and it will continue. The game will remember this left difference.


Vertical Overlap

Then for vertical overlap, it calculates the difference between the Player's Y Position and the top edge of the combined box.

 top_difference = (the Player's Y Position - object's Y Position) + 4 + combined_y_radius

The game also allows the Player to be slightly above the object by 4 pixels and still overlap, extending the top of the object 4 pixels for extra overlap. This is likely just in case the object moves down slightly or the object is slightly lower than a previous ledge the Player was standing on. The game does this by effectively pretending the Player is 4px lower than they really are when checking the y overlap. If the object is lower than the Player, top_difference would be negative before combined Y radius is added, so it is achieved by simply adding 4 to the distance. This is subtracted later.

Then, it will check if this new difference value has passed the top or bottom boundaries of the combined box, and exit the object collision if it has.

 // the Player is too far above to be touching
 if (top_difference < 0) exit object collision
 
 // the Player is too far down to be touching
 if (top_difference > combined_y_diameter) exit object collision

If no exit occurred, the Player is overlapping on the y axis, and it will continue to the next step. The game will remember this top difference.

The reason the game does it in this fashion rather than just checking between -radius and +radius for example is to preserve calculations needed. It has been done in such a way that it now has 2 variables it can keep using, left_difference and top_difference.


Finding The Direction of Collision

If the Player is found to be touching the object, the game will then decide whether they are to be popped out the top or bottom, or the left or right of the object. The game will compare the Player's position to the object's position to determine which side they are on.

To do this, the game will first determine which side the Player is in comparison with the object's position.

If the Player's X Position is greater than the object's X position, they're on the right, otherwise, they're on the left. If the Player's Y Position is greater than the object's Y position, they're on the bottom, otherwise, they're on the top.

After the side is determined for each axis, the game will calculate a distance to the nearest edge.


Horizontal Edge Distance

If the Player is on the left, the edge distance is simply equal to the left difference which is the distance to the left side of the combined box and will be a positive number.

 x_distance = left_difference

If the Player is on the right, the distance will be flipped around like so:

 x_distance = left_difference - object_x_diameter
 

This is effectively the distance to the right side of the combined box and will be a negative number.

Whichever side it is, we will call this new distance the x distance, and the game knows which side, left or right, the Player is based on the sign (+/-) of this value.

Vertical Edge Distance

It is the same along the y axis. If the Player is on the top, the edge distance is simply equal to the top difference which is the distance to the top side of the combined box and will be a positive number.

 y_distance = top_distance

If the Player is on the bottom, the distance will be flipped around (and that extra 4px from before will be subtracted).

 y_distance = top_difference - 4 - object_y_diameter
 

This is effectively the distance to the bottom side of the combined box and will be a negative number.

Whichever side it is, we will call this the y distance, and the game knows which side, top or bottom, the Player is based on the sign (+/-) of this value.

Note: You may have noticed that if the Player is on the top, the extra 4px isn't subtracted yet. It will be subtracted upon landing on top.


Choosing The Direction

Finally, with all of this information, the game can decide which way the Player should be popped out. Either vertically or horizontally.

It does this by finding which side the Player is nearer to, which makes sense.

 if (absolute(x distance) > absolute(y distance))
 {
   collide vertically
 }
 else
 {
   collide horizontally
 }
 

Here's a visual example of what axis Sonic would collide depending on his X Position and Y Position within the solid area of a block.

SPGSolidObjectNearerSide.png

The horizontal axis is favoured just a little more than the vertical, which is simply due to Sonic's Width and Height Radius not being square. Keep in mind this exact pattern is only valid for an object of this exact size and while Sonic is standing.

From there, the game can easily tell which way to pop out the Player on either axis depending on the sign (+/-) of the distance value. When colliding vertically, the game knows that Player is on top if the y distance is positive, and underneath if the y distance is negative. Same goes for left and right and the x distance.


Popping The Player Out

Once a collision has occurred and the game had decided the direction the Player then needs to be "popped out" of the combined box so that their position is no longer within it. But where does it put the Player? Well, there's also an even greater use for the x distance or y distance. They are the exact distance the Player needs to move to exit the object, but reversed. So when they are popped out, they will simply be subtracted from their position.

Popped Left and Right

Popping the Player out left or right will simply reset his speeds and position, and set him to pushing if he is grounded.

There are a couple of conditions. The game will only bother popping the Player out horizontally if absolute y distance is greater than 4. If not, it will exit.

If the game does decide to affect the Player's speeds, this also depends on a few factors. If the Player is on the left and the game has decided they need to be popped out to the object's left side, it will only stop the Player's speeds if they are is moving right (X Speed > 0), towards the object. The same is true for colliding with the right, but if the Player is moving to the left (X Speed < 0). Basically, he must be moving towards the object. When his speeds are stopped, X Speed and Ground Speed are set to 0.

Regardless, x distance will be subtracted from the Player's position, popping the Player out of the object.

A few other things happen behind the scenes, such as the object is told it is being pushed, and the Player is told they are pushing.

Popped Downwards

If the Player bumps the bottom of an object, the game will check if the Player is moving vertically (Y Speed is not 0). If not, the game then checks if the Player is standing on the ground, and if they are, kills them from crushing, then exits. This is why you can see secret crushing objects in ceilings, as when the Player touches them while standing on anything he will be crushed as described. Only objects can do this.

Otherwise, the game checks if 'Y Speed' is less than 0. If not, it will exit as the Player is moving down and away from the object.

Finally, if the y distance is smaller than 0, the game will subtract y distance from his Y Position and set his Y Speed to 0.

Popped Upwards

If the game decides the Player is to be popped out upwards, they will land on the object.

Before it does this, it checks if y distance is larger than or equal than 16. If it is, the game will exit the landing code.

Then the game subtracts the 4px it added earlier from y distance.

Next, it will completely forget about the combined X radius we were using before, and use the actual X radius of the object, not combined with anything at all. So 16 in the case of a push block for example. It will then compare the Player's position using this radius.

First it will get a distance from the Player's X Position to the object's right edge.

 x_comparison = object's X Position + object's X Radius - the Player's X Position
 

Then it will check this comparison to tell if the Player is within the x boundaries.

 // if the Player is too far to the right
 if (x_comparison is less than 0) exit landing
 
 // if the Player is too far to the left
 if (x_comparison is greater than or equal to action_diameter) exit landing
 

This means the Player will exit the landing and will just slip off the side keep falling if their X Position isn't directly above the object, which is actually quite strange as it's as if the Player is only 1 pixel thick. You may wish to keep using the combined radius here instead.

The last check is if the Player's Y Speed is negative, they wont land and it will exit.

Finally, if the code has reached this far, the Player will land. From this point, it's rather simple.

The game subtracts y distance from the Player's Y Position. It also subtracts an extra 1px afterwards to align them correctly (which is why that extra 1px was added to the combined_x_radius in the first place!).

Then it resets the Player to be grounded, similarly to normal Reacquisition Of The Ground but simply sets the Player's Y Speed to 0, and his ang to 0. Also, the game will set a flag telling the game the Player is on the object.

Finally, the Player's Ground Speed is set to equal their X Speed.

Specifics

As mentioned in Basics, the Player's collisions with tiles and objects only concern themselves with the Player's floored position (their pixel position), and the same applies to the object itself. So, upon the point of contact, the Player's floored X Position finds itself overlapping the combined box. He is then pushed out by this difference. Since this difference only accounts for the distance between floored values, it's a whole number. Meaning if the Player was 1px inside the object's right side while he has an X Position of 1.75, after being pushed out he'd have an X Position of 2.75, as a rough example.

So after being popped out, if the Player keeps trying to walk towards it, he has to cover the rest of the distance of the pixel he's currently in before his pixel position overlaps the object again. This amounts to contact being made every 4 frames or so.

Standing On Solid Objects

Unlike tiles, which are an organised simple grid of data that can be easily checked each frame, objects are more expensive to check for.

So when standing on top of an object, rather than check beneath the Player each frame to ensure he's still touching it and to move him with it, the game sets a standing-on-object flag which will effectively glue the Player to an object when he lands on it.

The flag's job is making him stick to the object's surface and stay grounded, even though he's not touching any Solid Tiles (as far as his tile sensors are concerned, the Player is in the air while standing on an object). This flag will only be unset when walking off the edge of an object or jumping/getting hurt.

Walking Off The Edges

If the Player is standing on an object, the object will only check if the Player has walked off of it.

First, it calculates a distance to the object's left side.

 x_left_distance = (the Player's X Position - the object's X) + combined X radius //get the position difference
   

The Player will have walked off the edge if this distance is less than 0 or is greater than or equal to (combined X radius * 2). When this happens, the standing-on-object flag is unset and the Player is no longer grounded.

Moving On Platforms

After all checks are complete and if the Player is still on it, the game handles moving the Player with the object and keeping them stuck to it.

Things to Note

As mentioned when describing hitboxes, they are uneven and odd sized compared to the sprite. Using the method described above - the same is true for solid object boxes, so the Player will push against objects 1px further away when facing leftwards than he will tiles.

Bugs Using This Method

Overall, this method for collision with objects is pretty well made. However, there are a few obvious problems that become apparent when you mess with objects enough.

Slipping

As mentioned, since landing on the top of objects doesn't measure using the same radius as the rest of object collision, bizarrely this means if you jump down towards the corner of an object, you'll slip right off the sides because it exits the landing code if the Player's position isn't right above the object. This appears to be deliberate as the smaller radius is very explicitly used, but doesn't add any benefit as far as I can tell.

SPGObjectBugSlipping2.gif

The way the object collision code is executed, being from inside each object in order, there's effectively a priority system in place. If two objects want to push the Player two conflicting ways, the one who executes their solid object code last will win out. The result of this, and partly thanks to the edge slipping mentioned above, the Player can very easily slip between two objects which haven't been placed perfectly touching next to each other.

SPGObjectBugSlipping1.gif

The Player will collide on top with both spikes, but his position isn't directly over either of them when landing, so he will slip down the sides. Next, both spikes will try and push him with their sides, but only the last spike to do so will actually result in a net position change.

Bottom Overlap

When the vertical overlap is being checked, the game pretends the Player is 4px lower than they actually are. This allows 4px of extra "grip" to the top of objects, however it also effectively removes 4px from underneath them. When jumping up into an object, the Player will be able to enter it by around 4px before being popped out. Though, this is hard to notice during normal gameplay.

SPGObjectBugBottom.gif

This can be corrected by accounting for the added 4px when checking overlap and calculating distances with the bottom of the object.

False Object Standing Flag

This final bug is less of a design flaw and more of a major bug.

If for some reason the object you are standing on is deleted or otherwise unloaded, and the game fails to reset the standing-on-object flag you can then start walking through the air. This is because the flag is telling the game that the Player is still grounded even though there's no longer any object to be grounded to. Because the Player's grounded, he won't fall. Additionally, they also won't be able to walk off the object's sides as the object isn't even there to check for it.

Object Specific Collision

While a general description of Solid Object collision may cover a pushable block or a solid rock, not all objects behave the same. Some objects have slopes, and some will change what kind of solidity they have to suit different situations.

Objects That Collide

Some objects like walking enemies, pushable blocks, and item monitors all have to land on and stick to solid ground. They typically do this by casting a single downward sensor, much like the Player does, at their central bottom point. The same applies to wall collision. The way that objects use tile collision varies greatly and will be described for each individual Game Object.

Sloped Objects

You may have noticed some objects in the classic games are sloped, rather than box shaped. Like the Collapsing GHZ platform, the large platforms from marble, diagonal springs, or even the Spring Ramps in S2.

This is achieved by using the same code as normal, but injecting a different value to use as the surface y position of the object. To get this y position, the game checks against a sloped object's height array:

SPGSlopedObjects.png

The array for these objects are

 32 32 32 32 32 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 48 48 48 48 48

and

 32 32 32 32 32 32 32 32 32 32 32 32 32 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 32 32 32 32 32 32 32 32 32 32 32 32 32

This height array is relative to the object's Y Position, and is centred on it's X Position. Each value is the distance from the Y Position to the object's surface. Effectively a "fake" Height Radius for the top surface at every x position along the object.

The game stores these height arrays compressed at half the size, as shown above. This is possible because the slopes never need to be steeper than a step of 2 pixels, so the game simply "stretches out" the array information when the array is read. There are exceptions to this however, if the arrays step up in 2s (like 12 14 16) when stretched out the game will interpolate between these values to a 45 degree slope. This happens for the diagonal springs, for example.

When a sloped object is acting solid to the Player, instead of using the y position of the surface of the solid box using the object's Height Radius, it instead reads the value from the array, and from there on as far as the Player is concerned, the object's Height Radius is as high as the array tells it. This continuously happens as the Player passes over the object, resulting in smooth motion.

As you can see there is some extra information to the sides, this is simply because the Player can be standing on an object while their X Position isn't directly over it. This could be solved by just using the first or last height array value if Sonic's X Position is outside of the object boundary.

Differences To Tiles

There are no real angle values because the array is height information only, and no sensors are being used here. This means that the Player will have an angle value of 0 as he walks on a sloped object, and won't jump off or be affected by slope physics at all. In addition, the Player will be slightly "deeper" into the slopes than they would on solid tiles. This is because his centre point is always snapped to the slope, rather than one of their side floor sensors. It's most likely for these reasons that objects do not have angles steeper than what is shown above.

Jump Through Platforms

Jump through platforms are small objects which are only solid from the top. Since all the Player can do with platforms is land on them, they use their own code to check for just that, and in a more scrutinised way.

First, it will check if the Player 's Y Speed is less than 0. If it is, it will exit the collision. This means it will only check for overlap with the Player while they are moving down or staying still. This is why the Player can jump right up through it.

Horizontal Overlap

Next, it will check for X overlap in the exact same way that it does when landing on a normal solid object, using the object's normal X radius. Complete with all it's issues. If there's an overlap, it will continue.

Vertical Overlap

Next, the Y overlap is where things get interesting.

The game calculates the platform's surface Y coordinate by subtracting the Height Radius from the Y Position.

Then the Player's bottom Y is calculated by adding their Height Radius to their Y Position. It also adds 4 to this bottom Y for much the same reason as the normal solid object collision, it allows the Player to collide even when they're 4 pixels above.

The first check is if the platform's surface Y is greater than the Player's bottom Y. If it is, it will exit as the platform is too low.

Next, it will check a distance between the Player's bottom and the platform's surface (platform's surface Y minus the Player's bottom Y). If the distance is less than -16 or is greater than or equal to 0, it will exit as the Player is too low.

If it reaches past all those checks, the Player will land.

Popping The Player Out

The distance from before is added to the Player's Y Position, plus an extra 3px. After this the normal landing-on-object things occur, such as setting his speeds and standing-on-object flag.

Walking Off Edges

Platforms also use a different walking off edges code to normal Solid Objects. And since it's up to objects what width radius they want to use, things can get a little inconsistent. It's mentioned above that objects add the Player's radius to get a combined radius. This actually isn't always the case. Sometimes objects will just provide their unaltered width radius which is the case with most platforms. This means not only will the Player fall through the corners of platforms like any other object, but he will also walk off them just as easily, way sooner than he really should as if they are only 1px in total width, unlike the normal object collision.

This was probably missed because the Player doesn't need to push against these platforms, so it's much harder to notice if the Player's Push Radius hasn't been applied.

After this of course, the Player is still standing on it, so the game handles updating the Player's position on the object and moving him if the object is moving.

Worthy of note, is that many objects share the platform's "walking off edges" code.


Note: The code itself isn't the issue, the issue is moreso that the objects can far more easily pass in a radius that isn't combined when they use this because the general solid object code also uses the radius for pushing and for walking off, which requires it to be combined.

Pushable Blocks

Pushable blocks (specifically the type found in Marble Zone) are essentially normal solid objects, except for the fact when you are pushing them move. They move rather slowly, and you might assume that it sets the block and the Player's speeds to some value like 0.3, but this is not the case.

The block actually moves 1 entire pixel whenever you touch it from the side. But that sounds much faster than they actually move right? Well, in practice the block will only move once around every 3 frames. And the reason for this is rather technical to say the least and requires that you properly emulate the way the original game's positions work.

Upon Contact

When the Player has contacted the push block, the Player has been popped out, and his speeds have been set to 0, the push block will then do some extra things. If the Player pushed to the left, both the Player and the block will move 1 pixel to the left, the Player's X Speed is set to 0 and Ground Speed is set to -0.25. If they pushed to the right, both the Player and the block will move 1 pixel to the right, the Player's X Speed is set to 0 and Ground Speed is set to 0.25.

After being popped out the Player is no longer touching the object. When this happens, the Player's pixel position has been altered, but their subpixel position remains the same. So if the Player was half a pixel into the object before, they're now half a pixel outside of it. Before they make contact with the object again, they needs to cover this subpixel distance. This would normally take around 4 frames for a static wall, but here it instead takes 2-3 frames because they are given a headstart when their Ground Speed is set to .25.

Because the mechanics of movement within 256 subpixels are difficult to explain or visually demonstrate, here's what a few frames of pushing a pushable block to the right would look like:

 Frame 0:
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.34375 -- added acc to Ground Speed
   X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2669.97265625 -- added X Speed to X Position. the Player's subpixel position (.972) is very close to entering the next pixel, which is where he will collide again.
   
 Frame 1:
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.390625 -- added acc to Ground Speed
   X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2670.36328125 -- added X Speed to X Position. the Player's X pixel has changed
   
   -- the Player makes contact with push block and is popped out to the left.
   Ground Speed: 0 -- Ground Speed set to 0
   X Speed: 0 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2669.36328125 -- 1 subtracted from X Position
   
   -- The push block runs its own code and both are moved to the right by 1 pixel, and the Player's Ground Speed is set.
   Ground Speed: 0.25 -- Ground Speed set to 0.25
   X Speed: 0 -- X Speed set to 0
   X Position: 2670.36328125 -- 1 added to X Position

At this point, the Player has just pushed the block and has been moved out of it, then along with it. The fractional part of their position is currently .363 , just left of halfway through the pixel.

 Frame 2 (1 frame since last push):
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.296875 -- added acc to Ground Speed
   X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2670.66015625 -- added X Speed to X Position
 
 Frame 3 (2 frames since last push):
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.34375 -- added acc to Ground Speed
   X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2671.00390625 -- added X Speed to X Position. the Player's X pixel has changed
   
   -- the Player makes contact with push block and is popped out to the left.
   Ground Speed: 0 -- Ground Speed set to 0
   X Speed: 0 -- X Speed set to 0
   X Position: 2670.00390625 -- 1 subtracted from X Position
   
   -- the Player makes contact with push block and both are moved to the right by 1 pixel.
   -- This only took 2 frames, because the Player's subpixel was positioned just right on the previous push, which is very rare.
   Ground Speed: 0.25 -- Ground Speed set to 0.25
   X Speed: 0 -- X Speed set to 0
   X Position: 2671.00390625 -- 1 added to X Position

The Player has just pushed the block again, and has been moved out of it, then along with it. It took 2 frames. This time, the fractional part of their position is currently .003 , the very left of the pixel. This means they have farther to travel to reach the block again.

 Frame 4 (1 frame since last push):
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.296875 -- added acc to Ground Speed
   X Speed: 0.296875 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2671.30078125 -- added X Speed to X Position
 
 Frame 5 (2 frames since last push):
   -- the Player gains speed along the floor naturally
   Ground Speed: 0.34375 -- added acc to Ground Speed
   X Speed: 0.34375 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2671.64453125 -- added X Speed to X Position
 
 Frame 6 (3 frames since last push):
   -- the Player gains speed along the floor naturally and moves his position
   Ground Speed: 0.390625 -- added acc to Ground Speed
   X Speed: 0.390625 -- X Speed set to Ground Speed (on flat ground)
   X Position: 2672.03515625 -- added X Speed to X Position. the Player's X pixel has changed
   
   -- the Player makes contact with push block and is popped out to the left.
   Ground Speed: 0 -- Ground Speed set to 0
   X Speed: 0 -- X Speed set to 0
   X Position: 2671.03515625 -- 1 subtracted from X Position
   -- the Player makes contact with push block and both are moved to the right by 1 pixel.
   -- This time, it took 3 frames, which is far more common.
   Ground Speed: 0.25 -- Ground Speed set to 0.25
   X Speed: 0 -- X Speed set to 0
   X Position: 2672.03515625 -- 1 added to X Position

The Player has just pushed the block again, and has been moved out of it, then along with it. This time it took 3 frames thanks to his subpixel/fractional positions being allowed to wrap around and never reset. This 3 frame delay is the most common and is effectively the push speed.

The reverse would have the exact same timings. It seems they deliberately controlled this delay by adding .25 to his Ground Speed.

If you simply want to roughly copy it without the specifics or nuances of this system or you are using different object collision, just make a timer which triggers a movement every 3 frames while the Player is pushing.

To see what happens to a push block once it is pushed off a ledge, see Game Objects.

Item Monitor

Item Monitors, as you may have noticed, are not always solid. While you can stand on them you can also go right through them while jumping or rolling. The game is actually checking what the Player is doing and changing how the Item Monitor will react.

While not curled up in a ball, the Item Monitor acts as solid. The Item Monitor's hitbox isn't accessible at this time.

While curled, however, the item box will no longer have any solidity at all, and its hitbox is active and accessible. The hitbox collision is what destroys the Item Box and bounces the Player off (Details in Monitors Rebound).

However, there is an exception. If the Player is moving up (Y Speed < 0) while curled, the Item Box will in fact still act solid. Additionally, if the Player's Y Position-16 is smaller than the item box's Y Position, the item box will bounce up with a Y Speed of -1.5 knocking the Item Box upwards, and the Player's Y Speed will be reversed.