Align R6 character with the floor

Hello,
Nice to meet you.

As the title says, I want to make my R6 character to align with the floor.
What happens occurs usually-


What I want to achieve -


As it shows in the screenshots, I am planning to align the RootJoint of the HumanoidRootPart
I tried many other solutions but all of them did not give me the expected output.
I am trying to find a solution which is modular and which is compatible with the server.

2 Likes

(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)
1 Like

Thanks for the reply, I will check it out as soon as I reach home

1 Like

I just realized that some of my reply has some errors, being that there is no such thing as R16, and I gave information for R15, but for R6 characters the Root should be located under a part that’s named Torso (Edit: sorry again, its been a while since I worked with R6 characters, it seems it is located in the HumanoidRootPart and named RootJoint)

2 Likes

Other minor changes, I was testing with the R6 rig and notices, that it would look at the ground on a flat surface, if we add a check for ray. Normal ~= Vector3.yAxis on the same line as we check if the ray exists, and add an else statement, that sets the C0 to its original value, this problem disappears, another thing is the R6 rig doesn’t need a negative for its angle in the x, and the y and z values are swapped:

these changes should look something like this:
if ray and ray.Normal ~= Vector3.yAxis then
	...	
else
	attachment.C0 = originalCFrame
end

and

CFrame.fromEulerAnglesXYZ(look * angle, -right * angle, 0)
3 Likes

It works fine and decently!
Thank you soo much for your solution!! It helps a lot.
Thanks

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.