Stuck on accounting for a camera offset with a head and torso camera rotation script

I forked this public head and torso rotation script where, R6 included, your character will basically look in the direction of your camera. I modified it to work with the NPC and to also replicate to clients for optimisation and so it looks smoother for other players. I also removed the option to use your mouse instead.
With a lot of effort I’ve gotten it working beautifully (though the code is probably awful), but now I’m stuck… In first person, players have a camera offset of (0, 0, -2) which leads to the effect of looking up and down being greatly mitigated.

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

local Cam = workspace.CurrentCamera

local Player = game.Players.LocalPlayer
local Body = Player.Character or Player.CharacterAdded:Wait()
local Head = Body:WaitForChild("Head")
local Hum = Body:WaitForChild("Humanoid")
local Core = Body:WaitForChild("HumanoidRootPart")
local IsR6 = (Hum.RigType.Value == 0)
local Trso = (IsR6 and Body:WaitForChild("Torso")) or Body:WaitForChild("UpperTorso")
local Neck = (IsR6 and Trso:WaitForChild("Neck")) or Head:WaitForChild("Neck")
local Waist = (not IsR6 and Trso:WaitForChild("Waist"))
local TS = game:GetService("TweenService")

local HeadHorFactor = 0.75
local HeadVertFactor = 0.375
local BodyHorFactor = 0.25
local BodyVertFactor = 0.25
local UpdateSpeed = 0.1

local NeckOrgnC0 = Neck.C0
local WaistOrgnC0 = (not IsR6 and Waist.C0)

local LoadCharacter = game.ReplicatedStorage:WaitForChild("LoadCharacter")

Neck.MaxVelocity = 1/3

local AnimatePlayerEvent = game.ReplicatedStorage:WaitForChild("AnimatePlayerEvent")
local AnimateAssistantEvent = game.ReplicatedStorage:WaitForChild("AnimateAssistantEvent")
local ReceiveAnimationEvent = Instance.new("RemoteEvent")
ReceiveAnimationEvent.Name = "ReceiveAnimationEvent"
ReceiveAnimationEvent.Parent = script

local sendTimer = 0
local sendInterval = 0.1 

game:GetService("RunService").RenderStepped:Connect(function()
	local CamCF = Cam.CFrame
	if ((IsR6 and Body:FindFirstChild("Torso")) or Body:FindFirstChild("UpperTorso")) and Body:FindFirstChild("Head") then
		local TrsoLV = Trso.CFrame.lookVector
		local HdPos = Head.CFrame.Position
		if IsR6 and Neck or Neck and Waist then
			local Dist, Diff
			Dist = (Head.CFrame.Position - CamCF.Position).magnitude
			Diff = Head.CFrame.Y - CamCF.Y
			if not IsR6 then
				Neck.C0 = Neck.C0:lerp(NeckOrgnC0 * Ang((aSin(Diff/Dist) * HeadVertFactor), -(((HdPos - CamCF.Position).Unit):Cross(TrsoLV)).Y * HeadHorFactor, 0), UpdateSpeed)
				Waist.C0 = Waist.C0:lerp(WaistOrgnC0 * Ang((aSin(Diff/Dist) * BodyVertFactor), -(((HdPos - CamCF.Position).Unit):Cross(TrsoLV)).Y * BodyHorFactor, 0), UpdateSpeed)
			else
				Neck.C0 = Neck.C0:lerp(NeckOrgnC0 * Ang(-(aSin(Diff/Dist) * HeadVertFactor), 0, -(((HdPos - CamCF.Position).Unit):Cross(TrsoLV)).Y * HeadHorFactor), UpdateSpeed)
			end

			local currentTime = os.clock()
			if Hum.Health > 0 and currentTime - sendTimer >= sendInterval then
				sendTimer = currentTime

				local animationData = {
					NeckC0 = Neck.C0,
					IsR6 = IsR6
				}

				if not IsR6 then
					animationData.WaistC0 = Waist.C0
				end

				AnimatePlayerEvent:FireServer(animationData)
			end
		end
	end
end)

