Context
I have a rather odd issue that I have been trying to solve for the past month. I have talked to many different people about the issue, but no one yet has seemed to provide a solution. I have also searched the DevForum quite a lot trying to see if someone has a similar issue, which they did, but the solutions given to them simply did not work for me.
In my game, I am trying to make an object move via BodyMovers which is controlled by user input. This system works fine, however, I started to notice that the object looked like it was stuttering. After doing more investigation into this, I realized the issue was with the Camera and not the object.
Now if I were to just set the camera to a defined position offset from the object, the stuttering is solved. However, I want my camera to smoothly follow the object, which introduced the stuttering issue. I am using a SmoothDamp function to move the camera, and yes the SmoothDamp function works as it is expected to in other situations (no issue with the function).
[Vector3] SmoothDamp(current: Vector3, target: Vector3, velocity: Vector3, smoothTime: number, maxSpeed: number, deltaTime: number)
For the purposes of this post, I have set up a reproduction place where I move a single Part via BodyVelocity. The network owner of the Part is set to the client and all works as expected minus the horrible stuttering.
Another important thing to note is I am linearly interpolating the values that are fed into the BodyVelocity.
function module.Lerpf(a, b, c)
return clamp(a + (b - a) * c, min(a,b), max(a,b))
end
Failed Solutions
These appeared to be the best posts that matched my specific issue, however the solutions given did not solve my issue even though it seemed to solve other’s issues.
My Code
--SERVICES
local UserInput = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
--MODULES
local Util = require(script:WaitForChild("Util"))
--VARIABLES
local Player = game:GetService("Players").LocalPlayer
local RootPart = workspace:WaitForChild("Root")
local Camera = workspace.CurrentCamera
--CONFIGURATION
local moveDir = 0
local activeSpeed = 0
local maxSpeed = 20
local speedAcc = 4
local BV = nil
local lastPhysicsDt, lastRenderDt = 0, 0
local lastCFrame = nil
--MODULE
local Controller = {Active = false, CONN_KeyPress = nil, CONN_KeyRelease = nil}
function Controller.Start()
if Controller.Active then return end
Controller.Active = true
Camera.CameraType = Enum.CameraType.Scriptable
Camera.CameraSubject = RootPart
Camera.Focus = RootPart.CFrame
BV = RootPart:WaitForChild("BodyVelocity")
--INPUT LISTENERS
Controller.CONN_KeyPress = UserInput.InputBegan:Connect(function(input, gpe)
if gpe then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.A then
moveDir -= 1
elseif input.KeyCode == Enum.KeyCode.D then
moveDir += 1
end
end
end)
Controller.CONN_KeyRelease = UserInput.InputEnded:Connect(function(input, gpe)
if gpe then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.A then
moveDir += 1
elseif input.KeyCode == Enum.KeyCode.D then
moveDir -= 1
end
end
end)
--UPDATE LISTENERS
Controller.CONN_Stepped = RunService.Stepped:Connect(function(_, deltaTime)
lastPhysicsDt = deltaTime
end)
RunService:BindToRenderStep("Control", Enum.RenderPriority.Camera.Value + 3, Controller.Update)
end
function Controller.Stop()
if not Controller.Active then return end
Controller.CONN_KeyPress:Disconnect()
Controller.CONN_KeyPress = nil
Controller.CONN_KeyRelease:Disconnect()
Controller.CONN_KeyRelease = nil
RunService:UnbindFromRenderStep("Control")
Controller.Active = false
end
function Controller.Update(deltaTime)
local sumDelta = lastRenderDt + lastPhysicsDt
activeSpeed = Util.Lerpf(activeSpeed, moveDir * maxSpeed, speedAcc * lastPhysicsDt)
BV.Velocity = Vector3.new(0, 0, activeSpeed)
if not lastCFrame or not (lastCFrame == RootPart.CFrame) then
local target = (RootPart.CFrame * CFrame.new(-20, 0, 0)).Position
local pos = Util.SmoothDamp(Camera.CFrame.Position, target, Vector3.new(), 0.04, math.huge, sumDelta)
Camera.CFrame = CFrame.new(pos) * CFrame.Angles(0, -math.pi / 2, 0)
lastCFrame = RootPart.CFrame
lastRenderDt = 0
else
lastRenderDt = deltaTime
end
end
return Controller
Video
Reproduction File
StutterIssue.rbxl (29.6 KB)