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 help is appreciated!