How to detect and slow/haste a player MOVING up/down a ramp/slope

After managing to create other aspects of my own character control and movement system, I recently became interested in creating a depiction of how something or someone would move up or down an incline.

Overview
By default, a standard ROBLOX character object is able to travel up or down a ramp or other slope, though at a constant, unaffected-by-physics velocity. As you all may know in real life, any object and person traveling at any plane that isn’t flat would either require more or less effort than the latter.

I wish to apply this lively mechanic to my game in the form that the Character’s stamina is depleted more quickly while going upwards and slower downward depending on how sheer the incline is.

Issue
I have used and experimented with comparing Vector3 values as MoveDirection, testing Velocity on the Y axis only. While they both managed to ping a successful direction change, their results did not suffice since they seem to only operate overall (Even while midair; Player stamina should not be hindered further while midair) and not exclusively. (Player only loses more stamina while walking upward)

What I suggest:
From what I know of detecting a player’s humanoid’s MoveDirection, such a property (A vector3) is only checked upon a control script receiving input(?) that would simply allow the player to move up, down, left, or right. [WASD] or [Arrow Keys] default input.

Upwards MoveDirection doesn’t seem to be triggered by moving up or down a slope or incline via non-y axis movement keys.

As for the bottom line question,

How would I make the game able to define when a player is moving at an angle upwards or downwards without direct input?

4 Likes

You could keep using your current solution and add a jumping check to that with something like HumanoidStateType or Humanoid.Jumping. Or you could implement it all yourself with something like raycasting below the player and using velocity of HRP. Lots of ways to accomplish it, but if you already have a system you can adapt that one.

2 Likes

Hm. I like to implement things from scratch, and I do know a good amount about Raycasting, although at a basic level. Though mainly due to fact that I want to get very general with detection (Not only ramp/inclined parts but Smooth terrain as well)

Perhaps you could give a quick overview of how I would use raycasting in this kind of situation? (No need for code if necessary - An explanation will do)

You can use Ray casting for this. Ray casts return a surface normal vector representing the direction of the slope they hit. This works for circular or ramp objects. You can use this surface normal to find the steepness of a slope.

This formula returns the angle in radians between two vectors. Make sure these vectors are both unit vectors. The surface normal is and so should your floor normal. math.acos(a:Dot(b))

Next you’ll want to determine the direction the player is traveling. You can use HumanoidRootPart’s Velocity: HumanoidRootPart.Velocity.Unit. This will be their direction of travel.

Now you can determine the angle between their direction of travel and the unit vector. If this angle is under 90 degrees they are traveling down a slope. If this angle is above 90 degrees they are traveling up a slope. If this angle is equal to 90 degrees they are traveling along a flat surface.

7 Likes

This solution seems to be helping so much - I’m literally giving myself math lessons as of now. :joy:

Though I want to be clear on this - When you mentioned unit vectors, are they the ‘MoveDirection’ type of vectors where X,Y, or Z can only be 1? I’m just trying to make sure I’m getting the right values.

Alright. I’m very sorry for the previous misunderstanding; From a lot more studying the concept of Dot Products, I understand that you’ve given me a suggestion to use:

math.acos(a:Dot(b))

To my understanding:

a is the surface normal of the ramp/incline.
b is the floor normal used for reference when Dot is performed on a, So this should essentially always be a flat-valued vector, (Like (1/-1,0,0)?)

And in the end, whatever angle, in radians, that was returned from the Dot operation will be the angle the player is traveling ?

Yep a unit vector has a magnitude of 1 meaning x y and z add up to 1. If you use a flat floor normal you could detect slopes but by using the character’s velocity it will be relative to their movement.

Think about it this way, if the character moves perfectly straight along a flat floor the floor normal will face up and their velocity will face forwards. So you’ll get a value very very close to 90 degrees.

If they travel forwards down a slope the floor normal will face slightly forward relative to their velocity so you’ll get a value slightly less than 90 depending on the angle of the slope.

If they travel forwards up a slope the floor normal will face slightly away relative to their velocity so you’ll get a value slightly more than 90 depending on the angle of the slope.

So you could find the angle of the slope using a static up vector however you can also use the player’s velocity to determine how fast they should travel instead. It will be smoothly offset based on how “directly” they run down the slope. If they walk sideways across the slope their speed boost/reduction will be roughly 0.

3 Likes