Actions

Difference between revisions of "SPG:Slope Physics"

From Sonic Retro

m (Added pixel/subpixel conversion with decimal)
(New formatting)
Line 6: Line 6:
  
 
==Constants==
 
==Constants==
  acceleration_speed: 0.046875 (12 subpixels)
+
{| class="prettytable" style="width: auto;"
  deceleration_speed: 0.5 (128 subpixels)
+
!Constant
  friction_speed: 0.046875 (12 subpixels)
+
!Value
  top_speed: 6
+
|-
+
|'''acceleration_speed'''
  gravity_force: 0.21875 (56 subpixels)
+
|''0.046875 (12 subpixels)''
+
|-
  slope_factor: 0.125 (32 subpixels)
+
|'''deceleration_speed'''
  slope_factor_rollup: 0.078125 (20 subpixels)
+
|''0.5 (128 subpixels)''
  slope_factor_rolldown: 0.3125 (80 subpixels)
+
|-
 +
|'''friction_speed'''
 +
|''0.046875 (12 subpixels)''
 +
|-
 +
|'''top_speed'''
 +
|''6''
 +
|-
 +
|'''gravity_force'''
 +
|''0.21875 (56 subpixels)''
 +
|-
 +
|'''slope_factor'''
 +
|''0.125 (32 subpixels)''
 +
|-
 +
|'''slope_factor_rollup'''
 +
|''0.078125 (20 subpixels)''
 +
|-
 +
|'''slope_factor_rolldown'''
 +
|''0.3125 (80 subpixels)''
 +
|}
  
 
==Introduction==
 
==Introduction==
Line 22: Line 40:
  
 
==Slope Momentum==
 
==Slope Momentum==
 
 
The Player's movement across the stage has to be influenced by angled ground in order to feel realistic.
 
The Player's movement across the stage has to be influenced by angled ground in order to feel realistic.
  
 
===Moving Along Slopes===
 
===Moving Along Slopes===
 +
In order for angled movement to be accurate, we need to make sure that the Player does not traverse an incline slope in the same amount of time as walking over flat ground of an equal width.
  
