How to vary walkspeed with gradient

Hello, I’m making a climbing game and looking for a way to vary walk speed with gradient, however, I’m a pretty basic scripter and not sure how to create it.

The premise is to slow down with gradient, similar to climbing in real life, increased gradient = slower player walk speed. I haven’t been able to find a previous post on the forum with something like this so I decided to make a post.

2 Likes

You’re looking for acceleration, which is the increasing or decreasing of speed over time.

I would recommend searching up YouTube videos over acceleration to understand how to apply it to your game.

Here is a code sample that applies it:

local RunService = game:GetService('RunService')

--This accelerates Humanoid to Walkspeed over Time
local function acceleratePlayerTo(Humanoid, Walkspeed, Time)
   for i = 1, Time/(1/40) do
      Humanoid.WalkSpeed = i/(Time/(1/40)) * Walkspeed
      RunService.Stepped:Wait()
   end
end

Edit: Sorry about all the edits, I should’ve tested the function before posting this.

6 Likes

I think by “gradient” he means “slope.”

4 Likes

so flat surface is 100% speed or 1
and 90degrees up is 0% speed or 0
looks like a cos of slope is what you want

playerWalkSpeed = baseWalkSpeed*cos(phi)
(phi is the tilt angle on walking surface)

P.S. if you want down to speed up, perhaps
playerWalkSpeed = baseWalkSpeed*(1-sin(phi))
(range from 0 walk speed at straight up and 2x speed straight down)

As to calculating the immediate slope of a dynamic surface? Not sure; I would probably try ray casting to find relative altitudes(Position.Y)

3 Likes

I’m not confident about this, but here is how I’d approach this.

The end goal is to find the angle of the surface being walked on, which then you already know how to calculate WalkSpeed. The angle is between the surface normal vector (find using raycasting straight down), and the vector that went straight down. This angle can be found by using a rearranged dot product formula:

image

So you would math.acos() the equation in red. In Lua it’s written like this:

local angle = math.acos((NormalVector:dot(StraightDownVector))/NormalVector.Magnitude * StraightDownVector.Magnitude)
--make sure to math.deg() if you want your angle in degrees

Note that you might get undefined due to rounding errors, so make sure you round your input to math.acos.

Here is a drawing for why to choose the surface normal vector and the straight down vector:
image

The angle of the slope is the same as the angle between the two vectors (proven by geometry).

Anyways, now that you have the angle of the hill, I was also thinking the problem of whether the player should walk faster when they walk down… but then I realized that if you walk fast down a steep hill in real life, you’re gonna trip and fall! So it’s not necessary to differentiate between those two (walking up and down).

Let me know if I get anything wrong.

3 Likes

This can be simplified. The normal vector gets dotted with the down vector <0, -1, 0>. The zeroes get rid of the x and z components, so you only need the y component of the normal vector. Then, because both are unit vectors, you can get rid of the divisor. That leaves you with theta = math.acos(math.clamp(-y, -1, 1)).

Correct me if I’m wrong here.

EDIT: This is also a PSA to always clamp the inputs of your arcsines and arccosines!

EDIT 2: Your calculation has an algebra error. That last multiplication should be a division. It doesn’t matter though because the magnitude is 1.

3 Likes
game:GetService("RunService").Heartbeat:Connect(function()
	local ray = workspace:Raycast(rootPart.Position, Vector3.new(0, -humanoid.HipHeight - 2, 0), params)
	if ray then
		local vector = rootPart.CFrame:VectorToObjectSpace(ray.Normal)
		
		local dot = ray.Normal:Dot(-rootPart.CFrame.LookVector)
		local angle = math.floor(math.deg(math.acos((dot / (ray.Normal.Magnitude * rootPart.CFrame.LookVector.Magnitude)))))
end)

im pretty sure that’s how to get the angle, correct me if there’s an error in my code, after that just do a bunch of if then logic.