Head turning script causing performance problems

I’ve made a head turning script that uses the camera direction to determine where to turn the head, and the arms as well (if a tool is equipped).

The script works by sending CFrame values to a remote event and then the server sending back those values to every client for each one to process it (to take less load on the server).

The problem being that randomly this seems to cause massive performance issues, players and myself have had the ping randomly start climbing into the thousands and not going down, and I’ve pinpointed it to the head turning script specifically.

I’ve tried everything that I could find on the devforum and other sources to try and fix this issue, from encoding the CFrame values to lower performance cost, to using unreliable remote events to prevent it, but nothing has seemed to work.

I’m fairly new to dealing with performance issues, so if this is amateur then sorry in advance, but I need to solve this.

Here’s the code.

-- CLIENT

services.RS.RenderStepped:Connect(function(delta)
	local camDir = limbs.Root.CFrame:toObjectSpace(cam.CFrame).LookVector

	local leftArmGoal, rightArmGoal, neckGoal = CFrame.new(-1,0.5,0) * CFrame.Angles(math.asin(camDir.Y),-1.55,0), CFrame.new(1,0.5,0) * CFrame.Angles(math.asin(camDir.Y),1.55,0), CFrame.new(0, jointOrigins.Neck.Y, 0) * CFrame.Angles(3 * math.pi/2, 0, math.pi) * CFrame.Angles(0, 0, -math.asin(camDir.x)) * CFrame.Angles(-math.asin(camDir.Y), 0, 0)
	local goals = {}

	if char:FindFirstChildOfClass("Tool") then
		goals = {
			["Left Shoulder"] = {"Left Shoulder", encodeCFrame(leftArmGoal)},
			["Right Shoulder"] = {"Right Shoulder", encodeCFrame(rightArmGoal)},
			["Neck"] = {"Neck", encodeCFrame(neckGoal)},
		}	
	else
		goals = {
			["Left Shoulder"] = {"Left Shoulder", encodeCFrame(jointOrigins["Left Shoulder"])},
			["Right Shoulder"] = {"Right Shoulder", encodeCFrame(jointOrigins["Right Shoulder"])},
			["Neck"] = {"Neck", encodeCFrame(neckGoal)},
		}
	end

	remotes.JS:FireServer(goals)

	for _, g in pairs(goals) do
		if g[1] == "Neck" then
			services.TS:Create(char.Torso[g[1]], neckTweenInfo, {C0 = decodeCFrame(g[2])}):Play()
		else
			services.TS:Create(char.Torso[g[1]], armTweenInfo, {C0 = decodeCFrame(g[2])}):Play()
		end
	end
end)

function updateJoints(joint: Motor6D, goalEncoded)
	local ragdolled = charStat:FindFirstChild("Ragdolled")
	if joint then
		local goal = decodeCFrame(goalEncoded)
		local chosenInfo = nil
		if joint.Name == "Neck" then
			chosenInfo = neckTweenInfo
		else
			chosenInfo = armTweenInfo
		end
		if not joint:FindFirstAncestor(player.Name) then
			if not ragdolled or ragdolled.Value == false then
				services.TS:Create(joint, chosenInfo, {C0 = goal}):Play()
			else
				services.TS:Create(joint, chosenInfo, {C0 = jointOrigins[joint.Name]}):Play()
			end
		end
	end
end

remotes.JC.OnClientEvent:Connect(updateJoints)
-- SERVER

function updateJoints(player: Player, jointPosTable)
	local char = player.Character
	if not char then return end
	for _, jointData in pairs(jointPosTable) do
		for _, m6d in pairs(char:GetDescendants()) do
			if m6d:IsA("Motor6D") and m6d.Name == jointData[1] then
				remotes.JC:FireAllClients(m6d, jointData[2])
			end
		end
	end
end

remotes.JS.OnServerEvent:Connect(updateJoints)

Any help is appreciated.

personally it seems a little redundant to be using tweens to move the cframe when it already happens every frame therefore probably looks smooth enough, and i dont think it would be particularly performant to be creating a tween every frame. you could substitute it with :Lerp() every frame in order to make the cframe slowly move into place.

The problem with lerping is that it creates this off-putting stuttering in the movement every so often.

Ex:

Bumping because its been awhile

Have you tried it without the server portion? I’d imagine that updating every single player every frame with everyone’s head positions would get pretty heavy.

The server portion communicates the positions to other clients, it doesn’t actually carry out any of the calculations, that’s all on the client’s side. I’m not 100% sure but I’m pretty sure the server is not the issue, because the ping spiking usually only happens to one person at a time.

Bumping due to this still being unsolved

are you multiplying the alpha with deltatime?

It’s definitely not a good idea to fire a Remote Event from every client every frame. Why not make the RenderStepped only manage the head turn for the local player, and then make a seperate loop that fires the Remote Event less frequently? It’ll update quickly for your own character on your screen, but for others it may be slightly delayed which hopefully shouldn’t matter too much.

Tweening every frame can be quite resourceful and honestly you could use bodygyros/alignorientation instead which are much smoother and aren’t as rresourceful.

If you still wanan save resources you can lerp instead.