Hello there,
I am attempting to create a realistic camera shake effect using RunService and Linear Interpolation. It works great, however, if you have a frame rate lower than 60, the camera movement jitters a lot and is very unstable.
If you would like to take a test my code, make a LocalScript in StarterCharacterScripts and paste in this:
--// Variables \\--
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Player.Character:WaitForChild("Humanoid")
local Mouse = Player:GetMouse()
local Camera = game:GetService("Workspace").CurrentCamera
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local BobbingConnection = nil
local func1, func2, func3, func4, val, val2 = 0, 0, 0, 0, 0, 0
local int, int2 = 10, 10
local vect3 = Vector3.new()
--// Functions \\--
local function lerp(a, b, c)
return a + (b - a) * c
end
--// View Bobbing and Camera Shake \\--
BobbingConnection = RunService.RenderStepped:Connect(function(deltaTime)
deltaTime = deltaTime * 60 --// Multiply deltaTime by 60 to make it consistent with RenderStepped's unit.
--// If the humanoid is dead, stop the camera bobbing and shaking.
if Humanoid.Health <= 0 then
BobbingConnection:Disconnect()
return
end
--// Calculate the root magnitude (player movement speed) and cap it to a maximum value.
local rootMagnitude = Humanoid.RootPart and Vector3.new(Humanoid.RootPart.Velocity.X, 0, Humanoid.RootPart.Velocity.Z).Magnitude or 0
local calcRootMagnitude = math.min(rootMagnitude, 50)
--// Calculate camera bobbing and shaking parameters based on deltaTime and rootMagnitude.
if deltaTime > 3 then
func1, func2 = 0, 0
else
func1 = lerp(func1, math.cos(tick() * 0.5 * math.random(10, 15)) * (math.random(5, 20) / 200) * deltaTime, 0.05 * deltaTime)
func2 = lerp(func2, math.cos(tick() * 0.5 * math.random(5, 10)) * (math.random(2, 10) / 200) * deltaTime, 0.05 * deltaTime)
end
--// Apply camera movement and shake based on the calculated parameters.
Camera.CFrame = Camera.CFrame *
(CFrame.fromEulerAnglesXYZ(0, 0, math.rad(func3)) *
CFrame.fromEulerAnglesXYZ(math.rad(func4 * deltaTime), math.rad(val * deltaTime), val2) *
CFrame.Angles(0, 0, math.rad(func4 * deltaTime * (calcRootMagnitude / 5))) *
CFrame.fromEulerAnglesXYZ(math.rad(func1), math.rad(func2), math.rad(func2 * 10)))
--// Calculate and apply camera rotation based on player movement direction.
val2 = math.clamp(lerp(val2, -Camera.CFrame:VectorToObjectSpace((Humanoid.RootPart and Humanoid.RootPart.Velocity or Vector3.new()) / math.max(Humanoid.WalkSpeed, 0.01)).X * 0.08, 0.1 * deltaTime), -0.35, 0.2)
--// Update camera rotation speed and intensity based on player movement speed.
func3 = lerp(func3, math.clamp(UserInputService:GetMouseDelta().X, -5, 5), 0.25 * deltaTime)
func4 = lerp(func4, math.sin(tick() * int) / 5 * math.min(1, int2 / 10), 0.25 * deltaTime)
if rootMagnitude > 1 then
val = lerp(val, math.cos(tick() * 0.5 * math.floor(int)) * (int / 200), 0.25 * deltaTime)
else
val = lerp(val, 0, 0.05 * deltaTime)
end
--// Adjust camera shake intensity based on player movement speed.
if rootMagnitude > 20 then
int = 20
int2 = 18
elseif rootMagnitude > 0.1 then
int = 12
int2 = 14
else
int2 = 0
end
--// Store the camera's LookVector for future calculations.
vect3 = lerp(vect3, Camera.CFrame.LookVector, 0.125 * deltaTime)
end)
I’ve added in some comments to make it easier to understand.
Any help is appreciated. Have a good day.
Thanks,
—Nebulimity