Actions

Difference between revisions of "SPG:Solid Objects"

From Sonic Retro

(Going into detail about the issues this system presents)
m (Finding The Direction of Collision: Clarification)
Line 122: Line 122:
 
If Sonic's ''xpos'' is greater than the object's X position, he's on the right, otherwise, he's on the left. If Sonic's ''ypos'' is greater than the object's Y position, he's on the bottom, otherwise, he's on the top.
 
If Sonic's ''xpos'' is greater than the object's X position, he's on the right, otherwise, he's on the left. If Sonic's ''ypos'' is greater than the object's Y position, he's on the bottom, otherwise, he's on the top.
  
After the side is determined, the game will calculate a distance to the nearest edge of the object.  
+
After the side is determined for each axis, the game will calculate a distance to the nearest edge.  
  
 
=====Horizontal Edge Distance=====
 
=====Horizontal Edge Distance=====
If Sonic is on the left, the game will calculate the distance as Sonic's ''xpos'' minus (object's X position minus the object's '''combined X radius'''). This effectively the distance to the object's left side and will be a positive number.  
+
If Sonic is on the left, the game will calculate the distance as Sonic's ''xpos'' minus (object's X position ''minus'' the object's '''combined X radius'''). This effectively the distance to the object's left side and will be a positive number.  
  
If Sonic is on the right, the distance will be (object's X position plus the object's '''combined X radius''') minus Sonic's ''xpos''. This is effectively the distance to the objects right side and will be a negative number.  
+
If Sonic is on the right, the distance will be Sonic's ''xpos'' minus (object's X position ''plus'' the object's '''combined X radius'''). This is effectively the distance to the objects right side and will be a negative number.  
  
 
Whichever side it is, we will call this the '''x distance'''.
 
Whichever side it is, we will call this the '''x distance'''.
  
 
=====Vertical Edge Distance=====
 
=====Vertical Edge Distance=====
It is the same along the Y axis. If Sonic is on the top, the game will calculate the distance as Sonic's ''ypos'' minus (object's Y position minus the object's '''combined Y radius'''). This effectively the distance to the object's top side and will be a positive number.  
+
It is the same along the Y axis. If Sonic is on the top, the game will calculate the distance as Sonic's ''ypos'' minus (object's Y position ''minus'' the object's '''combined Y radius'''). This effectively the distance to the object's top side and will be a positive number.  
  
If Sonic is on the bottom, the distance will be (object's Y position plus the object's '''combined Y radius''') minus Sonic's ''ypos''. This is effectively the distance to the objects bottom side and will be a negative number.  
+
If Sonic is on the bottom, the distance will be Sonic's ''ypos'' minus (object's Y position ''plus'' the object's '''combined Y radius'''). This is effectively the distance to the objects bottom side 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'''.
Line 156: Line 156:
 
[[Image:SPGSolidObjectNearerSide.png]]
 
[[Image:SPGSolidObjectNearerSide.png]]
  
The horizontal axis is favoured just a little more than the vertical. Keep in mind this exact pattern is only valid for an object of this exact size.
+
The horizontal axis is favoured just a little more than the vertical. 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 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'''.
 
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'''.

Revision as of 04:50, 10 August 2020

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

The following variables/constants will be referenced frequently in this section.

//Variables
xpos: The X-coordinate of Sonic's center.
ypos: The Y-coordinate of Sonic's center.
xsp: The speed at which Sonic is moving horizontally.
ysp: The speed at which Sonic is moving vertically.
gsp: The speed at which Sonic is moving on the ground.
slope: The current slope factor (slp) value being used.
ang: Sonic's angle on the ground.
	
//Constants
acc: 0.046875
dec: 0.5
frc: 0.046875 (same as acc)
top: 6
jmp: 6.5 (6 for Knuckles)
slp: 0.125
slprollup: 0.078125
slprolldown: 0.3125
fall: 2.5

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 Solid Tiles. This guide will go over the collision of various objects found in the Sonic trilogy.

Setup

Hitboxes and Solid Boxes (well, any object boxes) are built using a radius for width and a radius for height. However, just like with Sonic's sensors, this also includes the objects origin.

SPGHitBoxRadius.png

So, frustratingly, a hitbox with a width radius of 8 (when defined as 8 in the code) ends up being 17 pixels wide in-game rather than 16. As a result, all hitboxes in the entire game will be an odd number in size.

Sonic's Sizes

At any given time Sonic has 3 size radiuses. Firstly, his Push Radius which always remains constant (for pushing walls). He also has his Body Width Radius and his Body Height Radius (for floor/ceiling collision) both of which will change depending on Sonic's state.

Note: Information on these and how they change can be found at Solid Tiles. For specific object hitbox sizes, see Game Objects.

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.

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.

Sonic's Hitbox

In order to interact with other object's hitboxes, Sonic needs his own.

SPGHitBoxes.png

Sonic's hitbox is much like that of any other object. It sits at Sonic's xpos and ypos. It has a width radius of 8, and its height radius is always 3 pixels shorter than Sonic's Body 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.

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.

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.

The same is true for solid object boxes, so Sonic will push against objects 1px further away when facing leftwards than he will tiles.

Hitbox Types

