How do I mitigate "choppy custom camera" lerping?

Hello scripters (particularly camera manipulators), what would be your preferred solution for stuttering or choppy custom cameras? I’m quite a “beginner” in this field, so I may need some help.

Currently, it doesn’t stutter as it’s not lerping the CurrentCamera CFrame; however, when I tried to lerp it with a small number as its alpha, it started becoming choppy when moving from side to side.

What I want to achieve: Smooth camera lerping without stutter, particularly in implementing camera delay or bobble.

RunService:BindToRenderStep("cameraMovement "..self.Settings.weaponName, Enum.RenderPriority.Camera.Value + 1, function(dT)
        -- OTS camera system.
        local startCFrame = CFrame.new(Character.HumanoidRootPart.Position) * CFrame.Angles(0, math.rad(self.Settings.cameraAngles.AngleX), 0) * CFrame.Angles(math.rad(self.Settings.cameraAngles.AngleY), 0, 0)
        local cameraCFrame = startCFrame:PointToWorldSpace(self.Settings.cameraOffset)
        local cameraFocus = startCFrame:PointToWorldSpace(Vector3.new(self.Settings.cameraOffset.X, self.Settings.cameraOffset.Y, -10000))
        
        local resultingCameraCFrame : CFrame = CFrame.new(cameraCFrame, cameraFocus)
        
        -- Setup camera movement.
        if self.Settings.cameraAlpha.Value >= 1 then
            CurrentCamera.CFrame = resultingCameraCFrame
            
        elseif self.Settings.cameraAlpha.Value < 1 then
            local distance = (cameraCFrame - startCFrame.Position).Magnitude
            self.Settings.cameraAlpha.Value += dT / (distance / 20)
            CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, self.Settings.cameraAlpha.Value)
        end
        
        local RootPart = Character.HumanoidRootPart
        local lookingCFrame = CurrentCamera.CFrame:PointToWorldSpace(Vector3.new(0, 0, -10000))
        local rootLookDirection = (lookingCFrame - RootPart.CFrame.Position)
        local rootYaw = math.atan2(-rootLookDirection.X, -rootLookDirection.Z)
        RootPart.CFrame = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, rootYaw, 0) -- I was testing some trigonometry here. C:
        
        if not self.Settings.isFirearmEquipped and not self.Settings.hasWeaponEquipped.Value then
            CurrentCamera.CameraType = Enum.CameraType.Custom
            UserInputService.MouseBehavior = Enum.MouseBehavior.Default
            self.Settings.cameraAlpha.Value = 0
            RunService:UnbindFromRenderStep("cameraMovement "..self.Settings.weaponName)
            
        elseif not self.Settings.isFirearmEquipped and self.Settings.hasWeaponEquipped.Value then
            RunService:UnbindFromRenderStep("cameraMovement "..self.Settings.weaponName)
        end
    end)

(I know, my code structure’s lowk meh, but I just need a solution for lerping camera without stutter before moving to fixing that)

1 Like

Can you provide a video of the choppiness? Im not sure what your talking about?

I also read over your code, and I can see a problem.

Here you are incrementing alpha, but what if alpha jumps over 1? It will make it snap and look choppy as the range should be 0-1, clamp it first.

Let me know if you still get the issue, ill try to look into it deeper.

I have to agree with @JAcoboiskaka1121, I think its whatever values you are inputting, probably making you go over 1.

I copied your code, and plugged in some values and the camera moves really sooth for me.

Oh, I forgot to fix that. Thanks for bringing that up.
But here’s the video when I implement this code block:

