Head follow camera system issues

  1. What do you want to achieve?
    Server side functionality to let everyone see where you’re looking. The player should locally see their own raw animation, and the data that gets sent to the RemoteEvent to be replicated should be seen by everyone else.

  2. What is the issue?
    The replicated anim overlaps the raw clientside anim causing jittery movement (when it’s just the LocalScript, it behaves exactly as it should - except of course only you can see your anim)
    robloxapp-20230604-2225449.wmv (1.5 MB)
    (Sorry about the horrendous quality, makes it look a lot smoother than it is :sweat_smile: I think the anim result is this jittery because of overlapping, as when tested other player anims end up smoother than your own. May not be :smiling_face_with_tear:)

  3. What solutions have you tried so far?
    Checking tutorials, asking ChatGPT, thinking of logic that’d make it work - to no avail. This is by far the most complicated system implemented in my game, but it’d be lovely to have as it’s a clothing store, and as you browse you look at the clothing you’re interested in (using proximity prompts) before trying it on or buying it. That’s why I tried any means to get it working, and the result was a script that works really nicely, but only on the client’s side :confused:

Place + code:
Anim.rbxl (43.9 KB)

LocalScript inside of StarterCharacterScripts

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

local Cam = game.Workspace.CurrentCamera

local Plr = game.Players.LocalPlayer
local Body = Plr.Character or Plr.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 HeadHorFactor = 1
local HeadVertFactor = 0.75
local BodyHorFactor = 0.15
local BodyVertFactor = 0.15
local UpdateSpeed = 0.15

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

Neck.MaxVelocity = 1/3

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

game:GetService("RunService").RenderStepped:Connect(function()
	local CamCF = Cam.CoordinateFrame
	if ((IsR6 and Body["Torso"]) or Body["UpperTorso"]) and Body["Head"] then
		local TrsoLV = Trso.CFrame.lookVector
		local HdPos = Head.CFrame.p
		if IsR6 and Neck or Neck and Waist then
			if Cam.CameraSubject:IsDescendantOf(Body) or Cam.CameraSubject:IsDescendantOf(Plr) then
				local Dist, Diff
				Dist = (Head.CFrame.p - CamCF.p).magnitude
				Diff = Head.CFrame.Y - CamCF.Y
				if not IsR6 then
					Neck.C0 = Neck.C0:lerp(NeckOrgnC0 * Ang((aSin(Diff/Dist) * HeadVertFactor), -(((HdPos - CamCF.p).Unit):Cross(TrsoLV)).Y * HeadHorFactor, 0), UpdateSpeed / 2)
					Waist.C0 = Waist.C0:lerp(WaistOrgnC0 * Ang((aSin(Diff/Dist) * BodyVertFactor), -(((HdPos - CamCF.p).Unit):Cross(TrsoLV)).Y * BodyHorFactor, 0), UpdateSpeed / 2)
				else
					Neck.C0 = Neck.C0:lerp(NeckOrgnC0 * Ang(-(aSin(Diff/Dist) * HeadVertFactor), 0, -(((HdPos - CamCF.p).Unit):Cross(TrsoLV)).Y * HeadHorFactor), UpdateSpeed / 2)
				end
				local animationData = {
					NeckC0 = Neck.C0,
					WaistC0 = Waist.C0,
					IsR6 = IsR6
				}
				AnimatePlayerEvent:FireServer(animationData)
			end
		end
	end
end)

Script inside of ServerScriptService

local AnimatePlayerEvent = ReplicatedStorage:WaitForChild("AnimatePlayerEvent")

local function animatePlayer(player, animationData)
	local character = player.Character
	if not character then
		return
	end

	local humanoid = character:FindFirstChild("Humanoid")
	if not humanoid then
		return
	end

	local isR6 = animationData.IsR6
	local neckC0 = animationData.NeckC0
	local waistC0 = animationData.WaistC0

	local torso = isR6 and character:FindFirstChild("Torso") or character:FindFirstChild("UpperTorso")
	local neck = isR6 and torso:FindFirstChild("Neck") or character:FindFirstChild("Head"):FindFirstChild("Neck")
	local waist = not isR6 and torso:FindFirstChild("Waist")

	if neck and waist then
		neck.C0 = neckC0
		waist.C0 = waistC0
	end
end

AnimatePlayerEvent.OnServerEvent:Connect(animatePlayer)

RemoteEvent called AnimatePlayerEvent inside of ReplicatedStorage

Here’s something :thinking: wish he elaborated on the replicates for everyone else part though :frowning:

Another thing

I had a similar issue and ended up creating two neck and waist roots, one for the client and one for the server.

On the client that owns the character, clone both the neck and the waist, parent each motor6d to its respective body part, then disable (not destroy) the original motor (make sure to set Humanoid.RequiresNeck to false during this process, you can set it back to true when done).

Then every .1 seconds or so (I would advise against firing every frame, it may hog your request budget of 50kbps iirc and it will look fine if fired every .1 seconds), fire the server with the necessary data, then on the server set the C0 of the original neck and waist joints.

On a sidenote, I would recommend calculating C0 on the server as well, as an exploiter could spoof the CFrame that is sent and make their body parts weirdly positioned. That’s up to you though.

Here’s the thread if you want to read up:

2 Likes

I found my solution using ClientEvents, I didn’t even know they existed haha.

I’ll make it free for anyone to use since it’s not entirely my original work, but it should work entirely :smiley: enjoy

Anim.rbxl (44.5 KB)

1 Like

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