AnimatePlayerEvent.OnClientEvent:Connect(function(otherPlayer, animationData)
	local otherCharacter = otherPlayer.Character
	if otherCharacter then
		local otherBody = otherPlayer.Character or otherPlayer.CharacterAdded:Wait()
		local otherHead = otherCharacter:FindFirstChild("Head")
		local otherTrso = (animationData.IsR6 and otherBody:WaitForChild("Torso")) or otherBody:WaitForChild("UpperTorso")
		local otherNeck = (animationData.IsR6 and otherTrso:FindFirstChild("Neck")) or otherHead and otherHead:FindFirstChild("Neck")

		if otherBody and otherHead and otherTrso and otherNeck then
			if not animationData.IsR6 then
				local otherWaist = (not animationData.IsR6 and otherTrso:WaitForChild("Waist"))
				TS:Create(otherNeck, TweenInfo.new(.1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {C0 = animationData.NeckC0}):Play()
				TS:Create(otherWaist, TweenInfo.new(.1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {C0 = animationData.WaistC0}):Play()
			else
				TS:Create(otherNeck, TweenInfo.new(.1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {C0 = animationData.NeckC0}):Play()
			end
		end
	end
end)

AnimateAssistantEvent.OnClientEvent:Connect(function(neckData)
	local assistant = workspace["NPC's"].Assistant
	local assistantHead = assistant:FindFirstChild("Head")
	local assistantNeck = assistantHead:FindFirstChild("Neck")

	TS:Create(assistantNeck, TweenInfo.new(.1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {C0 = neckData.NeckC0}):Play()
end)

local function ResetAnimation()
	if not IsR6 then
		Neck.C0 = NeckOrgnC0
		Waist.C0 = WaistOrgnC0
	else
		Neck.C0 = NeckOrgnC0
	end
end

LoadCharacter.OnClientEvent:Connect(ResetAnimation)

Where would I look to change this properly? Everything I’ve tried has yielded poor results. I can change it depending on what the player’s perspective is myself, I just have no idea what to change :confused: help is appreciated!

Are you wondering where the offset is?

Maybe you could multiply the factor when entering in first person, or do a completely different function to make your character rotate using the camera orientation instead of the direction/distance of the camera.

I tried multiplying the original head and camera CFrames by (0, 0, -2), (0, 0, 2), and all sorts of values in-between in different parts of the code. My best result was it looking down/up too quickly/slowly where it shouldn’t, like it was too sensitive, so something wasn’t right. Like if you looked very level forward it’d be fine but the moment you look slightly up your character will stare at the sky lol. Weirdly the best results usually didn’t affect third person at all so, yeah that shows how much I don’t understand the math behind this code :skull:
That second suggestion actually sounds hella smart, since that’s all that’s needed in first person… That’s a great way to look at it and 100% what I’ll try and do, thanks :smile: (preparing to mark as the solution)

1 Like

I’ve tried a lot of different things but unfortunately I’m stumped haha. Might have to pay someone to spoon feed me or something cause I know what to do I just can’t figure it out
This aside I found out the original script and all versions of it didn’t work properly the same way with first person either. Should’ve figured

I took another angle and I think I got my answer - look vector :scream: I just printed the values, checked them live, and swapped the first person and the third person bits to make the testing a heap easier (I kept going through the trouble of a two player server test, stupid…) and realised look vector is exactly what I was looking for. Right now first person just needs different “hor” and “ver” factors because it’s approximately a quarter weaker looking up and down if that makes sense but yeah I’ll work it out

I also got some funny results while testing lol so enjoy this (this wasn’t nearly the funniest)

Got it working very nicely. Not sure why but I had to add a multiple of 1.5 to each of the factors for fp which ended up looking nearly identical going in and out in game. Woohoo

1 Like

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