There are various types of hitboxes that general objects use: Ones for collectibles like rings, ones for Badniks, ones which always hurt Sonic, and special ones that use object-specific responses. They all do different things, however, the method they use to come into contact with Sonic in the first place is all the same.

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.

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.

Solid Objects

Note: This section is a work in progress.

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.

This should all happen after Sonic's positions have moved that frame, and is from the object's point of view (occurs from each solid object where this applies).

General Solid Object Collision

Usually, objects which want to be solid define a width radius and height radius for their solidity area, in much the same way as hitboxes.

Though, Sonic's collision with these boxes does not involve his hitbox at all. Rather the game calculates a box the size of Sonic's sensor arrangement to be used.

Before we get started, 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.

Checking For An Overlap

To check for an overlap horizontally, the object combines its own X radius with Sonic's Push Radius and adds 1px extra. Though, in practice, the object simply provides the number pre-calculated without actually doing the calculation at runtime.

To check the vertical overlap, it very similar. the object combines its own Y radius with Sonic's current Body 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.

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.

If the absolute difference between Sonic's xpos and the object's X position is less than or equal to combined X radius then an overlap has occurred.

If the absolute difference between Sonic's ypos and the object's Y position is less than or equal to combined Y radius then an overlap has occurred.

Important to note is the game also allows Sonic to be slightly above the object by 4 pixels and still overlap, effectively 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. This 4 pixels isnt part of combined Y radius, but when checking for vertical overlap, 4 is added to the radius before comparison, and then removed before it is used again.

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.

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

If Sonic's xpos is greater than the object's X position, he's on the right, otherwise, he's on the left. If Sonic's ypos is greater than the object's Y position, he's on the bottom, otherwise, he's 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 Sonic is on the left, the game will calculate the distance as Sonic's xpos minus (object's X position minus the object's combined X radius). This effectively the distance to the object's left side and will be a positive number.

If Sonic is on the right, the distance will be Sonic's xpos minus (object's X position plus the object's combined X radius). This is effectively the distance to the objects right side and will be a negative number.

Whichever side it is, we will call this the x distance.

Vertical Edge Distance

It is the same along the Y axis. If Sonic is on the top, the game will calculate the distance as Sonic's ypos minus (object's Y position minus the object's combined Y radius). This effectively the distance to the object's top side and will be a positive number.

If Sonic is on the bottom, the distance will be Sonic's ypos minus (object's Y position plus the object's combined Y radius). This is effectively the distance to the objects bottom side and will be a negative number.

Whichever side it is, we will call this the y distance.

Choosing The Direction

Finally, with all of this information, the game can decide which way Sonic should be popped out.

It does this by finding which side Sonic 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 xpos and ypos within the solid area of a block.

SPGSolidObjectNearerSide.png

The horizontal axis is favoured just a little more than the vertical. 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 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

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.

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.

There are a couple 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.

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 (xsp > 0), towards the object. The same is true for colliding with the right, but if Sonic is moving to the left (xsp < 0). Basically, he must be moving towards the object. When his speeds are stopped, xsp and gsp are set to 0.

Regardless, x distance will be subtracted from Sonic's position, popping Sonic 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.

Popped Downwards

If Sonic bumps the bottom of an object, the game will check if sonic is moving vertically (ysp is not 0). If not, the game then checks if Sonic is standing on the ground, and if he is, kills him from crushing.

Otherwise, if Sonic is moving vertically and has a negative ysp, the game checks if y distance is 0. If it is, the game doesn't need to do anything as Sonic isn't inside the object by enough, and will exit.

Finally, if the y distance is smaller than 0, the game will subtract y distance from his position and set his ysp to 0. Nothing happens if he is moving down.

Popped Upwards

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

Before it does this, 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.

 action_radius = 16 //an example
 action_diameter = action_radius*2
 
 x_comparison = action_radius plus object's X position
 x_comparison -= sonic's xpos
 
 //if sonic is too far to the right
 if (x_comparison is less than 0)
 {
   exit;
 }
 
 //if sonic is too far to the left
 if (x_comparison is greater than or equal to action_diameter)
 {
   exit;
 }
 

This means Sonic will exit the landing and will just slip off the side keep falling if his "xpos" position isn't directly above the object, which is actually quite strange as it's as if Sonic is only 1 pixel thick.

Last check is if Sonic's ysp is negative, he wont land and it will exit.

Finally, if it gets this far, he will land. From this point it's rather simple.

The game subtracts y distance from Sonic's ypos. It also subtracts an extra 1px afterward to align him correctly.

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

Finally, Sonic's gsp is set to equal his xsp.

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

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.

Walking Off The Edges

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

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

 x_left_distance = sonic's xpos minus the object's X //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.

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.

Bugs Using This Method

Overall, this method for collision with objects is pretty well made. However there are a few obvious problems which 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 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.

SPGObjectBugSlipping2.gif

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

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.

False Object Standing Flag

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

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.

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.

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.

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

However, there is an exception. If Sonic is moving up (ysp < 0) while curled, the Item Box will in fact still act solid. Additionally, if Sonic's ypos-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 ysp will be reversed.

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 they will move themselves and Sonic 1 pixel in the push direction. However, they clearly do not move this fast... Well, for some reason the block will only move around every 3 frames for so, but it's not consistent and may not be 100% intentional.