Issues player lean in FPS Mode

Hello again!
I need to make my player lean when he looks down in first person mode

For example, the player starts the game like this (looking normal)
image

But if I look down then it leans like in this picture
image

How can I achieve this?

I’ve tried thousands of ways and I can’t do it, could someone give me a hand :pray:

You would have to fix the rotations of the LeftHip and RightHip Motor6Ds. Since R6 does not have a Waist Motor6D, achieving this is more complicated. To rotate the Torso, you would have to rotate the Root Motor6D.

My advice is, rotate the Root Motor6D so that the torso is looking in the direction your camera is looking at, then, use the rotational offset of the Root Motor6D from it’s initial rotation (which you should have by making simple calculations), and subtract it from the rotation of the LeftHip and RightHip Motor6Ds.

I can attempt to do something like this and let you know if I figure something out.

2 Likes

Oh I understand, this is something complicated, I don’t know how to do it, do you think you could explain me step by step please :frowning:

Después de experimentar un poco con que eje usar y usando regla de 3, esto es lo más sencillo que se puede usar.

--// Instances
local Cam = workspace.CurrentCamera
local Player = game:GetService("Players").LocalPlayer
Character = Player.Character or Player.CharacterAdded:Wait()
M6D_T = Character:WaitForChild("HumanoidRootPart"):WaitForChild("RootJoint")
M6D_LL = Character:WaitForChild("Torso"):WaitForChild("Left Hip")
M6D_LR = Character:WaitForChild("Torso"):WaitForChild("Right Hip")

--// Formule
local CameraAngle = Vector2.new(-45, 45)
local Torso = Vector2.new(20, -20)
game:GetService("RunService").Stepped:Connect(function()
	--// Fix camera on a axi.
	local T = {Cam.CFrame:ToOrientation()}
	T[1] = math.rad(math.clamp(math.deg(T[1]), CameraAngle.X, CameraAngle.Y))
	Cam.CFrame = CFrame.new(Cam.CFrame.Position) * CFrame.fromOrientation(unpack(T))

	--// Rule of 3
	local R = 0
	if T[1] > 0 then
		R = (Torso.X*math.deg(T[1]))/CameraAngle.Y
	elseif T[1] < 0 then
		R = (Torso.Y*math.deg(T[1]))/CameraAngle.X
	end

	--// Rotate legs and torso
	M6D_T.Transform *= CFrame.Angles(math.rad(R),0,0)
	M6D_LL.Transform *= CFrame.Angles(0,0, math.rad(-R))
	M6D_LR.Transform *= CFrame.Angles(0,0, math.rad(R))
end)

Fórmula de la cámara tomada de esta publicación.
Que Torso y CameraAngle sean vectores no influye, solo los use para ordenarlo mejor.

1 Like

Okay, here’s my step by step guide on how to do this:

Step 1


Begin by making the RootJoint inside the HumanoidRootPart rotate toward the direction of the camera. You can do this by creating a simple update function that gets called every frame. I went ahead and made some code for this:

-- services
local runService = game:GetService("RunService")

-- player stuff
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local character = player.Character

-- character stuff
local humanoid = character:WaitForChild("Humanoid")
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local torso = character:WaitForChild("Torso")

-- put all motors inside the character into a dictionary for easy access
-- remove spaces from motor names
local motors = {}
for _, motor in pairs(character:GetDescendants()) do
	if motor:IsA("Motor6D") then
		-- gives a table with the motor as the first index, and the motor initial c0 as the second
		motors[string.gsub(motor.Name, "%s+", "")] = {motor, motor.C0}
	end
end

-- update function
function renderStepped(dt)
	-- get useful motor6ds
	local root = motors["RootJoint"]
	local leftHip = motors["LeftHip"]
	local rightHip = motors["RightHip"]
	-- get angles of camera
	local y, x, z = camera.CFrame:ToEulerAnglesYXZ()
	-- rotate motor6ds
	root[1].C0 = root[2] * CFrame.fromEulerAnglesYXZ(-y, 0, 0)
end

-- connections
runService:BindToRenderStep("updateMotors", Enum.RenderPriority.Character.Value, renderStepped)

Assuming the player spawns in with an R6 rig, and this code is placed in a local script inside of StarterCharacter, you should have something that looks like this:

Step 2


