Moving Arms and Head with camera

I need to create a mechanic in my first person shooter where the pitch of a character’s arms and head rotate accordingly to the player’s camera. This is not a viewmodel. I have already implemented viewmodels and now need a server side way to show other players what the client is looking at. Also, it has to work with existing idle + fire animations

I’ve linked a video of how pitch is changed in the animation editor just as a quick reference.

I answered this exact question on an older topic.

I have since however, updated the system to run a lot better.
Here’s the updated code:

CLIENT

local RunService = game:GetService('RunService')
local Event = script:WaitForChild('LookEvent')
local Player = game.Players.LocalPlayer
local Camera = workspace:WaitForChild('Camera')
local Character = Player.Character or Player.CharacterAdded:Wait()
local Head = Character:WaitForChild('Head')
local Humanoid = Character:WaitForChild('Humanoid')

--// Here are the settings :3
local updateRate = 1/20  --// How many times per second the server will update, if you want 60, do 1/60
local accumulated = 0

local HeadHorFactor = 0.8
local HeadVertFactor = 0.7

local TorsoHorFactor = 0.8
local TorsoVertFactor = 0.7

local UpdateSpeed = 0.5
--// Settings end :3

local Torso
local Waist
local Neck
local WaistOrgnC0

if Humanoid.RigType == Enum.HumanoidRigType.R6 then
	Torso = Character:WaitForChild('Torso')
	Neck = Torso:WaitForChild('Neck')
else
	Torso = Character:WaitForChild('UpperTorso')
	Neck = Character:WaitForChild('Head'):WaitForChild('Neck')
	Waist = Torso:WaitForChild('Waist')
	WaistOrgnC0 = Waist.C0
end

local RightShoulder = Torso:WaitForChild('Right Shoulder')
local RightShoulderOrgnC0 = RightShoulder.C1
local LeftShoulder = Torso:WaitForChild('Left Shoulder')
local LeftShoulderOrgnC0 = LeftShoulder.C1

local runService = game:GetService("RunService")

if Humanoid.RigType == Enum.HumanoidRigType.R6 then
	Torso = Character:WaitForChild('Torso')
	Neck = Torso:WaitForChild('Neck')
else
	Torso = Character:WaitForChild('UpperTorso')
	Neck = Character:WaitForChild('Head'):WaitForChild('Neck')
end

local RootPart = Character:WaitForChild('HumanoidRootPart')
local Mouse = Player:GetMouse()

local Ang = CFrame.Angles
local aSin = math.asin
local aTan = math.atan

local NeckOrgnC0 = Neck.C0

local Dist
local Diff
local TorsoPosition

function getMath()
	if Humanoid.RigType == Enum.HumanoidRigType.R6 then

		local HeadPosition = Head.CFrame.Position
		local TorsoLookVector = Torso.CFrame.lookVector

		Dist = (Head.CFrame.Position - Camera.CFrame.Position).magnitude
		Diff = Head.CFrame.Y - Camera.CFrame.Y

	elseif Humanoid.RigType == Enum.HumanoidRigType.R15 then

		local HeadPosition = Head.CFrame.Position
		TorsoPosition = Torso.CFrame.Position
		local TorsoLookVector = Torso.CFrame.lookVector

		Dist = (Head.CFrame.Position - Camera.CFrame.Position).magnitude
		Diff = Head.CFrame.Y - Camera.CFrame.Y
	end
end

