Trying To Make A Smooth Camera That Follows Character But It's Choppy

Hey, I’ve been trying to make a smooth camera that follows character on X axis relative to camera CFrame.

Example from Blood Engine game:

As you can see on the video, the camera position is slightly delayed from character on X axis, which I want to achieve.

However, I’ve faced the choppy camera shake when walking on X axis relative to camera:

Laggy camera:

Here is the code:

local Character = game.Players.LocalPlayer.Character
local Camera = workspace.CurrentCamera

-- Creating an anchored flying part
local tweenCamera = Instance.new("Part",module.character) 
tweenCamera.Name = "TweenCamera"
tweenCamera.Anchored = true
tweenCamera.CanCollide = false
tweenCamera.CanTouch = false
tweenCamera.CanQuery = false
tweenCamera.Transparency = 0
tweenCamera.Size = Vector3.new(1,1,1)

-- Making the flying part CameraSubject
Camera.CameraSubject = tweenCamera

local HumanoidRootPart = Character.HumanoidRootPart

RunService.Stepped:Connect(function(_,deltaTime)
	local startPos = tweenCamera.Position

	local finishCF = HumanoidRootPart.CFrame * CFrame.new(2,0,0)
	local finishPos = finishCF.Position
	local magnitude = (startPos - finishPos).Magnitude
	local distance = (2 + magnitude/10)
    --[[ Calculating the distance to add for current frame, so it's faster when far,
         smoother when close to character
    ]]
	
	local deltaCF = Camera.CFrame:ToObjectSpace(HumanoidRootPart.CFrame)
	tweenCamera.CFrame = Camera.CFrame * CFrame.new(tweenCamera.CFrame:ToObjectSpace(finishCF).X * distance,deltaCF.Y,deltaCF.Z)
	--[[ Lerping the flying part X axis so it follows the character smoothly,
		 but keeping it's Y and Z axis same as Character's, so it follows
		 Character with delay only on X axis
    ]]
end)

I’ve tried everything. All RunService events, like Heartbeat, Stepped, RenderStepped, BindToRenderStepped with all priorities, PostSimulation etc.

Removing the deltaTime from math makes the shake more drastic.

The cause of the shake

This shake occurs due to the flying part positioning. And because the CameraSubject is the flying part, everything seems to shake, while only the flying part is actually shaking.

According to my experience, the flying part positioning is so “shaky” because the camera and character render separately every time. And if I ignore the camera CFrame in the math, everything will go smooth:

Smooth result, but it follows on each axis:

Here is the code:

RunService.Stepped:Connect(function(_,deltaTime)
	local startPos = tweenCamera.Position
	
	local finishPos = (HumanoidRootPart.CFrame * CFrame.new(2,0,0)).Position
	local magnitude = (startPos - finishPos).Magnitude
	local distance = (2 + magnitude/10)
	
	tweenCamera.Position = tweenCamera.Position:Lerp(finishPos, math.min(deltaTime * distance,magnitude))
end)

As you can see, the shake occurs because of the desync between Character and Camera.

I need Camera to follow character smoothly only on X axis relative to it’s CFrame without the shake.

Any help is highly appreciated!

5 Likes

Why not use renderstepped instead?

As I said, I used lots of methods, including RenderStepped. RenderStepped causes even more shake.

I made the code in the way you can just make a local script in a template place and paste it there to test it out by yourself and possibly find a solution.

1 Like

Oh my bad didn’t read that part. I think thats where the issue is, is that it causes a symptom of shaking. Is the rotation of the CFrame multiplied with moving the cframe at the same time (e.g CFrame.new() * CFrame.Angles())?

Oh I see what you mean how the camera shakes.

Okay the issue with it shaking is with the tweenpart when it moves along the CFrame, why not just set the CurrentCamera’s CFrame along the hrp instead of changing camera subjects and then modifying the CFrame?

1 Like

I wish it was that simple. Camera CFrame can be changed only if it’s type is Scriptable, else it causes problems with dragging the camera with mouse. If it’s Scriptable, player won’t be able to drag the camera with mouse at all.

It looks like in the game, the camera does not follow the character. It instead offsets the camera against the characters X movement. I modified your code and it runs almost the same as in game

local RunService = game:GetService("RunService")

local Character = game.Players.LocalPlayer.Character
local Camera = workspace.CurrentCamera

local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")

local XOffset = 0

function lerp(a, b, c)
	return a + (b-a) * c
end

RunService:BindToRenderStep("MoveCamera", Enum.RenderPriority.Camera.Value, function(deltaTime)
	local flatCamLookVector = (Camera.CFrame.LookVector * Vector3.new(1,0,1)).Unit 	
	-- flat camera vector (no Y axis)
	-- So the X and Z movement is relative to the camera
	-- but the Y is relative to the world
	
	local flatCamCFrame = CFrame.new(Vector3.zero, flatCamLookVector) -- cframe from the flat vector
	local localMovement = flatCamCFrame:VectorToObjectSpace(HumanoidRootPart.Velocity/16) -- turn velocity from world to the flat camera cframe
	local xMovement = localMovement.X -- calculate the X movement based on the camera
	
	XOffset = lerp(XOffset, xMovement, math.pow(0.1, deltaTime*60)) -- interpolate for smoothness
	-- see https://www.construct.net/blogs/2/924 for delta time implementation
	
	Camera.CFrame = Camera.CFrame * CFrame.new(2, 0, 0) + flatCamCFrame.RightVector*-XOffset -- add negative offset to camera CFrame
end)

Edit : delta time correction

3 Likes

Wow! I’ve never seen such math operations. Your code works perfectly!

One thing, can you explain on how to change the speed of the camera follow here. For example, faster when far, slower when close or the velocity is 0.

You can change 0.1 in the math.pow function to your desired speed, like this

local speedMultiplier = math.abs(xMovement) > 8 and 0.2 or 0.1 
-- sets interpolation speed to 0.2 if x velocity is greater than 8 
-- else, set to 0.1 

XOffset = lerp(XOffset, xMovement, math.pow(speedMultiplier, deltaTime*60)) -- interpolate for smoothness

This will make the interpolation go faster when you are walking, but slower when you are standing still.
As for the faster when far thing, I don’t think that is possible with the code since it is calculated with the characters velocity, and not the distance.

Thank you so much for the help!

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