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)

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).

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

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.

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

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

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.

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:

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