(Note: this method doesn’t smooth the rotation)
The affect you are trying to achieve can be obtained through the use of roblox’s Motor6D object.
Firstly this code can be run either on the server or the client, but as its purely aesthetic, I am running the code on the client with a LocalScript
, however this means that other players will not be able to see the affect.
We need the character’s HumanoidRootPart
as it will provide information necessary for orienting the character.
Next we need to find the Motor6D
, The specific Motor6D
we want is called Root
and for R16 characters it is located under the LowerTorso
part.
after we find these parts we are going to want to save the original C0
of the Root
part for later
we also need to create a RaycastParams
object as we will need to find the normal of the platform the player is walking on.
next we need to create a loop to update the rotation of the player, the Motor6D
reference recommends using RunService
’s PreSimulation
event.
Inside the loop the first thind we are going to do is create a Raycast
going downwards from the characters HumanoidRootPart
then we check if the ray is non null.
after checking, we store the HumanoidRootPart
’s CFrame
, then we create a flattened version of the normal, by multiplying it by a Vector3D
of the value (1, 0, 1)
and getting its Unit
next we need to find the DotProduct
of the flattened normal and both the CFrame
’s LookVector
and RightVector
, we are going to multiply these into the angle we get later.
to find the angle we are going to find the DotProduct
of the rays non-flattened normal, and the vertical axis or Vector3D.yAxis
and then get the arc cos of the resulting value.
Lastly we are going to adjust the Motor6D
’s C0
property, by setting it to the original C0
value and multiplying it into the CFrame
created using CFrame.fromEulerAnglesXYZ(x,y,z)
where we set x to the negative of the multiplication of the LookVector
’s DotProduct
and the angle, the y value to 0 and the z value to the negative of the multiplication of the RightVector
’s DotProduct
and the angle
Here's what the code should look like upon completion
local character = script.Parent
local RootPart = character:WaitForChild("HumanoidRootPart")
local torso = character:WaitForChild("LowerTorso")
local attachment = torso:WaitForChild("Root")
local runService = game:GetService("RunService")
wait(1)
local originalCFrame = attachment.C0
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {character}
runService.PreSimulation:Connect(function()
local ray = workspace:Raycast(RootPart.Position, -12*Vector3.yAxis, rayParams)
if ray then
local cframe: CFrame = RootPart.CFrame
local flattened = (ray.Normal*Vector3.new(1,0,1)).Unit
local look = cframe.LookVector:Dot(flattened)
local right = cframe.RightVector:Dot(flattened)
local angle = math.acos(Vector3.yAxis:Dot(ray.Normal))
attachment.C0 = originalCFrame *
CFrame.fromEulerAnglesXYZ(angle * - look, 0, angle * - right)
end
end)