Rotating a characters head to match Camera rotation

I want to have the characters head match the Up/Down and Left/Right rotation of the camera while being clamped to facing forwards ( e.g. you look backwards the head rotates forwards like with the example code bellow ). I currently have the following code but it doesn’t work when the character turns to any other direction then straight:

local Plr = game:GetService( "Players" ).LocalPlayer

game:GetService( "RunService" ).RenderStepped:Connect( function ( )
	
	local Neck = Plr.Character and Plr.Character:FindFirstChild( "Torso" ) and Plr.Character.Torso:FindFirstChild( "Neck" )
	
	if Neck then
		
		local Y, X, Z = workspace.CurrentCamera.CFrame:toEulerAnglesYXZ( )
		
		Y = -Y - math.rad( 90 )
		
		if X > 0 and X < 1.57 then
			
			X = X + ( 1.57 - X ) * 2
			
		elseif X < 0 and X > -1.57 then
			
			X = X + ( -1.57 - X ) * 2
			
		end
		
		Neck.C0 = CFrame.new( 0, 1, 0 ) * CFrame.Angles( X, Y, Z )
		
	end
	
end )

I’m really bad with CFrames.

12 Likes

I can’t help you with your problem, but I believe the new, recommended version of FromEulerAnglesYXZ is Angles.

CFrame.Angles(0,0,0)

Good luck :wink:

1 Like

I’m currently playing around with your code, but while you wait you should take into account the fact that this will not work with R15 character rigs, which are becoming more and more prominent than R6 as time goes by.

Instead you can retrieve the neck in Character.Head.Neck, which can just be cross-compatible with the following code:

Character:FindFirstChild("Neck", true) -- Recursive FindFirstChild call
2 Likes

This was just test code before I tidied it up, but thanks.

1 Like

Should work with both R15 and R6 and all that.

local CFNew, CFAng = CFrame.new, CFrame.Angles
local asin = math.asin

local Camera = workspace.CurrentCamera
local Player = game.Players.LocalPlayer
repeat wait() until Player.Character
local Character = Player.Character
local Root = Character:WaitForChild("HumanoidRootPart")
local Neck = Character:FindFirstChild("Neck", true)
local YOffset = Neck.C0.Y
game:GetService("RunService").RenderStepped:Connect(function()
    local CameraDirection = Root.CFrame:toObjectSpace(Camera.CFrame).lookVector.unit
    if Neck then
        Neck.C0 = CFNew(0, YOffset, 0) * CFAng(0, -asin(CameraDirection.x), 0) * CFAng(asin(CameraDirection.y), 0, 0)
    end
end)
4 Likes

FYI that won’t work if they die or get a new character, here’s the fixed code for anyone wondering (Plus some optimisations and a check to make sure the camera is focused on the player):

local CFNew, CFAng, CFtoObjectSpace = CFrame.new, CFrame.Angles, CFrame.new( ).toObjectSpace
local asin, pi, hpi = math.asin, math.pi, math.pi / 2

local Plr= game.Players.LocalPlayer

game:GetService("RunService").RenderStepped:Connect(function()
    if Plr.Character then
        local Root, Neck, Humanoid, Camera = Plr.Character:FindFirstChild("HumanoidRootPart"), Plr.Character:FindFirstChild("Neck", true), Plr.Character:FindFirstChild("Humanoid"), workspace.CurrentCamera
        if Root and Neck and Humanoid and Camera.CameraSubject then
            local R6 = Humanoid.RigType == Enum.HumanoidRigType.R6
            if Camera.CameraSubject.Parent == Plr.Character then
                local CameraDirection = CFtoObjectSpace(Root.CFrame, Camera.CFrame).lookVector.unit
                Neck.C0 = CFNew(Neck.C0.p) * CFAng(0, -asin(CameraDirection.x), 0) * (R6 and CFAng(-hpi + asin(CameraDirection.y), 0, pi) or CFAng(asin(CameraDirection.y), 0, 0))
            else
                Neck.C0 = R6 and CFNew(Neck.C0.p) * CFAng(-hpi, 0, pi) or CFNew(Neck.C0.p)
            end
        end
    end
end)
23 Likes

This doesn’t replicate to the server though. How can I get it to do that?

1 Like

Send the location to the server when you want it updated and have the server change it. I’d also advise against using RenderStepped, and use Heartbeat

1 Like

Please do not necrobump threads.
cc @nick_hz @jackctaylor

Well since it’s already been necrobumped, forgive me for chiming in. Clients have network ownership over their character, my approach is to use this and rotate a part based on the camera’s orientation that will replicate over to the server. From this part’s orientation the server can handle the rotating without remote traffic. It still counts towards the 50KB/s and still has the latency associated with replicating. Works well enough for me though.

I didn’t realize I did? It just popped up in my feed for some reason.

I’ve had several likes on this post over the past few weeks so I thought I’d provide a more up to date implementation of this that I’m using. This version replicates to other clients at a configurable rate (defaults to every 1/20 of a second, I found this to be sufficient) and uses a fake head so that collisions/physics aren’t affected by the rotation.

You just need the HeadRotation script in ServerScriptService and the HeadRotation localscript in StarterPlayer.StarterPlayerScripts

HeadRotation.rbxl (19.3 KB)

43 Likes

Can I also use this for rotation the character to the position of the camera? This is for a gliding system and I will be changing the neck to the humanoid root part.

You’d likely have to alter the code quite a bit to make it rotate the whole character correctly

Hello excuse me, but how can I do this from the attached project but for the torso, hands, and head of the character, lean like this:

image

2 Likes

You can also do head cloning just incase the player having headless, I encountered this problem a bit ago and fixed it by using a Server script checking if the neck is found, if not (Headless usually doesn’t have a neck) then it will create a new one by Cloning the old once and using the same property’s as the old head by making it transparent and such.