How to get angle of platform player standing on

I’m trying to rotate a character, dependant on the slope they are standing on.

For example, if I’m standing on this slope, like so


I want them to be rotated, like so

Instead of being straight.

I’ve tried using BodyForces, they seem to do nothing though

local function Render()
	local Part = workspace:Raycast(Character.HumanoidRootPart.Position, Vector3.new(0, -10, 0))
	if not Part then return end
	
	local BodyForce = Character.PrimaryPart.BodyForce
	BodyForce.Force = Part.Normal
end

RunService.RenderStepped:Connect(Render)
5 Likes

You should be able to achive this using Raycasts.
Edit: to make it clear, you can get the part with it and then just use the parts rotation angles.

1 Like

I am using Raycasting.

My problem is, I don’t know why this don’t work

1 Like

You are getting past the Part section, right? Then just use the Part to get the Rotation Angles. You dont need any BodyForce for this in my guess.

1 Like

What happens if it’s wedges tho? Wedges don’t have a rotation applied, yet they are still sloped

1 Like

This is pretty tricky. You can use FindPartOnRay() to get the surface normal directly below the player (FindPartOnRay returns the surface normal). I can’t provide a solution, but hopefully that gets you started… The other thing to note is that the humanoid is going to try and prevent you from rotating the character since it automatically tries to rotate the character the right way up (I don’t have a solution to this, but it is a problem that being aware of will hopefully save you some time).

1 Like

Simple answer is it comes down to using trigomtry. Yes, that scary thing that comes before you learn Calculus. :doh:

Anyways, what you’re trying to solve here is to find the angle/grade of the wedge part. Now unlike a regular part that is rotated, you can’t really figure out the angle of the slope by just reading a property. But you can figure out the side lengths through the Size vector!

Now for this to be really simple I will just be using math.atan2(). Let’s just say it’s amazing because it keeps the math simple on our end by doing most of the hard work for us.

image

As you can see, we give it 2 numbers and it gives us theta! (theta is just a fancy variable name for angles!) :happy2:

So basically we just need to give it a y and x value and then convert the result it gives from us to degrees! Now Wedge parts, we care about the Y and Z axises since they determine how long and tall the wedge’s slope is.

And so ta da, we now have a function that returns the angle of our wedge part!

local function GetAngle(wedge)
	return (math.deg(math.atan2(wedge.Size.Y,wedge.Size.Z)))
end
2 Likes

What happens if I’m not using wedges though? I need it to work on virtually any surface, wedge, rotated part, etc.

1 Like

I can’t say for spheres on what to do, but for a part you can just use it’s UpVector.

Most of the time you can also just use the Part’s Orientation property to find how it’s rotated. It’s just not possible with the top surface of a wedge because of it’s unique shape.

You can get the angles by casting multiple rays like 2, 1st at the character and 2nd in front of character. You can get the rotation by doing CFrame.new(pos1,pos2) and getting the CFrame opponents.

Edit: Of course you should do more rays for accuracy, 2 is a just an example.

1 Like

Use this method of casting three rays to find the plane surface the character is standing on then do the cross product with the characters current look vector to get the new right vector similar to this resource:

1 Like

Is this what you want to do? If so, then you only need 1 ray. The example isn’t perfect, but here’s something to work from. It goes in StarterCharacterScripts

local char = script.Parent
local rootPart = char:WaitForChild("HumanoidRootPart")
local xzGyro = Instance.new("BodyGyro")
local yGyro = Instance.new("BodyGyro")
yGyro.MaxTorque = Vector3.new(0,3e5,0)
yGyro.P = 5e5
xzGyro.MaxTorque = Vector3.new(3e5,0,3e5)
xzGyro.P = 5e5
xzGyro.Parent = rootPart

while wait(0.5) do
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {char}
	params.FilterType = Enum.RaycastFilterType.Blacklist
	local result = workspace:Raycast(rootPart.Position, Vector3.new(0,-10,0), params)
	
	yGyro.CFrame = CFrame.new(rootPart.Position, rootPart.Position + char.Humanoid.MoveDirection*10)
	
	if (result) then
		print(result.Normal)
		local currentRightVector = rootPart.CFrame.RightVector
		local upVector = result.Normal
		local newFacialVector = currentRightVector:Cross(upVector)
		xzGyro.CFrame = CFrame.fromMatrix(rootPart.Position, currentRightVector, upVector, newFacialVector)
	end
	
end
18 Likes

Got you covered:
I solved this a while ago:

6 Likes

Tried to convert it to R6 but failed, can you please?