Making my camera *smooth like butter*

So, I already made my code where I’m just simply tweening a part to where the player is going and then putting the camera on it to make a smooth like butter top-down camera.

Thing is, it’s my first time coding by myself and I have no clue why my camera stutters like this:

Anyways here’s my code, sorry if it’s a bit messy, but please halp!

-- Services
local rS = game:GetService("RunService")
local tS = game:GetService("TweenService")

-- Variables
local camera = game.Workspace.CurrentCamera
local root = script.Parent:WaitForChild("HumanoidRootPart")
local cameraPart = Instance.new("Part")
local newPos

-- Script
cameraPart.Anchored = true
cameraPart.Parent = game.Workspace
cameraPart.CFrame = CFrame.lookAt(root.CFrame.Position + Vector3.new(0, 25, 30), root.Position)
camera.CameraType = Enum.CameraType.Scriptable

local tweenInfo = TweenInfo.new(
	.1, 
	Enum.EasingStyle.Sine, 
	Enum.EasingDirection.InOut
)

rS.RenderStepped:Connect(function()
	camera.CFrame = cameraPart.CFrame
	newPos = root.Position + Vector3.new(0, 25, 30)
	local tween = tS:Create(cameraPart, tweenInfo, {Position = newPos})
	tween:Play()
end)

(And no it’s not my recording software doing this, nor my PC, I tested with more FPS and the issue seems gone.)

6 Likes

use Heartbeat and tell me if its still choppy :))

Did you test it in the Studio or by actually starting the game? If you test in the studio, not that it limits your FPS and performance. So while it helps you to optimise your game more efficiently, it will definitely lower the game smoothness.

Second, the RenderStepped event runs every second 30 or 60 times, depending on your framerate. It means that every frame lasts for about 0.017 seconds, and you make the tween last for 0.1 seconds. If you’re trying to make the camera movement look smoother, I recommend you use linear interpolation (LERP) instead of tweeting.

rS.RenderStepped:Connect(function()
	cameraPart.Position = cameraPart.Position:lerp(root.Position, 0.8) + Vector3.new(0, 25, 30) -- You can adjust the speed and "smoothness" by increasing or decreasing the "0.8", just make sure its a value between 0 and 1
	camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly
end)

Make sure to read my notes too!
Hope this helps

3 Likes

I don’t think that is the effect he is going for

Heartbeat still depends on the framerate of the client and using it will make the camera respond slower. For camera manipulation, always use RenderStepped. Use Heartbeat for scripts that could be harder to run or might slow the game.

Yeah that’s what I thought too, it removes the white effect thing, but not the stutters.
But when I’m using the lerp it actually stops stuttering but I have another issue, I’m looking on how to fix it.

Your script does work, problem is: when changing the alpha of the lerp the part just decides to go further to the back (when decreasing).

.8 value:

.4 value:

Thats how Interpolation works, it will transition towards the Goal.