Now that we have something that rotates the torso, we need to rotate the legs to go down toward the ground. We can achieve this by rotating the Hip Motor6Ds with the same rotation that we rotated the Torso. Let’s attempt to do this with some code. Adding the bottom two lines to the renderStepped function, we should be able to rotate the Hip joints along with the Root joint.

-- update function
function renderStepped(dt)
	-- get useful motor6ds
	local root = motors["RootJoint"]
	local leftHip = motors["LeftHip"]
	local rightHip = motors["RightHip"]
	-- get angles of camera
	local y, x, z = camera.CFrame:ToEulerAnglesYXZ()
	-- rotate motor6ds
	root[1].C0 = root[2] * CFrame.fromEulerAnglesYXZ(-y, 0, 0)
	leftHip[1].C0 = leftHip[2] * CFrame.fromEulerAnglesYXZ(0, 0, y)
	rightHip[1].C0 = rightHip[2] * CFrame.fromEulerAnglesYXZ(0, 0, -y)
end

The result should look like this:

Step 3


Now you may notice there is a small problem. As the Torso begins to rotate away from its original rotation, the legs begin to go up off the ground. We can solve this by using some trigonometry. Let me explain.

As the Torso begins to rotate, the space between the legs and the ground becomes the same as half the size of the Y component of the Torso’s size. This is because the Torso’s position remains the same, but the rotation of the Torso is changing. As the rotation of the Torso reaches an offset of 90 degrees, it’s basically sideways. So the offset of the Torso from the ground becomes

math.abs(0.5 * Torso.Size.Y - 0.5 * Torso.Size.Z)

Now how do we calculate this depending on the rotation of the Torso at the given moment? Well, since we’re dealing with the rotation of the camera about the X axis (rotation up and down), we can take the sine of the angle and multiply it by the offset we calculated above. Of course, the sine function can give a positive or a negative number, so we’ll take the absolute value of the function as well. In code, this looks like this:

-- update function
function renderStepped(dt)
	-- get useful motor6ds
	local root = motors["RootJoint"]
	local leftHip = motors["LeftHip"]
	local rightHip = motors["RightHip"]
	-- get angles of camera
	local y, x, z = camera.CFrame:ToEulerAnglesYXZ()
	local groundOffset = math.abs(0.5 * torso.Size.Y - 0.5 * torso.Size.Z) * math.abs(math.sin(y))
	-- rotate motor6ds
	root[1].C0 = root[2] * CFrame.fromEulerAnglesYXZ(-y, 0, 0) * CFrame.new(0, 0, -groundOffset)
	leftHip[1].C0 = leftHip[2] * CFrame.fromEulerAnglesYXZ(0, 0, y)
	rightHip[1].C0 = rightHip[2] * CFrame.fromEulerAnglesYXZ(0, 0, -y)
end

After adding this to the code, we should fix the problem we had earlier. We end up with something like this:

Hopefully this helps you out!

3 Likes

Wow! Thank you very much I really have learned a lot, the truth is quite complex I think I need to learn some of math haha :smiley: thanks you brother!

Sotr thanks you too! :smiley:

1 Like

By the way… And if I wish this could be seen for all players, would it work too? or should i do a fireserver somewhere?

To do that you would have to make use of RemoteEvents because the Motor6Ds would be changing in the client and not replicated. I would send the rotation of camera in the Y direction and calculate everything on each client.

Oh I see! So should I do something like this?
Would it be okay to do something like this?

-- connections

local connection = runService:BindToRenderStep("updateMotors", Enum.RenderPriority.Character.Value, renderStepped)

-- events

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEventTest")

remoteEvent:FireServer(connection)
1 Like

Yes, it would be exactly like that. Then, in the server, you would do remoteEvent:FireClient(player) to all the players besides the player that sent that signal. That way, if one player sent a signal, the other players would get the signal besides the player that sent it so that only other players are updating the player’s character and the player isn’t updating their character twice, causing jittery movement.

Brilliant! I did it and it is working perfect! Thank you very much for your help and sorry for the inconvenience

Hey this looks awesome however I have an issue, and it’s when the players torso leans there is movement in the legs. How would i approach keeping the legs in the same spot rather than moving them?

1 Like

This method is outdated now, you should make another post just so if people have the same problem they can look at it too. I’ll gladly answer this in another post.

To clarify, Motor6Ds shouldn’t have updated C0 or C1 properties anymore, so this code is basically obsolete.