Source vs GoldSrc Movement: Downward Slopes

- Games

The slope-related movement physics in the GoldSrc (Half-Life 1) engine and the Source (Half-Life 2) engine are fairly similar. However, there is one caveat around moving down slopes: in the GoldSrc engine you will bounce down slopes somewhat often, while in the Source engine you will smoothly move down almost any slope unless you collide with them while moving extremely quickly.

Bouncing off a ramp in Team Fortress Classic (GoldSrc)

We'll get to some gameplay implications of this a bit later, but first let's establish the mechanics behind 'bouncing down a slope' because it's a bit weird.

ClipVelocity making the velocity parallel to the slope🔗

As discussed in much more detail in the rampsliding post, whenever a player collides with a surface, a function called ClipVelocity will transform their velocity to be parallel to that surface. One characteristic of this function that's worth noting is that the closer to parallel the velocity already is, the more speed will be maintained after the collision.

velocity maintained during ClipVelocity: 77%
Speed loss due to ClipVelocity at different approach angles

But if ClipVelocity makes the velocity parallel to the surface, then where does the bounce come from? Well, that's where a quirk of the movement physics comes into play.

Flattening velocity when on the ground🔗

After ClipVelocity finishes and most of the rest of the movement code is run (the player actually moves a tiny amount along the slope using the result of ClipVelocity, etc), there are two things towards the end of the movement code that matter:

  1. CatagorizePosition [sic] is called and, in the case we care about here, it will determine that we are now 'on the ground' (since we've just collided with a ramp that is not too-steep-to-stand-on).
  2. Whenever a player is 'on the ground', the vertical component of their velocity always gets set to zero (i.e. their velocity is made to be purely horizontal by just dropping all vertical speed).

This means that the resulting velocity after colliding with a shallow-enough downward slope is always perfectly horizontal, and that the player's final speed is determined solely by the horizontal components of the result of ClipVelocity.

The final velocity (in purple) after flattening the result of ClipVelocity (shown as dashed)

Because velocity is now flat (rather than parallel to the surface we collided with), it's very likely that on one of the next few subsequent ticks, the player will move far enough off the slope that they will no longer be considered 'on the ground,' and thus will start falling again (e.g. they will bounce off the ramp).

You're not leaving this slope that easily🔗

If this were the whole story, then players would do nothing but bounce whenever they tried to move down a slope. To avoid bouncing when just trying to walk down a slope, there is a small correction made whenever a player is 'on the ground' but slightly above a surface:

This means that it is slightly harder to 'escape' a slope once the movement code has determined you are 'on the ground.' If you try running down a slope, your velocity will be flat the whole way (zero vertical component), but you will be snapped back to the surface of the slope each tick as long as you are moving slowly enough. So, the only way to actually bounce down a slope is to horizontally move far enough in one tick that the slope is more than 2 units below your new position (i.e. you have to still be going fairly fast horizontally after colliding with the slope in order to bounce off of it).

result: bouncing off the slope
Diagram showing the necessary horizontal distance traveled in one tick to bounce off a 30° slope in the GoldSrc engine

What the Source engine does differently🔗

Everything above is the same in the Source engine, however, the miniature 'snap the player onto the ramp if they're close enough to it' functionality was substantially upgraded: instead of just checking 2 units below the player, it now checks for slopes up to 18 units below the player (technically, the distance it checks is determined by StepSize, but StepSize is typically set to 18). This is done during a new function called StayOnGround which is run during WalkMove (which is only called when the player is on the ground).

result: bouncing off the slope
StepSize = 18
Diagram showing the necessary horizontal distance traveled in one tick to bounce off a 30° slope in the Source engine

What a movement 'tick' means in each engine🔗

We've now established that being able to bounce off a slope is limited/dictated by the distance traveled in a single tick. If ticks occur very slowly, then each tick will involve a longer distance traveled than if ticks happen very quickly. For example, if a player is moving 1000 units/sec and the movement is processed at 1 tick/sec, then the player will move 1000 units each tick; however, if the movement is processed at 100 ticks/sec, then the player will move 10 units per tick.

So, what determines how long each 'tick' is? Well, this differs per engine:

Gameplay ramifications in Fortress games🔗

In the games Team Fortress Classic (GoldSrc engine) and Fortress Forever (Source engine), bouncing down ramps can be used to a player's advantage. In both games, concussion grenades allow you to intentionally hit downward ramps at high speeds and the ability to preserve that momentum via bouncing off the ramp is extremely advantageous (versus hitting the ramp and slowing down to either runspeed or the bunnyhop cap).

Similar techniques that exploit bouncing off a ramp at high speeds in Team Fortress Classic (GoldSrc engine) [left] and Fortress Forever (Source engine) [right]

Due to the differences between the engines discussed previously, the bounce in the Team Fortress Classic clip would not have worked in Fortress Forever. The bounce in the Fortress Forever clip in this case uses two additional helping hands:

We'll get into the details of what will and won't generate a bounce in Fortress Forever specifically, but first let's explore which factors matter for how fast the bounce ends up being.

Understanding how to maximize the bounce speed🔗

There are two main factors that determine the final speed after a bounce (besides the entry speed):

  1. The angle of approach: Hitting the ramp while moving close to parallel to its surface means more velocity maintained during ClipVelocity
  2. The angle of the ramp: The shallower the ramp itself, the less velocity is lost when it is 'flattened' (e.g. the more horizontal the velocity is after ClipVelocity, the less vertical velocity there is to lose when it's flattened)

So, in theory, the fastest bounce would be achieved by hitting a very shallow ramp while moving very close to parallel to its surface. In practice, this is made extremely difficult for two different reasons:

  1. Steeper ramps are easier to collide with at more-parallel approach angles. It's not easy to collide with a near-horizontal downwards ramp while moving nearly horizontally yourself.
  2. The shallower the ramp, the faster you have to be going to be able to bounce at all. To bounce, you need to move far enough in one tick that the ramp becomes far enough below you that you don't get snapped back down onto its surface. The shallower the ramp, the further you'd need to travel to clear that threshold.
velocity maintained after bounce: 66%
23% decrease during ClipVelocity
13% decrease during velocity flattening
Speed maintained through a bounce at various approach and slope angles

The speed needed to bounce in Fortress Forever🔗

Fortress Forever has a default of -tickrate 66 (and this is what most/all servers use), so we can use that to determine specifically what speeds are necessary to achieve a bounce for a given approach and slope angle combination.

≥ 2058
≥ 2460
Speed necessary to bounce off a slope in Fortress Forever (-tickrate 66)

If you play around with the above diagram, you'll quickly notice that the speed threshold for bouncing off any ramp is quite high. In practice, you'll likely need to double concussion jump down into a ramp in order to get the speed necessary achieve a bounce at all.