How to get slope angle from Normal?

How do I know the slope in degrees from the raycast result Normal?

I am trying to make a custom “MaxSlopeAngle” property, so that when the player climbs too steep a hill they ragdoll and slide down.

1 Like

One way is to get the angle between the UpVector and normal vector to get angle of inclination.

Another way is to math.atan2( y^2 / sqrt(x^2+y^2) ) the components, it will result in an edge case if the normal vector is directly (0,1,0) or (0,-1,0) but you can should be able to take into account if the x and z is close to zero.

2 Likes

Edit: Actually don’t know what I was thinking, @dthecoolest has the right idea. Here’s how you would do that.

local function getAngle(normal)
    return math.deg(math.acos(normal:Dot(Vector3.yAxis))
end

Old post:
This is what you need to get the angle in degrees.

local function getAngle(normal)
    return 90 - math.deg(math.atan(normal.Y / math.sqrt(normal.X^2 + normal.Z^2)))
end
15 Likes

@dthecoolest @JarodOfOrbiter

Thank you guys it does seem to work!

@JarodOfOrbiter both equations you gave are giving almost the same results. If you don’t mind can you explain the difference and a little bit of the math so I understand why it works?

The first method at the top of my post uses this wonderful headache called linear algebra to get the angle difference between the normal and the straight up direction (0, 1, 0). I won’t go into exactly how Dot works because it’s annoying college level crap and has some prerequisites before you can understand it, but when you use acos on the dot product of two vectors, you get the radian angle between those vectors.

The second method is what sprang to mind when I saw your question and it’s more simple to explain, IMO, because it’s just high school trigonometry. The first thing that happens is this. math.sqrt(normal.X^2 + normal.Z^2). If you’re familiar with trig at all, you might recognize the Pythagorean Theorem, or a^2 + b^2 = c^2. This part basically takes the X axis and Z axis and collapses it into a single axis, because we don’t particularly care about X or Z individually. This combines them. Then I use atan(y / x) to get the angle in radians between y and x. This was actually a mistake because I rushed it and didn’t think it through. Typically you want rise over run, but we don’t want that this time. If I flip the sides of the division symbol, I can get rid of the 90 - bit at the start.

return math.deg(math.atan(math.sqrt(normal.X^2 + normal.Z^2) / normal.Y))

Here’s the kicker though. I really shouldn’t have rushed. This is the real solution right here. No need for the dot product or even the X or Z. We don’t care about X or Z and we don’t need them. Y has all we need.

local function getAngle(normal)
    return math.deg(math.acos(normal.Y))
end

This is the same as what I was doing with the dot product, but the dot product is only useful if we want to find the angle between two vectors. Since one of our vectors is guaranteed to be straight up, we don’t actually need it. So here you go.

Oh also since I didn’t explain what acos was. Acos (or arc cosine) is the same as cos-1 or inverse cosine. It’s the mathematical inverse function of cosine. It basically undoes whatever cosine does. So cosine(angle) would actually give us the Y vector for a part tilted at that angle.

8 Likes

Thank you so much! This is a great explanation and super helpful!

1 Like

How do I put this in a script for sliding thougj