function updateChar()
	if Humanoid.RigType == Enum.HumanoidRigType.R6 then
		local HeadPosition = Head.CFrame.Position
		local TorsoLookVector = Torso.CFrame.lookVector
		getMath()

		Neck.C0 = Neck.C0:Lerp(NeckOrgnC0*Ang(-(aSin(Diff/Dist)*HeadVertFactor), 0, -(((HeadPosition - Camera.CFrame.Position).Unit):Cross(TorsoLookVector)).Y*HeadHorFactor),UpdateSpeed/2)
		
		local MouseOriginPosition = Player:GetMouse().Origin.Position
		local X = -(math.asin((MouseOriginPosition - Camera.CFrame.Position).unit.y)) * -1
		
		local _, Y, Z = RightShoulder.C0:ToEulerAnglesXYZ()
		local OldRightC0 = RightShoulder.C0
		RightShoulder.C0 = CFrame.new(RightShoulder.C0.Position) * CFrame.Angles(X, Y, Z)

		local _, Y, Z = LeftShoulder.C0:ToEulerAnglesXYZ()
		LeftShoulder.C0 = CFrame.new(LeftShoulder.C0.Position) * CFrame.Angles(X, Y, Z)
		
	elseif Humanoid.RigType == Enum.HumanoidRigType.R15 then
		local HeadPosition = Head.CFrame.Position
		local TorsoPosition = Torso.CFrame.Position
		local TorsoLookVector = Torso.CFrame.lookVector
		getMath()

		Neck.C0 = Neck.C0:lerp(NeckOrgnC0*Ang((aSin(Diff/Dist)*(HeadVertFactor/2)), -(((HeadPosition - Camera.CFrame.Position).Unit):Cross(TorsoLookVector)).Y*(HeadHorFactor/2), 0), UpdateSpeed/2)
		Waist.C0 = Waist.C0:lerp(WaistOrgnC0*Ang((aSin(Diff/Dist)*(TorsoVertFactor/2)), -(((TorsoPosition - Camera.CFrame.Position).Unit):Cross(TorsoLookVector)).Y*(TorsoHorFactor/2), 0), UpdateSpeed/2)
	end
end



runService.RenderStepped:Connect(function(deltaTime)
	accumulated += deltaTime
	updateChar() --// Update the character for the client, this line can be removed or kept, up to you
	
	if accumulated >= updateRate then --// If how much time between frames and last update if greater than or equal to our goal
		accumulated = 0
		if Humanoid.RigType == Enum.HumanoidRigType.R6 and Player.CameraMode == Enum.CameraMode.LockFirstPerson then
			Event:FireServer(Neck.C0, nil, RightShoulder.C0, LeftShoulder.C0) --// Update the character for other players (server)
		elseif Humanoid.RigType == Enum.HumanoidRigType.R15 and Player.CameraMode == Enum.CameraMode.LockFirstPerson then
			Event:FireServer(Neck.C0, Waist.C0) --// Update the character for other players (server)
		end
	end
end)

SERVER

local Event = script.Parent:WaitForChild('LookEvent')

local Character = script.Parent.Parent.Parent
local Head = Character:WaitForChild('Head')
local Humanoid = Character:WaitForChild('Humanoid')

local Torso
local Waist
local Neck

if Humanoid.RigType == Enum.HumanoidRigType.R6 then
	Torso = Character:WaitForChild('Torso')
	Neck = Torso:WaitForChild('Neck')
else
	Torso = Character:WaitForChild('UpperTorso')
	Neck = Character:WaitForChild('Head'):WaitForChild('Neck')
	Waist = Torso:WaitForChild('Waist')
end

local RightShoulder = Torso:WaitForChild('Right Shoulder')
local LeftShoulder = Torso:WaitForChild('Left Shoulder')

Event.OnServerEvent:Connect(function(Player, NeckC0, WaistC0, RSc0, LSc0)
	if Humanoid.RigType == Enum.HumanoidRigType.R6 then

		Neck.C0 = NeckC0
		RightShoulder.C0 = RSc0
		LeftShoulder.C0 = LSc0
		
	elseif Humanoid.RigType == Enum.HumanoidRigType.R15 then
		
		Neck.C0 = NeckC0
		Waist.C0 = WaistC0
	end
end)

(also, #help-and-feedback:scripting-support)

I might also quickly add, the system I have isn’t 100% identical to your video, as the arms rotate around their respective shoulder and the head around the neck, but it still gets almost the same result.

1 Like

Where does the server-script go to?

Anywhere.

Personally, I just had a local script under StarterCharacterScripts, and had an event and the server-script under the local-script.
Optimized? Probably not. Works? Yes.