Howdy!
It’s been a minute, hope you’ve had all the best in these 9 months
To everyone still looking for a solution, you’re in luck
Working with ROBLOX’s default camera is tricky - but I’ve managed to find a solution without having to script yourself a whole new module from scratch.
The Problem
ROBLOX’s default camera updates every passing frame. Essentially, every CFrame you’d want the camera to smooth or change into will automatically be overwritten in the next frame.
Even with the case of CFrame:Lerp in RenderStepped:
Camera.CFrame = Camera.CFrame:Lerp(Camera.CFrame * CFrame.Angles(0, 0, math.rad(goal)), deltaTime)
The final result will only be snappy and watered-down by DeltaTime or whatever update speed you’ve chosen.
The Basics
We know how to adjust the angle using the method below
RunService.RenderStepped:Connect(function(deltaTime)
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(goal))
end)
If we consider the relative velocity of a character, we can adjust the camera’s angle per the character’s direction
RunService.RenderStepped:Connect(function(deltaTime)
local relativeVelocity = RootPart.CFrame:VectorToObjectSpace(RootPart.Velocity)
if relativeVelocity.X > 1 then -- Going right
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(15))
elseif relativeVelocity.X < -1 then -- Going left
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(-15))
else -- Neither
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(0))
end
end)
This works just fine if you’re happy with the result, but it remains snappy.
The Trick
Let’s start over.
Instead of adjusting the Camera.CFrame per direction to change it’s angle, we want to change the rotation axis of the camera exclusively - this way, it can be smoothed.
In order to do that, we need to add two new variables to our script.
local axis = 0
local angle = 0
To smooth the angle, you’ll need a smoothing method - you can use whichever one you like
It’s recommended to use Tweening through TweenService - it’s optimized for this very reason
I prefer this neat Lerp function - It’s simple and it’s done me wonders.
function Lerp(a, b, t)
return a + (b - a) * t
end
a - initial value
b - final value
t - time (the UpdateSpeed / DeltaTime in our case)
Onto RenderStepped: the Camera.CFrame will have to be updated every frame - regardless of direction - with consideration of the axis we want to adjust
Here, the angle alone can be adjusted per direction.
RunService.RenderStepped:Connect(function(deltaTime)
local relativeVelocity = RootPart.CFrame:vectorToObjectSpace(RootPart.Velocity)
if relativeVelocity.X > 1 then
angle = 15
elseif relativeVelocity.X < -1 then
angle = -15
else
angle = 0
end
axis = angle -- this is the roll
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(axis)) -- This does the job
end)
But it isn’t smooth just yet!
All you need to do is replace
axis = angle
with
axis = Lerp(axis, angle, deltaTime)
This will smooth the axis from whichever previous value it had to the new angle every frame.
You can then adjust how quick or slow the smoothing is by multiplying deltaTime with a value.
TL;DR - Final Script
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")
local RootPart = Humanoid.RootPart
local Camera = workspace.CurrentCamera
function Lerp(a, b, t)
return a + (b - a) * t
end
local roll = 0
local goal = 0
RunService.RenderStepped:Connect(function(deltaTime)
local relativeVelocity = RootPart.CFrame:vectorToObjectSpace(RootPart.Velocity)
if relativeVelocity.X > 1 then
angle = 15
elseif relativeVelocity.X < -1 then
angle = -15
else
angle = 0
end
axis = Lerp(axis, angle, 6 * deltaTime) -- this is the roll
Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(axis)) -- This does the job
end)