Player character jittering when using a Scriptable camera

I wanted to make a custom third person camera controller.
Everything works fine and the camera itself moves smoothly, but the players character seems to jitter slightly.

Video of what i mean:

When commenting the only line of code that rotates the player, the jittering stays.

I tired looking for different fixes, but nothing seems to work for me. The jittering disappears when i dont smooth out the camera CFrame using Lerp, but i really wont to keep it. Is there any other way to go about this?

Here is the code im using:
(Im new to lua and roblox, apologies for the horrible code.)

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local player = Players.LocalPlayer
local cam = Workspace.CurrentCamera

-- SETTINGS
local MOUSE_SENS = 0.15
local PITCH_LIMIT = 75
local SMOOTHNESS = 10
local CAMERA_OFFSET = Vector3.new(4, 1.5, 6)
local PITCH_CLOSE_AMOUNT = 6
local PITCH_HEIGHT_AMOUNT = 5

-- STATE
local yaw, pitch = 0, 0
local smoothCFrame = cam.CFrame
local character, root

-- CHARACTER SETUP
local function setupCharacter(char)
	character = char
	root = char:WaitForChild("HumanoidRootPart")
end
if player.Character then setupCharacter(player.Character) end
player.CharacterAdded:Connect(setupCharacter)

-- CAMERA SETUP
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
UserInputService.MouseIconEnabled = false
cam.CameraType = Enum.CameraType.Scriptable

-- MOUSE INPUT
UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		yaw -= input.Delta.X * MOUSE_SENS
		pitch = math.clamp(pitch - input.Delta.Y * MOUSE_SENS, -PITCH_LIMIT, PITCH_LIMIT)
	end
end)

-- EXPONENTIAL SMOOTHING HELPER
local function exponentialLerp(dt, smoothness)
	return 1 - math.exp(-smoothness * dt)
end

-- MAIN CAMERA LOOP
RunService.RenderStepped:Connect(function(dt)
	if not character or not root then return end

	root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0, math.rad(yaw), 0)

	local pitchFactor = pitch / PITCH_LIMIT
	local offset = CAMERA_OFFSET
	- Vector3.new(0, 0, PITCH_CLOSE_AMOUNT * math.abs(pitchFactor))
		+ Vector3.new(0, -PITCH_HEIGHT_AMOUNT * pitchFactor, 0)

	local desiredCamPos = root.Position + root.CFrame:VectorToWorldSpace(offset)

	local lookDir = (CFrame.fromAxisAngle(root.CFrame.RightVector, math.rad(pitch)) * CFrame.new(root.CFrame.LookVector)).Position.Unit
	local targetCFrame = CFrame.new(desiredCamPos, desiredCamPos + lookDir)

	local t = exponentialLerp(dt, SMOOTHNESS)
	smoothCFrame = smoothCFrame:Lerp(targetCFrame, t)
	cam.CFrame = smoothCFrame
end)
1 Like

Try swapping out RenderStepped to PostSimulation or PreSimulation

Switching to Post/PreSimulation doesn’t seem to work and still has the same jitter. (Also tried different RunService options)

Try use RunService:BindToRenderStep or RunService.PreRender, PreRender fires first when frame draw in your game

make it so it changes when velocity changes, not position!