RunService:BindToRenderStep("cameraMovement "..self.Settings.weaponName, Enum.RenderPriority.Camera.Value + 1, function(dT)
        -- OTS camera system.
        local startCFrame = CFrame.new(Character.HumanoidRootPart.Position) * CFrame.Angles(0, math.rad(self.Settings.cameraAngles.AngleX), 0) * CFrame.Angles(math.rad(self.Settings.cameraAngles.AngleY), 0, 0)
        local cameraCFrame = startCFrame:PointToWorldSpace(self.Settings.cameraOffset)
        local cameraFocus = startCFrame:PointToWorldSpace(Vector3.new(self.Settings.cameraOffset.X, self.Settings.cameraOffset.Y, -10000))
        
        local resultingCameraCFrame : CFrame = CFrame.new(cameraCFrame, cameraFocus)
        
        -- Setup camera movement.
        if self.Settings.cameraAlpha.Value >= 1 then
            -- OTS state.
            CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, 0.1)
            -- As you can see, I tried lerping it to 0.1, but it ended up stuttering the character when I move sideways.            

        elseif self.Settings.cameraAlpha.Value < 1 then
            -- Transition the camera to OTS.
            local distance = (cameraCFrame - startCFrame.Position).Magnitude
            self.Settings.cameraAlpha.Value += math.clamp(dT / (distance / 20), 0, 1)
            CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, self.Settings.cameraAlpha.Value)
        end
        
        local RootPart = Character.HumanoidRootPart
        local lookingCFrame = CurrentCamera.CFrame:PointToWorldSpace(Vector3.new(0, 0, -10000))
        local rootLookDirection = (lookingCFrame - RootPart.CFrame.Position)
        local rootYaw = math.atan2(-rootLookDirection.X, -rootLookDirection.Z)
        RootPart.CFrame = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, rootYaw, 0) -- I was testing some trigonometry here. C:
        
        if not self.Settings.isFirearmEquipped and not self.Settings.hasWeaponEquipped.Value then
            CurrentCamera.CameraType = Enum.CameraType.Custom
            UserInputService.MouseBehavior = Enum.MouseBehavior.Default
            self.Settings.cameraAlpha.Value = 0
            RunService:UnbindFromRenderStep("cameraMovement "..self.Settings.weaponName)
            
        elseif not self.Settings.isFirearmEquipped and self.Settings.hasWeaponEquipped.Value then
            RunService:UnbindFromRenderStep("cameraMovement "..self.Settings.weaponName)
        end
    end)

1 Like

I haven’t implemented CFrame lerping during the OTS state. Though when I lerp it in its OTS state, the character started to stutter on my screen, which is the issue.

Is there a way to lessen the stutter (or remove it completely)?
Maybe focus on this code block now:

-- Setup camera movement.
        if self.Settings.cameraAlpha.Value >= 1 then
            -- OTS state.
            CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, 0.1)
            -- As you can see, I tried lerping it to 0.1, but it ended up stuttering the character when I move sideways.            

        elseif self.Settings.cameraAlpha.Value < 1 then
            -- Transition the camera to OTS.
            local distance = (cameraCFrame - startCFrame.Position).Magnitude
            self.Settings.cameraAlpha.Value += math.clamp(dT / (distance / 20), 0, 1)
            CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, self.Settings.cameraAlpha.Value)
        end

The camera seems smooth to me, are you sure theres an issue?

You update the characters rotation each frame. The code below lerps it if the angle is above 1.

Try implementing something like this maybe?:

local rootYaw = math.atan2(-rootLookDirection.X, -rootLookDirection.Z)
local lastY = nil

if lastY == nil or (rootYaw - lastYaw) > math.rad(1) then
    RootPart.CFrame = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, rootYaw, 0)
    lastYaw = rootYaw
end

You might need to tweak some values.

Try to also implement more consistent increments for the lerp, and use a set speed, maybe, thats the issue?:

local speed = 3
self.Settings.cameraAlpha.Value = math.min(self.Settings.cameraAlpha.Value + dT * speed, 1)

I would also add a failsafe, just to be sure it doesnt go over 1 and otherwise snaps if it its 1.

if self.Settings.cameraAlpha.Value >= 1 then
    CurrentCamera.CFrame = resultingCameraCFrame 
end

If these dont not work, im out of ideas honestly.

If you’re talking about the choppiness on the character when moving, you can try to set the humanoid’s AutoRotate property to false when using the custom lock on. Otherwise, try using an AlignOrientation instance instead of changing the rootpart‘s cframe

1 Like

Anyways, I found a solution to make it smoother.
I utilized an Exponential equation for the alpha; based on more in-depth research, this is the closest I can get to achieving a smooth camera lerp without using a Spring module.

I don’t know, maybe it’s smoother for you guys now if you try this one out:

function createLerp:Exponential(deltaTime, speed)
    return 1 - 0.01 ^ (deltaTime * speed)
    -- Recommended speed: 1-5, exceeding this limit caused it to jitter for me.
end
2 Likes

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