FPS Arms stutter and lag behind

I am creating an FPS arms script for my game, which works by modifying the Left and Right Shoulder Motor6Ds’ C0 properties every render step. It seems to stutter and lag behind and I’m not sure how to fix it. Here’s a video of the lag and the code.


Code:

local RunService = game:GetService("RunService")
local char = script.Parent
local torso = char.Torso
local hrp = char.HumanoidRootPart
local rightArm = char["Right Arm"]
local leftArm = char["Left Arm"]
local rightShoulder = torso["Right Shoulder"]
local leftShoulder = torso["Left Shoulder"]
local camera = game.Workspace.CurrentCamera
firstPerson = false

local function update()
	rightArm.LocalTransparencyModifier = rightArm.Transparency
	leftArm.LocalTransparencyModifier = leftArm.Transparency
	local distance = (hrp.Position + hrp.CFrame.UpVector * 1.5 - camera.CoordinateFrame.Position).Magnitude
	if distance <= 1 then
		if firstPerson == false then
			firstPerson = true
			return
		end
		rightShoulder.C0 = (camera.CoordinateFrame * CFrame.new(1, -1, 0)):ToObjectSpace(torso.CFrame):Inverse() * CFrame.Angles(0, math.pi/2, 0)
		leftShoulder.C0 = (camera.CoordinateFrame * CFrame.new(-1, -1, 0)):ToObjectSpace(torso.CFrame):Inverse() * CFrame.Angles(0, -math.pi/2, 0)
	else
		if firstPerson == true then
			firstPerson = false
			rightShoulder.C0 = CFrame.new(1, 0.5, 0) * CFrame.Angles(0, math.pi/2, 0)
			leftShoulder.C0 = CFrame.new(-1, 0.5, 0) * CFrame.Angles(0, -math.pi/2, 0)
		end
	end
end

RunService:BindToRenderStep("update", Enum.RenderPriority.Camera.Value + 1, update)
1 Like

You can lerp the values to make it smooth.

local RunService = game:GetService("RunService")
local char = script.Parent
local torso = char.Torso
local hrp = char.HumanoidRootPart
local rightArm = char["Right Arm"]
local leftArm = char["Left Arm"]
local rightShoulder = torso["Right Shoulder"]
local leftShoulder = torso["Left Shoulder"]
local lRightC0, lLeftC0 = nil, nil
local camera = game.Workspace.CurrentCamera
firstPerson = false

local function update()
	rightArm.LocalTransparencyModifier = rightArm.Transparency
	leftArm.LocalTransparencyModifier = leftArm.Transparency
	local distance = (hrp.Position + hrp.CFrame.UpVector * 1.5 - camera.CoordinateFrame.Position).Magnitude
	if distance <= 1 then
		if firstPerson == false then
			firstPerson = true
			return
		end
		
		lRightC0 = rightShoulder.C0
		lLeftC0 = leftShoulder.C0
		rightShoulder.C0 = lRightC0:Lerp((camera.CoordinateFrame * CFrame.new(1, -1, 0)):ToObjectSpace(torso.CFrame):Inverse() * CFrame.Angles(0, math.pi/2, 0), .25)
		leftShoulder.C0 = lLeftC0:Lerp((camera.CoordinateFrame * CFrame.new(-1, -1, 0)):ToObjectSpace(torso.CFrame):Inverse() * CFrame.Angles(0, -math.pi/2, 0), .25)
	else
		if firstPerson == true then
			firstPerson = false
			rightShoulder.C0 = CFrame.new(1, 0.5, 0) * CFrame.Angles(0, math.pi/2, 0)
			leftShoulder.C0 = CFrame.new(-1, 0.5, 0) * CFrame.Angles(0, -math.pi/2, 0)
		end
	end
end

RunService:BindToRenderStep("update", Enum.RenderPriority.Camera.Value + 1, update)

That sort of works, but whenever I make the lerp speed faster, the same stuttering issue occurs.

You could always try tweening the ‘CFrame’ values.

I’m not sure if tweening every render step would be good for performance.

Tweening probably isn’t ideal for performance. I believe the problem is that you’re updating the CFrames after the camera. Maybe try chaning:

RunService:BindToRenderStep("update", Enum.RenderPriority.Camera.Value + 1, update)

to

RunService:BindToRenderStep("update", Enum.RenderPriority.Camera.Value - 1 update)

So the CFrames update before?

1 Like

If this is client-side code then it’ll be fine.

Now the problem has swapped. Instead of the arms lagging behind the camera, it now lags behind the player during movement.

I’d just mess around with it. Usually I just use RunService.RenderStepped and that works for me.

It might have to do with the Motor6s updating. It might be a good idea to just clone the arms when in first person and directly set their CFrame.

Edit:
Here is an article about view models:

1 Like

I used RunService.Stepped instead and it worked perfectly. Thanks for your help though!

1 Like