In order for angled movement to be accurate, we need to make sure that the Player does not traverse an incline slope in the same amount of time as walking over flat ground of an equal width.
+
If Sonic were a simple "slope-less" platformer that required nothing but flat blocks, you would only need two speed variables: '''''X Speed''''' and '''''Y Speed''''', the horizontal and vertical components of the Player's velocity. '''acceleration_speed''', '''deceleration_speed''', and '''friction_speed''' are added to '''''X Speed'''''; jump/bounce velocity and '''gravity_force''' are added to '''''Y Speed''''' (when the Player is in the air).
  
If Sonic were a simple "slope-less" platformer that required nothing but flat blocks, you would only need two speed variables: X Speed and Y Speed, the horizontal and vertical components of the Player's velocity. ''acceleration_speed'', ''deceleration_speed'', and ''friction_speed'' are added to X Speed; jump/bounce velocity and ''gravity_force'' are added to Y Speed (when the Player is in the air).
+
But when slopes are involved, while the Player moves along a slope, they're moving both horizontally and vertically. This means that both '''''X Speed''''' and '''''Y Speed''''' have a non-zero value. Simply adding '''acceleration_speed''', '''deceleration_speed''', or '''friction_speed''' to '''''X Speed''''' no longer works; imagine the Player was trying to run up a wall - adding to their horizontal speed would be useless because they need to move upward.
  
But when slopes are involved, while the Player moves along a slope, they're moving both horizontally and vertically. This means that both X Speed and Y Speed have a non-zero value. Simply adding ''acceleration_speed'', ''deceleration_speed'', or ''friction_speed'' to X Speed no longer works; imagine the Player was trying to run up a wall - adding to their horizontal speed would be useless because they need to move upward.
+
The trick is to employ a third speed variable (as the original engine does), '''''Ground Speed'''''. This is the speed of the Player along the ground, disregarding '''''Ground Angle''''' altogether. '''acceleration_speed''', '''deceleration_speed''', and '''friction_speed''' are applied to '''''Ground Speed''''', not '''''X Speed''''' or '''''Y Speed'''''.
  
The trick is to employ a third speed variable (as the original engine does), Ground Speed. This is the speed of the Player along the ground, disregarding Ground Angle altogether. ''acceleration_speed'', ''deceleration_speed'', and ''friction_speed'' are applied to Ground Speed, not X Speed or Y Speed.
+
While on the ground, '''''X Speed''''' and '''''Y Speed''''' are entirely derived from '''''Ground Speed''''' every step before the Player is moved. Perhaps a code example is in order:
  
While on the ground, X Speed and Y Speed are entirely derived from Ground Speed every step before the Player is moved. Perhaps a pseudo-code example is in order:
+
<syntaxhighlight>// Calculate X and Y Speed from Ground Speed
 +
X Speed = Ground Speed * cos(Ground Angle)
 +
Y Speed = Ground Speed * -sin(Ground Angle)
  
  X Speed = Ground Speed * cos(Ground Angle)
+
// Actually move via X Speed and Y Speed
  Y Speed = Ground Speed * -sin(Ground Angle)
+
X Position += X Speed;
 
+
Y Position += Y Speed;</syntaxhighlight>
  X Position += X Speed
 
  Y Position += Y Speed
 
  
No matter what happens to the Ground Angle, Ground Speed is preserved, so the game always knows what speed the Player is "really" moving at.
+
No matter what happens to the '''''Ground Angle''''', '''''Ground Speed''''' is preserved, so the game always knows what speed the Player is "really" moving at.
  
What's more, is that the position the Player is moved to with X Speed and Y Speed are where the player will next check for collision with the floor, so it is vital that the next position the player moves to for the next frame's checks aligns as much with the current slope direction as possible.
+
What's more, is that the position the Player is moved to with '''''X Speed''''' and '''''Y Speed''''' are where the player will next check for collision with the floor, so it is vital that the next position the player moves to for the next frame's checks aligns as much with the current slope direction as possible.
  
 
===Slowing Down Uphill And Speeding Up Downhill===
 
===Slowing Down Uphill And Speeding Up Downhill===
Line 51: Line 69:
 
By this point, the Player should be able to handle any basic hills with an accurate velocity but they still need to slow down when going uphill and speed up when going downhill.
 
By this point, the Player should be able to handle any basic hills with an accurate velocity but they still need to slow down when going uphill and speed up when going downhill.
  
Fortunately, this is simple to achieve - with something called the Slope Factor. Just subtract Slope Factor * sin(Ground Angle) from Ground Speed at the beginning of every step. This only happens if the player is not in ceiling mode.
+
Fortunately, this is simple to achieve - with something called the '''''Slope Factor'''''. Just subtract '''''Slope Factor''''' * sin('''''Ground Angle''''') from Ground Speed at the beginning of every step. This only happens if the player is not in ceiling mode.
  
  Ground Speed -= Slope Factor * sin(Ground Angle);
+
<syntaxhighlight>Ground Speed -= Slope Factor * sin(Ground Angle);</syntaxhighlight>
  
The value of Slope Factor is always ''slope_factor'' when running, but not so when rolling. When the Player is rolling uphill (the sign of Ground Speed is equal to the sign of sin(Ground Angle)), Slope Factor is ''slope_factor_rollup''. When the Player is rolling downhill (the sign of Ground Speed is '''not''' equal to the sign of sin(Ground Angle)), Slope Factor is ''slope_factor_rolldown''.
+
The value of '''''Slope Factor''''' is always '''slope_factor''' when running, but not so when rolling. When the Player is rolling uphill (the sign of Ground Speed is equal to the sign of sin('''''Ground Angle''''')), Slope Factor is '''slope_factor_rollup'''. When the Player is rolling downhill (the sign of Ground Speed is ''not'' equal to the sign of sin('''''Ground Angle''''')), '''''Slope Factor''''' is '''slope_factor_rolldown'''.
  
Note: ''In Sonic 1 and 2, walking/running Slope Factor doesn't get subtracted if the Player is stopped (Ground Speed is 0). But in Sonic 3 & Knuckles, if Ground Speed is 0, the game will still subtract Slope Factor if the value of it is greater than or equal to 0.05078125. So that the Player can't stand on steep slopes - it will force them to walk down. Rolling slope factor, however, has no check for if Ground Speed is 0 in any of the games.''
+
Note:  
 +
* In Sonic 1 and 2, walking/running '''''Slope Factor''''' doesn't get subtracted if the Player is stopped ('''''Ground Speed''''' is ''0''). But in Sonic 3 & Knuckles, if '''''Ground Speed''''' is ''0'', the game will still subtract '''''Slope Factor''''' if the value of it is greater than or equal to ''0.05078125 (13 subpixels)''. So that the Player can't stand on steep slopes - it will force them to walk down. Rolling slope factor, however, has no check for if '''''Ground Speed''''' is ''0'' in any of the games.
  
 
==360 Degree Movement==
 
==360 Degree Movement==
Line 71: Line 90:
 
It seems pretty reasonable to assume that, because the Player can traverse ground in 360 degrees, the engine handles all 360 degrees in much the same way. But, in fact, the engine splits the angles into four quadrants, greatly simplifying things.
 
It seems pretty reasonable to assume that, because the Player can traverse ground in 360 degrees, the engine handles all 360 degrees in much the same way. But, in fact, the engine splits the angles into four quadrants, greatly simplifying things.
  
To better understand what I am talking about, imagine a simpler platformer without full loops, just a few low hills and ramps. All the character would need to do is, after moving horizontally, move up or down until they met the level of the floor. The angle of the floor would then be measured. The angle would be used to attenuate Ground Speed, but nothing more. The character would still always move horizontally and move straight up and down to adhere to floor level.
+
To better understand what I am talking about, imagine a simpler platformer without full loops, just a few low hills and ramps. All the character would need to do is, after moving horizontally, move up or down until they met the level of the floor. The angle of the floor would then be measured. The angle would be used to attenuate '''''Ground Speed''''', but nothing more. The character would still always move horizontally and move straight up and down to adhere to floor level.
  
This is much like how the Sonic games do things. Only, when Ground Angle gets too steep, the Player switches "quadrant", moving from Floor mode to Right Wall mode (to Ceiling mode, to Left Wall mode, and back around to Floor mode, etc). At any one time, in any one mode, the Player behaves like a simpler platformer. The magic happens by combining all four modes, and cleverly switching between them smoothly.
+
This is much like how the Sonic games do things. Only, when '''''Ground Angle''''' gets too steep, the Player switches "quadrant", moving from Floor mode to Right Wall mode (to Ceiling mode, to Left Wall mode, and back around to Floor mode, etc). At any one time, in any one mode, the Player behaves like a simpler platformer. The magic happens by combining all four modes, and cleverly switching between them smoothly.
  
 
So how and when does the Player switch mode?
 
So how and when does the Player switch mode?
  
When in Floor mode, and Ground Angle is steeper than 45° (224) ($E0), the engine switches into Right Wall mode. Everything is basically the same, only the sensors check to the right instead of downward, and the Player is moved to "floor" level horizontally instead of vertically.
+
When in Floor mode, and '''''Ground Angle''''' is steeper than ''45° (224) ($E0)'', the engine switches into Right Wall mode. Everything is basically the same, only the sensors check to the right instead of downward, and the Player is moved to "floor" level horizontally instead of vertically.
  
Now that they're in Right Wall mode, if Ground Angle is shallower than 46° (223) ($DF), the engine switches back into Floor mode.
+
Now that they're in Right Wall mode, if '''''Ground Angle''''' is shallower than ''46° (223) ($DF)'', the engine switches back into Floor mode.
  
 
The other transitions work in exactly the same way, with the switch angles relative to the current mode.
 
The other transitions work in exactly the same way, with the switch angles relative to the current mode.
  
When the mode is being calculated, it simply checks which quadrant the Player's Ground Angle is currently in, which will place the Player in the correct mode (ranges are inclusive):
+
When the mode is being calculated, it simply checks which quadrant the Player's '''''Ground Angle''''' is currently in, which will place the Player in the correct mode (ranges are inclusive):
 
+
{| class="prettytable" style="width: auto;"
  Floor Mode (start of rotation)
+
!Mode
  0° to 45° (255~224) ($FF~$E0)
+
!Angle Range
 
+
|-
  Right Wall Mode
+
|Floor Mode (start of rotation)
  46° to 134° (223~161) ($DF~$A1)
+
|''0° to 45° (255~224) ($FF~$E0)''
 
+
|-
  Ceiling Mode
+
|Right Wall Mode
  135° to 225° (160~96) ($A0~$60)  
+
|''46° to 134° (223~161) ($DF~$A1)''
 
+
|-
  Left Wall Mode
+
|Ceiling Mode
  226° to 314° (95~33) ($5F~$21)
+
|''135° to 225° (160~96) ($A0~$60)''
 
+
|-
  Floor Mode (end of rotation)
+
|Left Wall Mode
  315° to 360° (32~0) ($20~$00)
+
|''226° to 314° (95~33) ($5F~$21)''
 +
|-
 +
|Floor Mode (end of rotation)
 +
|''315° to 360° (32~0) ($20~$00)''
 +
|}
  
 
Note:
 
Note:
Line 105: Line 128:
 
These ranges are symmetrical for left and right, but does favour the floor and ceiling modes, with their ranges being a degree or two wider.
 
These ranges are symmetrical for left and right, but does favour the floor and ceiling modes, with their ranges being a degree or two wider.
  
You might rightly ask where the ground sensors are when in Right Wall mode. They're in exactly the same place, only rotated 90 degrees. Sensor <span style="color:#00f000; font-weight: bold;">A</span> is now at the Player's Y Position + Width Radius instead of X Position - width Radius. Sensor <span style="color:#38ffa2; font-weight: bold;">B</span> is now at the Player's Y Position - Width Radius, instead of X Position + Width Radius. Instead of downward vertical sensor, they are now horizontal facing left, at his foot level (which is now "below" them, at X Position + Width Radius). They move and rotate in the same way for the other modes.
+
You might rightly ask where the ground sensors are when in Right Wall mode. They're in exactly the same place, only rotated 90 degrees. Sensor <span style="color:#00f000; font-weight: bold;">A</span> is now at the Player's '''''Y Position''''' + '''''Width Radius''''' instead of '''''X Position''''' - '''''Width Radius'''''. Sensor <span style="color:#38ffa2; font-weight: bold;">B</span> is now at the Player's '''''Y Position''''' - '''''Width Radius''''', instead of '''''X Position''''' + '''''Width Radius'''''. Instead of downward vertical sensor, they are now horizontal facing left, at his foot level (which is now "below" them, at '''''X Position''''' + '''''Width Radius'''''). They move and rotate in the same way for the other modes.
  
 
Yes, because the sensors move so far, it is possible for the Player to be "popped" out to a new position in the step in which he switches mode. However, this is hardly ever more than a few pixels and really isn't noticeable at all during normal play.  
 
Yes, because the sensors move so far, it is possible for the Player to be "popped" out to a new position in the step in which he switches mode. However, this is hardly ever more than a few pixels and really isn't noticeable at all during normal play.  
Line 117: Line 140:
 
[[Image:SPGInnerCurve.PNG|link=Special:FilePath/SPGInnerCurve.PNG]] [[Image:SPGInnerCurveChart.PNG|link=Special:FilePath/SPGInnerCurveChart.PNG]]
 
[[Image:SPGInnerCurve.PNG|link=Special:FilePath/SPGInnerCurve.PNG]] [[Image:SPGInnerCurveChart.PNG|link=Special:FilePath/SPGInnerCurveChart.PNG]]
  
You can observe Sonic's mode changing on the frame after his floor angle (Ground Angle) exceeds 45°.  Sonic's position shifts a bit when the change occurs, due to the totally new collision angle and position.
+
You can observe Sonic's mode changing on the frame after his floor angle ('''''Ground Angle''''') exceeds ''45°''.  Sonic's position shifts a bit when the change occurs, due to the totally new collision angle and position.
  
 
[[Image:SPGOuterCurve.PNG|link=Special:FilePath/SPGOuterCurve.PNG]] [[Image:SPGOuterCurveChart.PNG|link=Special:FilePath/SPGOuterCurveChart.PNG]]
 
[[Image:SPGOuterCurve.PNG|link=Special:FilePath/SPGOuterCurve.PNG]] [[Image:SPGOuterCurveChart.PNG|link=Special:FilePath/SPGOuterCurveChart.PNG]]
  
You may notice the Player's mode switches erratically on the convex curve, this is because his floor angle (Ground Angle) will suddenly decrease when switching to wall mode, causing it to switch back and forth until he is far enough down the curve to stabilise. This isn't usually noticeable, and happens less the faster you are moving.
+
You may notice the Player's mode switches erratically on the convex curve, this is because his floor angle ('''''Ground Angle''''') will suddenly decrease when switching to wall mode, causing it to switch back and forth until he is far enough down the curve to stabilise. This isn't usually noticeable, and happens less the faster you are moving.
  
 
Note: The reason the gifs show the mode switch being the frame ''after'' the angle threshold is reached is simply because the collision being shown is the one used for ''that'' frame, ''before'' the Player's Ground Angle updates, but after they have moved.
 
Note: The reason the gifs show the mode switch being the frame ''after'' the angle threshold is reached is simply because the collision being shown is the one used for ''that'' frame, ''before'' the Player's Ground Angle updates, but after they have moved.
Line 127: Line 150:
 
===When to Change Mode===
 
===When to Change Mode===
  
If you've checked the guide regarding the [[SPG:Main_Game_Loop|Main Game Loop]] you may notice the mode switching isn't mentioned at all, that's because the game doesn't actually ever "switch" the Player's mode. The Player's current "mode" is decided right before collision occurs. It will measure his angle, and decide which mode of collision to use right there and then. There is no "Mode" state stored in memory. So effectively, the Player's mode updates whenever his angle (Ground Angle) does.  
+
If you've checked the guide regarding the [[SPG:Main_Game_Loop|Main Game Loop]] you may notice the mode switching isn't mentioned at all, that's because the game doesn't actually ever "switch" the Player's mode. The Player's current "mode" is decided right before collision occurs. It will measure his angle, and decide which mode of collision to use right there and then. There is no "mode" state stored in memory. So effectively, the Player's mode updates whenever his angle ('''''Ground Angle''''') does.  
  
Since the Ground Angle is decided ''after'' floor collision (as a result of floor collision) the floor collision that frame has to use the previous frames angle, even though the Player has moved to a new part of the slope since then. This results in the Player's mode effectively changing 1 frame ''after'' the Player reaches one of the 45 degree angle thresholds, as seen above.
+
Since the '''''Ground Angle''''' is decided ''after'' floor collision (as a result of floor collision) the floor collision that frame has to use the previous frames angle, even though the Player has moved to a new part of the slope since then. This results in the Player's mode effectively changing 1 frame ''after'' the Player reaches one of the ''45'' degree angle thresholds, as seen above.
  
 
==Falling and Slipping Down Slopes==
 
==Falling and Slipping Down Slopes==
Line 135: Line 158:
 
At this point, slope movement will work rather well, but it's not enough just to slow the Player down on steep slopes. They need to slip down when it gets too steep and you are moving too slowly.  
 
At this point, slope movement will work rather well, but it's not enough just to slow the Player down on steep slopes. They need to slip down when it gets too steep and you are moving too slowly.  
  
The angle range of slopes for slipping is when your Ground Angle is within the range 46° to 315° (223~32) ($DF~$20) inclusive.
+
The angle range of slopes for slipping is when your '''''Ground Angle''''' is within the range ''46° to 315° (223~32) ($DF~$20)'' inclusive.
  
In addition, the game will check if absolute Ground Speed falls below 2.5 ($280).
+
In addition, the game will check if absolute '''''Ground Speed''''' falls below ''2.5 (2 pixels, 128 subpixels)''.
  
So, when these conditions are met, what happens? Well, the Player will slip. This achieved by detaching the Player from the floor (clearing the grounded state), setting Ground Speed to 0, and employing the [[SPG:Running#Control_Lock|control lock timer]].
+
So, when these conditions are met, what happens? Well, the Player will slip. This achieved by detaching the Player from the floor (clearing the grounded state), setting '''''Ground Speed''''' to ''0'', and employing the [[SPG:Running#Control_Lock|control lock timer]].
  
 
[[Image:SPGSlopeSlip.gif]] ''Next to Sonic you can see the control lock timer.''
 
[[Image:SPGSlopeSlip.gif]] ''Next to Sonic you can see the control lock timer.''
  
Here, when he gets too steep, Sonic detaches from the floor, Ground Speed is set to 0, and control lock timer is set.
+
Here, when he gets too steep, Sonic detaches from the floor, '''''Ground Speed''''' is set to ''0'', and control lock timer is set.
  
But wait, why does Sonic not stop dead in his tracks if he become airbone and Ground Speed was set to 0? Well, if the floor isn't steep enough to freely fall from, the Player will immediately land back onto the floor and the Ground Speed will be restored from the X/Y Speeds as normal. Landing on the floor and speed conversion is further detailed up ahead in [[SPG:Slope Physics#Landing On The Ground|Landing On The Ground]])
+
But wait, why does Sonic not stop dead in his tracks if he become airbone and '''''Ground Speed''''' was set to 0? Well, if the floor isn't steep enough to freely fall from, the Player will immediately land back onto the floor and the '''''Ground Speed''''' will be restored from '''''X/Y Speed''''' as normal. Landing on the floor and speed conversion is further detailed up ahead in [[SPG:Slope Physics#Landing On The Ground|Landing On The Ground]])
  
 
Okay, what about if the Player is on an even steeper floor?
 
Okay, what about if the Player is on an even steeper floor?
Line 153: Line 176:
 
You can notice he detaches from the floor and control lock is set. It doesn't tick down until he lands, and even after the timer has begun, when he crosses the gap the timer pauses. The code for both the control lock timer and the slipping are only ran when grounded.
 
You can notice he detaches from the floor and control lock is set. It doesn't tick down until he lands, and even after the timer has begun, when he crosses the gap the timer pauses. The code for both the control lock timer and the slipping are only ran when grounded.
  
So, what about the timer? When the Player falls or slips off in the manner described above, the [[SPG:Running#Control_Lock|control lock timer]] is set to 30 ($1E) (it won't begin to count down until the Player lands back on the ground). While this timer is non-zero and the Player is on the ground, it prevents directional input from adjusting the Player's speed with the left or right buttons. The timer counts down by one every step when grounded, so the lock lasts about half a second. During this time only ''slope_factor'' and the speed the Player fell back on the ground with is in effect, so the Player will slip back down the slope.
+
So, what about the timer? When the Player falls or slips off in the manner described above, the [[SPG:Running#Control_Lock|control lock timer]] is set to ''30 ($1E)'' (it won't begin to count down until the Player lands back on the ground). While this timer is non-zero and the Player is on the ground, it prevents directional input from adjusting the Player's speed with the left or right buttons. The timer counts down by one every step when grounded, so the lock lasts about half a second. During this time only '''slope_factor''' and the speed the Player fell back on the ground with is in effect, so the Player will slip back down the slope.
  
In the above first example gif, you may notice the Control lock timer counts down twice, this is purely because Sonic happened to be too steep and too slow still when the timer ended initially, and he slipped once again, seamlessly.
+
In the above first example gif, you may notice the control lock timer counts down twice, this is purely because Sonic happened to be too steep and too slow still when the timer ended initially, and he slipped once again, seamlessly.
  
 
So, with some example code, it works like the following:
 
So, with some example code, it works like the following:
 
+
<syntaxhighlight>// Is player grounded?
  // is player grounded?
+
if player is grounded
  if player is grounded
+
{
  {
+
    if control_lock_timer == 0
      if control_lock_timer == 0
+
    {
      {
+
        // Should player slip?
          // should player slip?
+
        if abs(Ground Speed) < 2.5 and (Ground Angle is within range)
          if abs(Ground Speed) < 2.5 and (Ground Angle is within range)
+
        {
          {
+
            grounded = false;
              grounded = false
+
            Ground Speed = 0;
              Ground Speed = 0
+
            control_lock_timer = 30;
              control_lock_timer = 30
+
        }
          }
+
    }
      }
+
    else
      else
+
    {
      {
+
        // Tick down timer
          // tick down timer
+
        control_lock_timer -= 1;  
          control_lock_timer -= 1;  
+
    }
      }
+
}</syntaxhighlight>
  }
 
  
  
 
===Sonic 3 Method===
 
===Sonic 3 Method===
  
Sonic 3 works a little differently, where Sonic will slip down at angles ''even shallower'' than 45, and only detach from the floor when at angles ''even steeper'' than 45.  
+
Sonic 3 works a little differently, where Sonic will slip down at angles ''even shallower'' than ''45°'', and only detach from the floor when at angles ''even steeper'' than ''45°''.  
  
The angle range for slipping down a slope is when your Ground Angle is within the range 35° to 326° (231~24) ($E7~$18) inclusive.
+
{| class="prettytable" style="width: auto;"
 
+
!Range
The angle range for falling off is when your Ground Angle is within the range 69° to 293° (207~48) ($CF~$30) inclusive.
+
!Values
 
+
|-
Not only are there these new ranges, Ground Speed is now modified by 0.5 instead of being set to 0.
+
|Slipping
 +
|'''''Ground Angle''''' is within the range ''35° to 326° (231~24) ($E7~$18)'' inclusive.
 +
|-
 +
|Falling
 +
|'''''Ground Angle''''' is within the range ''69° to 293° (207~48) ($CF~$30)'' inclusive.
 +
|}
  
 +
Not only are there these new ranges, '''''Ground Speed''''' is now modified by ''0.5'' instead of being set to ''0''.
  
 
Here's how it works:
 
Here's how it works:
  
  // is player grounded?
+
<syntaxhighlight>// Is the Player grounded?
  if player is grounded
+
if player is grounded
  {
+
{
      if control_lock_timer == 0
+
    if control_lock_timer == 0
      {
+
    {
          // should player slip?
+
        // Should player slip?
          if abs(Ground Speed) < 2.5 and (Ground Angle is within slip range)
+
        if abs(Ground Speed) < 2.5 and (Ground Angle is within slip range)
          {
+
        {
              //lock controls
+
            // Lock controls
              control_lock_timer = 30
+
            control_lock_timer = 30;
             
+
           
              // should player fall?
+
            // Should player fall?
              if (Ground Angle is within fall range)
+
            if (Ground Angle is within fall range)
              {
+
            {
                  // detach
+
                // Detach
                  grounded = false
+
                grounded = false;
              }
+
            }
              else
+
            else
              {
+
            {
                  // depending on what side the slope is, add or subtract 0.5 from Ground Speed
+
                // Depending on what side the slope is, add or subtract 0.5 from Ground Speed
                  if Ground Angle < 180°
+
                if Ground Angle < 180°
                  {
+
                {
                      Ground Speed -= 0.5
+
                    Ground Speed -= 0.5;
                  }
+
                }
                  else
+
                else
                  {
+
                {
                      Ground Speed += 0.5
+
                    Ground Speed += 0.5;
                  }
+
                }
              }
+
            }
          }
+
        }
      }
+
    }
      else
+
    else
      {
+
    {
          // tick down timer
+
        // Tick down timer
          control_lock_timer -= 1;  
+
        control_lock_timer -= 1;  
      }
+
    }
  }
+
}</syntaxhighlight>
  
 
==Landing On The Ground==
 
==Landing On The Ground==
Both X Speed and Y Speed are derived from Ground Speed while the Player is on the ground. When they fall or otherwise leave the ground, X Speed and Y Speed are already the proper values for him to continue his trajectory through the air. But when they land back on the ground, Ground Speed must be calculated from the X Speed and Y Speed that they have when it happens.
+
Both '''''X Speed''''' and '''''Y Speed''''' are derived from Ground Speed while the Player is on the ground. When they fall or otherwise leave the ground, '''''X Speed''''' and '''''Y Speed''''' are already the proper values for him to continue his trajectory through the air. But when they land back on the ground, '''''Ground Speed''''' must be calculated from the '''''X Speed''''' and '''''Y Speed''''' that they have when it happens.
 
You might think that the game would use cos() and sin() to get an accurate value, but that is not the case. In fact, something much more basic happens, and it is different when hitting into a curved ceiling as opposed to landing on a curved floor, so I will cover them separately.
 
You might think that the game would use cos() and sin() to get an accurate value, but that is not the case. In fact, something much more basic happens, and it is different when hitting into a curved ceiling as opposed to landing on a curved floor, so I will cover them separately.
  
As you land the angle of the ground you touch is read (Ground Angle).
+
As you land the angle of the ground you touch is read ('''''Ground Angle''''').
The following covers the angle (Ground Angle) of the ground (floor or ceiling) that the Player touches as they land, and only happens the frame when they land when changing from in air to on ground.
+
The following covers the angle ('''''Ground Angle''''') of the ground (floor or ceiling) that the Player touches as they land, and only happens the frame when they land when changing from in air to on ground.
  
 
'''Notes''':  
 
'''Notes''':  
 
*''The landing ground speed reaction is also determined by what direction the player was moving in the air, which is the same calculation as noted in [[SPG:Slope_Collision#While_Airborne|Slope Collision]].''
 
*''The landing ground speed reaction is also determined by what direction the player was moving in the air, which is the same calculation as noted in [[SPG:Slope_Collision#While_Airborne|Slope Collision]].''
*''Since the classic games don't use degrees, and rather have angles ranging from 0 to 256, both approximate degree values and a more accurate (and inverted) decimal representation of the Hex values are included.''
 
  
 
===When Falling Downward===
 
===When Falling Downward===
Line 246: Line 273:
  
 
The following ranges are inclusive.
 
The following ranges are inclusive.
 +
{| class="prettytable" style="width: auto;"
 +
!Range
 +
!Values
 +
!Result
 +
|-
 +
|Shallow range
 +
|''0° to 23° (255~240) ($FF~$F0)''
  
 +
and mirrored:
 +
''339° to 360° (15~0) ($0F~$00)''
 +
|'''''Ground Speed''''' is always set to the value of '''''X Speed'''''.
  
====Shallow====
+
|-
Shallow range is when Ground Angle is in the range of:
+
|Slope
 
+
|''24° to 45° (239~224) ($EF~$E0)''
  0° to 23° (255~240) ($FF~$F0)
 
  and mirrored:
 
  339° to 360° (15~0) ($0F~$00)
 
 
 
In the shallow range, Ground Speed is always set to the value of X Speed.
 
 
 
====Slope====
 
Slope range is when Ground Angle is in the range of:
 
 
 
  24° to 45° (239~224) ($EF~$E0)  
 
  and mirrored:
 
  316° to 338° (31~16) ($1F~$10)
 
 
 
In this range, when moving mostly left or mostly right, Ground Speed is set to X Speed. Otherwise, Ground Speed is set to:
 
  
 +
and mirrored:
 +
''316° to 338° (31~16) ($1F~$10)''
 +
|When moving mostly left or mostly right, '''''Ground Speed''''' is set to '''''X Speed'''''. Otherwise, '''''Ground Speed''''' is set to:
 
   Y Speed * 0.5 * -sign(sin(Ground Angle))
 
   Y Speed * 0.5 * -sign(sin(Ground Angle))
 
+
|-
====Steep Slope====
+
|Steep Slope
Steep Slope range is when Ground Angle is in the range of:
+
|''46° to 90° (223~192) ($DF~$C0)''
 
+
  46° to 90° (223~192) ($DF~$C0)  
+
and mirrored:
  and mirrored:
+
''271° to 315° (63~32) ($3F~$20)''
  271° to 315° (63~32) ($3F~$20)
+
|When moving mostly left or mostly right, '''''Ground Speed''''' is set to '''''X Speed'''''. Otherwise, '''''Ground Speed''''' is set to:
 
 
In this range, When moving mostly left or mostly right, Ground Speed is set to X Speed. Otherwise, Ground Speed is set to:
 
 
   Y Speed * -sign(sin(Ground Angle))
 
   Y Speed * -sign(sin(Ground Angle))
 
+
|}
  
 
===When Going Upward===
 
===When Going Upward===
Line 284: Line 308:
 
The following ranges are inclusive.
 
The following ranges are inclusive.
  
====Slope====
+
{| class="prettytable" style="width: auto;"
Slope range is when the ceiling Ground Angle detected is in the range of:
+
!Range
 
+
!Values
  91° to 135° (191~160) ($BF~$A0)  
+
!Result
  and mirrored
+
|-
  226° to 270° (95~64) ($5F~$40)
+
|Slope
 
+
|''91° to 135° (191~160) ($BF~$A0)''
The Player reattaches to the ceiling and Ground Speed is set to Y Speed * -sign(sin(Ground Angle)).
 
 
 
====Ceiling====
 
Ceiling range is when the ceiling Ground Angle is in the range of:
 
 
 
  136° to 225° (159~96) ($9F~$60)
 
  
The Player hits their head like with any ceiling, and doesn't reattach to it. Y Speed is set to 0, and X Speed is unaffected.
+
and mirrored:
 +
''226° to 270° (95~64) ($5F~$40)''
 +
|The Player reattaches to the ceiling and '''''Ground Speed''''' is set to '''''Y Speed''''' * -sign(sin('''''Ground Angle''''')).
 +
|-
 +
|Ceiling
 +
|''136° to 225° (159~96) ($9F~$60)''
 +
|The Player hits their head like with any ceiling, and doesn't reattach to it. '''''Y Speed''''' is set to ''0'', and '''''X Speed''''' is unaffected.
 +
|}
  
 
==Notes==
 
==Notes==

Revision as of 16:19, 6 June 2023

Sonic Physics Guide
Collision
Physics
Gameplay
Presentation
Special

Notes:

  • The research applies to all four of the Sega Mega Drive games and Sonic CD.
  • This guide relies on information about tiles and sensors discussed in Solid Tiles.
  • This page is essentially part 2 of 2. This details Player physics when on slopes and specific methods of collision with steep slopes such as walls and ceilings. For part 1, describing specifics & basics of Player and Solid Tile collision, go to Slope Collision.

Constants

Constant Value
acceleration_speed 0.046875 (12 subpixels)
deceleration_speed 0.5 (128 subpixels)
friction_speed 0.046875 (12 subpixels)
top_speed 6
gravity_force 0.21875 (56 subpixels)
slope_factor 0.125 (32 subpixels)
slope_factor_rollup 0.078125 (20 subpixels)
slope_factor_rolldown 0.3125 (80 subpixels)

Introduction

Once you have the Player object able to collide with solid tiles, they need to move correctly over the terrain surface with momentum and physics. Knowing how sensors work will allow the Player move smoothly over terrain with different heights, and knowing how the Player's ground speed is affected by inputs to walk will allow him to move left and right, but that is not all there is to the engine. This guide will explain how the Player reacts to certain angles, and how 360 degree movement with momentum is achieved.

Slope Momentum

The Player's movement across the stage has to be influenced by angled ground in order to feel realistic.

Moving Along Slopes

In order for angled movement to be accurate, we need to make sure that the Player does not traverse an incline slope in the same amount of time as walking over flat ground of an equal width.

If Sonic were a simple "slope-less" platformer that required nothing but flat blocks, you would only need two speed variables: X Speed and Y Speed, the horizontal and vertical components of the Player's velocity. acceleration_speed, deceleration_speed, and friction_speed are added to X Speed; jump/bounce velocity and gravity_force are added to Y Speed (when the Player is in the air).

But when slopes are involved, while the Player moves along a slope, they're moving both horizontally and vertically. This means that both X Speed and Y Speed have a non-zero value. Simply adding acceleration_speed, deceleration_speed, or friction_speed to X Speed no longer works; imagine the Player was trying to run up a wall - adding to their horizontal speed would be useless because they need to move upward.

The trick is to employ a third speed variable (as the original engine does), Ground Speed. This is the speed of the Player along the ground, disregarding Ground Angle altogether. acceleration_speed, deceleration_speed, and friction_speed are applied to Ground Speed, not X Speed or Y Speed.

While on the ground, X Speed and Y Speed are entirely derived from Ground Speed every step before the Player is moved. Perhaps a code example is in order:

// Calculate X and Y Speed from Ground Speed
X Speed = Ground Speed * cos(Ground Angle)
Y Speed = Ground Speed * -sin(Ground Angle)

// Actually move via X Speed and Y Speed
X Position += X Speed;
Y Position += Y Speed;

No matter what happens to the Ground Angle, Ground Speed is preserved, so the game always knows what speed the Player is "really" moving at.

What's more, is that the position the Player is moved to with X Speed and Y Speed are where the player will next check for collision with the floor, so it is vital that the next position the player moves to for the next frame's checks aligns as much with the current slope direction as possible.

Slowing Down Uphill And Speeding Up Downhill

By this point, the Player should be able to handle any basic hills with an accurate velocity but they still need to slow down when going uphill and speed up when going downhill.

Fortunately, this is simple to achieve - with something called the Slope Factor. Just subtract Slope Factor * sin(Ground Angle) from Ground Speed at the beginning of every step. This only happens if the player is not in ceiling mode.

Ground Speed -= Slope Factor * sin(Ground Angle);

The value of Slope Factor is always slope_factor when running, but not so when rolling. When the Player is rolling uphill (the sign of Ground Speed is equal to the sign of sin(Ground Angle)), Slope Factor is slope_factor_rollup. When the Player is rolling downhill (the sign of Ground Speed is not equal to the sign of sin(Ground Angle)), Slope Factor is slope_factor_rolldown.

Note:

  • In Sonic 1 and 2, walking/running Slope Factor doesn't get subtracted if the Player is stopped (Ground Speed is 0). But in Sonic 3 & Knuckles, if Ground Speed is 0, the game will still subtract Slope Factor if the value of it is greater than or equal to 0.05078125 (13 subpixels). So that the Player can't stand on steep slopes - it will force them to walk down. Rolling slope factor, however, has no check for if Ground Speed is 0 in any of the games.

360 Degree Movement

So the Player can run over basic hills and ramps and ledges, and all that is great. But it is still not enough. They cannot make their way from the flat ground, up a steeper and steeper slope, to walls and ceilings without more work.

Why not? Well, because sensor A and B check straight downward, finding the height of the ground. There is just no way they can handle the transition to walls when everything is built for moving straight up and down on the Y-axis.

How can we solve this? By using four different modes of movement. This will take a little explaining.

The Four Modes

It seems pretty reasonable to assume that, because the Player can traverse ground in 360 degrees, the engine handles all 360 degrees in much the same way. But, in fact, the engine splits the angles into four quadrants, greatly simplifying things.

To better understand what I am talking about, imagine a simpler platformer without full loops, just a few low hills and ramps. All the character would need to do is, after moving horizontally, move up or down until they met the level of the floor. The angle of the floor would then be measured. The angle would be used to attenuate Ground Speed, but nothing more. The character would still always move horizontally and move straight up and down to adhere to floor level.

This is much like how the Sonic games do things. Only, when Ground Angle gets too steep, the Player switches "quadrant", moving from Floor mode to Right Wall mode (to Ceiling mode, to Left Wall mode, and back around to Floor mode, etc). At any one time, in any one mode, the Player behaves like a simpler platformer. The magic happens by combining all four modes, and cleverly switching between them smoothly.

So how and when does the Player switch mode?

When in Floor mode, and Ground Angle is steeper than 45° (224) ($E0), the engine switches into Right Wall mode. Everything is basically the same, only the sensors check to the right instead of downward, and the Player is moved to "floor" level horizontally instead of vertically.

Now that they're in Right Wall mode, if Ground Angle is shallower than 46° (223) ($DF), the engine switches back into Floor mode.

The other transitions work in exactly the same way, with the switch angles relative to the current mode.

When the mode is being calculated, it simply checks which quadrant the Player's Ground Angle is currently in, which will place the Player in the correct mode (ranges are inclusive):

Mode Angle Range
Floor Mode (start of rotation) 0° to 45° (255~224) ($FF~$E0)
Right Wall Mode 46° to 134° (223~161) ($DF~$A1)
Ceiling Mode 135° to 225° (160~96) ($A0~$60)
Left Wall Mode 226° to 314° (95~33) ($5F~$21)
Floor Mode (end of rotation) 315° to 360° (32~0) ($20~$00)

Note:

  • Since the classic games don't use degrees, and rather have angles ranging from 0 to 255, both approximate degree values and a more accurate decimal representation of the Hex values are included.

These ranges are symmetrical for left and right, but does favour the floor and ceiling modes, with their ranges being a degree or two wider.

You might rightly ask where the ground sensors are when in Right Wall mode. They're in exactly the same place, only rotated 90 degrees. Sensor A is now at the Player's Y Position + Width Radius instead of X Position - Width Radius. Sensor B is now at the Player's Y Position - Width Radius, instead of X Position + Width Radius. Instead of downward vertical sensor, they are now horizontal facing left, at his foot level (which is now "below" them, at X Position + Width Radius). They move and rotate in the same way for the other modes.

Yes, because the sensors move so far, it is possible for the Player to be "popped" out to a new position in the step in which he switches mode. However, this is hardly ever more than a few pixels and really isn't noticeable at all during normal play.

One more thing: I said that solid tiles were made of height arrays. Operative word: height. How do they work when in Right Wall mode? Well, each solid tile has two complementary height arrays, one used for when moving horizontally, the other for when moving vertically.

What about Left Wall and Ceiling mode? Wouldn't there need to be four height arrays? No, because tiles of those shapes simply use normal height arrays, just inverted. When in Ceiling mode, the Player knows that the height value found should be used to move them down and not up.

With these four modes, the Player can go over all sorts of shapes. Inner curves, outer curves, you name them. Here are some approximate example images with their angle values to help give you some idea of what this results in:

SPGInnerCurve.PNG SPGInnerCurveChart.PNG

You can observe Sonic's mode changing on the frame after his floor angle (Ground Angle) exceeds 45°. Sonic's position shifts a bit when the change occurs, due to the totally new collision angle and position.

SPGOuterCurve.PNG SPGOuterCurveChart.PNG

You may notice the Player's mode switches erratically on the convex curve, this is because his floor angle (Ground Angle) will suddenly decrease when switching to wall mode, causing it to switch back and forth until he is far enough down the curve to stabilise. This isn't usually noticeable, and happens less the faster you are moving.

Note: The reason the gifs show the mode switch being the frame after the angle threshold is reached is simply because the collision being shown is the one used for that frame, before the Player's Ground Angle updates, but after they have moved.

When to Change Mode

If you've checked the guide regarding the Main Game Loop you may notice the mode switching isn't mentioned at all, that's because the game doesn't actually ever "switch" the Player's mode. The Player's current "mode" is decided right before collision occurs. It will measure his angle, and decide which mode of collision to use right there and then. There is no "mode" state stored in memory. So effectively, the Player's mode updates whenever his angle (Ground Angle) does.

Since the Ground Angle is decided after floor collision (as a result of floor collision) the floor collision that frame has to use the previous frames angle, even though the Player has moved to a new part of the slope since then. This results in the Player's mode effectively changing 1 frame after the Player reaches one of the 45 degree angle thresholds, as seen above.

Falling and Slipping Down Slopes

At this point, slope movement will work rather well, but it's not enough just to slow the Player down on steep slopes. They need to slip down when it gets too steep and you are moving too slowly.

The angle range of slopes for slipping is when your Ground Angle is within the range 46° to 315° (223~32) ($DF~$20) inclusive.

In addition, the game will check if absolute Ground Speed falls below 2.5 (2 pixels, 128 subpixels).

So, when these conditions are met, what happens? Well, the Player will slip. This achieved by detaching the Player from the floor (clearing the grounded state), setting Ground Speed to 0, and employing the control lock timer.

SPGSlopeSlip.gif Next to Sonic you can see the control lock timer.

Here, when he gets too steep, Sonic detaches from the floor, Ground Speed is set to 0, and control lock timer is set.

But wait, why does Sonic not stop dead in his tracks if he become airbone and Ground Speed was set to 0? Well, if the floor isn't steep enough to freely fall from, the Player will immediately land back onto the floor and the Ground Speed will be restored from X/Y Speed as normal. Landing on the floor and speed conversion is further detailed up ahead in Landing On The Ground)

Okay, what about if the Player is on an even steeper floor?

SPGSlopeFall.gif

You can notice he detaches from the floor and control lock is set. It doesn't tick down until he lands, and even after the timer has begun, when he crosses the gap the timer pauses. The code for both the control lock timer and the slipping are only ran when grounded.

So, what about the timer? When the Player falls or slips off in the manner described above, the control lock timer is set to 30 ($1E) (it won't begin to count down until the Player lands back on the ground). While this timer is non-zero and the Player is on the ground, it prevents directional input from adjusting the Player's speed with the left or right buttons. The timer counts down by one every step when grounded, so the lock lasts about half a second. During this time only slope_factor and the speed the Player fell back on the ground with is in effect, so the Player will slip back down the slope.

In the above first example gif, you may notice the control lock timer counts down twice, this is purely because Sonic happened to be too steep and too slow still when the timer ended initially, and he slipped once again, seamlessly.

So, with some example code, it works like the following:

// Is player grounded?
if player is grounded
{
    if control_lock_timer == 0
    {
        // Should player slip?
        if abs(Ground Speed) < 2.5 and (Ground Angle is within range)
        {
            grounded = false;
            Ground Speed = 0;
            control_lock_timer = 30;
        }
    }
    else
    {
        // Tick down timer
        control_lock_timer -= 1; 
    }
}


Sonic 3 Method

Sonic 3 works a little differently, where Sonic will slip down at angles even shallower than 45°, and only detach from the floor when at angles even steeper than 45°.

Range Values
Slipping Ground Angle is within the range 35° to 326° (231~24) ($E7~$18) inclusive.
Falling Ground Angle is within the range 69° to 293° (207~48) ($CF~$30) inclusive.

Not only are there these new ranges, Ground Speed is now modified by 0.5 instead of being set to 0.

Here's how it works:

// Is the Player grounded?
if player is grounded
{
    if control_lock_timer == 0
    {
        // Should player slip?
        if abs(Ground Speed) < 2.5 and (Ground Angle is within slip range)
        {
            // Lock controls
            control_lock_timer = 30;
            
            // Should player fall?
            if (Ground Angle is within fall range)
            {
                // Detach
                grounded = false;
            }
            else
            {
                // Depending on what side the slope is, add or subtract 0.5 from Ground Speed
                if Ground Angle < 180°
                {
                    Ground Speed -= 0.5;
                }
                else
                {
                    Ground Speed += 0.5;
                }
            }
        }
    }
    else
    {
        // Tick down timer
        control_lock_timer -= 1; 
    }
}

Landing On The Ground

Both X Speed and Y Speed are derived from Ground Speed while the Player is on the ground. When they fall or otherwise leave the ground, X Speed and Y Speed are already the proper values for him to continue his trajectory through the air. But when they land back on the ground, Ground Speed must be calculated from the X Speed and Y Speed that they have when it happens. You might think that the game would use cos() and sin() to get an accurate value, but that is not the case. In fact, something much more basic happens, and it is different when hitting into a curved ceiling as opposed to landing on a curved floor, so I will cover them separately.

As you land the angle of the ground you touch is read (Ground Angle). The following covers the angle (Ground Angle) of the ground (floor or ceiling) that the Player touches as they land, and only happens the frame when they land when changing from in air to on ground.

Notes:

  • The landing ground speed reaction is also determined by what direction the player was moving in the air, which is the same calculation as noted in Slope Collision.

When Falling Downward

SPGLandFloor.png

The following ranges are inclusive.

Range Values Result
Shallow range 0° to 23° (255~240) ($FF~$F0)

and mirrored: 339° to 360° (15~0) ($0F~$00)

Ground Speed is always set to the value of X Speed.
Slope 24° to 45° (239~224) ($EF~$E0)

and mirrored: 316° to 338° (31~16) ($1F~$10)

When moving mostly left or mostly right, Ground Speed is set to X Speed. Otherwise, Ground Speed is set to:
 Y Speed * 0.5 * -sign(sin(Ground Angle))
Steep Slope 46° to 90° (223~192) ($DF~$C0)

and mirrored: 271° to 315° (63~32) ($3F~$20)

When moving mostly left or mostly right, Ground Speed is set to X Speed. Otherwise, Ground Speed is set to:
 Y Speed * -sign(sin(Ground Angle))

When Going Upward

SPGLandCeiling.png

The following ranges are inclusive.

Range Values Result
Slope 91° to 135° (191~160) ($BF~$A0)

and mirrored: 226° to 270° (95~64) ($5F~$40)

The Player reattaches to the ceiling and Ground Speed is set to Y Speed * -sign(sin(Ground Angle)).
Ceiling 136° to 225° (159~96) ($9F~$60) The Player hits their head like with any ceiling, and doesn't reattach to it. Y Speed is set to 0, and X Speed is unaffected.

Notes

  • This page is essentially part 2 of 2. This details Player physics when on slopes and specific methods of collision with steep slopes such as walls and ceilings. For part 1, describing specifics & basics of Player and Solid Tile collision, go to Slope Collision.