Found the issue!
The “Vector3.new” needed to go right after the “…Lerp(root.Position…”

EDIT: Nope, my camera isn’t still as smooth as I would like, but I don’t know if it’s actually possible.
Even tho games like AUT already made that sort of camera, so why wouldn’t it be possible for me?

1 Like

If that fixed it and that’s the effect you were hoping to achieve, mark your answer as the solution :3

Can you please record it and show it? Lerp should fix it. Remember to add the code to your answer

Sorry! I was writing the edit, while figuring if I could fix it before asking help again lol…

Yep, here is everything:

I changed the code to make sure it does work, but the stuttering effect is still present.

Code (tested both inside RBX studio and the game itself):

-- Services
local rS = game:GetService("RunService")
local tS = game:GetService("TweenService")

-- Variables
local camera = game.Workspace.CurrentCamera
local root = script.Parent:WaitForChild("HumanoidRootPart")
local cameraPart = Instance.new("Part")
local newPos

-- Script
cameraPart.Anchored = true
cameraPart.CanTouch = false
cameraPart.CanCollide = false
cameraPart.Parent = game.Workspace
cameraPart.CFrame = CFrame.lookAt(root.CFrame.Position + Vector3.new(0, 22, 30), root.Position)

camera.CameraType = Enum.CameraType.Scriptable

rS.RenderStepped:Connect(function()
	cameraPart.Position = cameraPart.Position:Lerp(root.Position + Vector3.new(0, 22, 30), .15)-- You can adjust the speed and "smoothness" by increasing or decreasing the "0.8", just make sure its a value between 0 and 1
	camera.CFrame = cameraPart.CFrame -- camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly
end)

EDIT: Doesn’t it have something to do with FPS? Cuz, when going to higher FPS than 60, issue is fixed.

Don’t use TweenService, you end up having to create lots of new objects, not only is this choppy but also really bad for performance to do every single frame.

Instead use CFrame:Lerp() in RunService:RenderStepped().

game:GetService("RunService").RenderStepped:Connect(function(delta)
 camera.CFrame = camera.CFrame:Lerp(Goal_CFrame, delta * speed)
end

You can multiply delta to get faster/slower interpolation.
I used to interpolate cameras like this and it’s fairly smooth and works pretty well.

I’ve done this before. I used lerp to somewhat create an “average deltatime”, which then I used that and multiplied it by the number in the CFrame alpha. Here is the fixed code:

-- Services
local rS = game:GetService("RunService")
local tS = game:GetService("TweenService")

-- Variables
local camera = game.Workspace.CurrentCamera
local root = script.Parent:WaitForChild("HumanoidRootPart")
--local cameraPart = Instance.new("Part")
local newPos

-- Script
--[[cameraPart.Anchored = true
cameraPart.CanTouch = false
cameraPart.CanCollide = false]]
local offset = CFrame.new(0,10,30)
--cameraPart.Parent = game.Workspace
--cameraPart.CFrame = CFrame.lookAt(root.CFrame.Position + Vector3.new(0, 22, 30), root.Position)

camera.CameraType = Enum.CameraType.Scriptable
local avgdt = 1/60 --do not change
local adjustMultiplier = .001 --if you get stuttering, change the value
local smoothness = 10
function lerp(a, b, alpha)
	return a + ((b - a) * alpha)
end
rS.RenderStepped:Connect(function(dt)
	avgdt = lerp(avgdt, dt, adjustMultiplier)
	local alpha = avgdt * 60 / smoothness
	--cameraPart.Position = cameraPart.Position:Lerp(root.Position + Vector3.new(0, 22, 30), alpha)
	camera.CFrame = camera.CFrame:Lerp(offset+root.Position, alpha)
	--cameraPart.CFrame -- camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly
end)

Unfortunately, this would seem to always have stuttering in your case. The other best solution would be to use a part with AlignPosition to smoothly move to the root’s position. Here is the AlignPosition version:

-- Services
local rS = game:FindService("RunService")
--local tS = game:GetService("TweenService")

-- Variables
local camera = game.Workspace.CurrentCamera
local root = script.Parent:WaitForChild("HumanoidRootPart")
local cameraPart = Instance.new("Part")
local newPos

-- Script
local AP = Instance.new("AlignPosition")
local AT = Instance.new("Attachment")
local RootAT = Instance.new("Attachment")
RootAT.Parent = root
AT.Parent = cameraPart
AP.Attachment0 = AT
AP.Attachment1 = RootAT
AP.Responsiveness = 5 --smoothness from 5-200
--AP.MaxForce = cameraPart.Mass * workspace.Gravity * 2
--Unfortunately, MaxVelocity appears to be broken so it will not be used.
AP.Parent = cameraPart
cameraPart.Transparency = 1
cameraPart.CanTouch = false
cameraPart.CanCollide = false
cameraPart.CanQuery = false
--local offset = CFrame.new(0,10,30)
local offset = Vector3.new(0,22,30)
local angle = CFrame.Angles(-math.pi/4,0,0)
cameraPart.Parent = game.Workspace
--cameraPart.CFrame = CFrame.lookAt(root.CFrame.Position + Vector3.new(0, 22, 30), root.Position)

camera.CameraType = Enum.CameraType.Scriptable
--[[local avgdt = 1/60 --do not change
local adjustMultiplier = .001 --if you get stuttering, change the value
local smoothness = 10
function lerp(a, b, alpha)
	return a + ((b - a) * alpha)
end]]
rS.RenderStepped:Connect(function(dt)
	--[[avgdt = lerp(avgdt, dt, adjustMultiplier)
	local alpha = avgdt * 60 / smoothness
	--cameraPart.Position = cameraPart.Position:Lerp(root.Position + Vector3.new(0, 22, 30), alpha)
	camera.CFrame = camera.CFrame:Lerp(offset+root.Position, alpha)
	--cameraPart.CFrame -- camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly]]
	camera.CFrame = CFrame.new(cameraPart.Position + offset) * angle
end)

This eliminates the stuttering, but it causes other issues.

Replace the last part with this, it should work perfectly.


rS.RenderStepped:Connect(function(dt)
	cameraPart.Position = cameraPart.Position:Lerp(root.Position + Vector3.new(0, 22, 30), dt * 5)-- You can adjust the speed and "smoothness" by increasing or decreasing the "0.8", just make sure its a value between 0 and 1
	camera.CFrame = cameraPart.CFrame -- camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly
end)

Here you go I managed to fix it, you had to use a Heartbeat event, it’s the one you use for when you mess with physical properties such as in your case Position. I also made it use DeltaTime so it has the same effect for any FPS.
EDIT: You can mess with the speed changing the number multiplied by deltatime, the less the slower, the more the faster.

-- Services
local rS = game:GetService("RunService")
local tS = game:GetService("TweenService")

-- Variables
local camera = game.Workspace.CurrentCamera
local root = script.Parent:WaitForChild("HumanoidRootPart")
local cameraPart = Instance.new("Part")
local newPos

-- Script
cameraPart.Anchored = true
cameraPart.CanTouch = false
cameraPart.CanCollide = false
cameraPart.Parent = game.Workspace
cameraPart.CFrame = CFrame.lookAt(root.CFrame.Position + Vector3.new(0, 22, 30), root.Position)

camera.CameraType = Enum.CameraType.Scriptable

rS.PostSimulation:Connect(function(dt)
	cameraPart.Position = cameraPart.Position:Lerp(root.Position + Vector3.new(0, 22, 30), dt * 5)-- You can adjust the speed and "smoothness" by increasing or decreasing the "0.8", just make sure its a value between 0 and 1
	camera.CFrame = cameraPart.CFrame -- camera.CFrame = cameraPart.CFrame -- Set the camera's CFrame to the part's Cframe AFTER you have moved the part, so the camera gets moved correctly
end)
9 Likes

Wow! Now it’s totally smoothen out, thank you so much for taking your time!

2 Likes

That is quite interesting since I’ve tried the heartbeat event and it did not seem to work. Fortunately, it does work if I removed the avgdt function in my code, which is odd because it usually would make it smoother.

1 Like

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