How do I optimize my camera script for better perfomance?

Im making a fps game that uses a viewport frame with a gun and the arms, i made it so only when when the gun is equipped you get into first person. The script in question is the camera movement, as wobble effect and weapon sway, but it is costing too much on fps, what can i do to improve it?

The script:

local uis = game:GetService("UserInputService")
local runs = game:GetService("RunService")
local ts = game:GetService("TweenService")

local plr = game:GetService("Players").LocalPlayer
local plrChar = plr.Character

local viewport = script.Parent.Viewport

local charViewport = viewport:WaitForChild("WorldModel"):WaitForChild("Arms")
local gunPos = charViewport.GunPos

local viewportCamera = script.Parent.Viewport:WaitForChild("Camera")
local TurnX = 0
local TurnY = 0
local turn2 = 0

local function Lerp(a, b, t)
	return a + (b - a) * t
end

local camti = TweenInfo.new(0.1)

--Not so important animations to when the gun is equipped

local previousVelocity = nil

local hrp = plrChar:WaitForChild("HumanoidRootPart")

local camera = workspace.CurrentCamera	
local char = script.Parent
local hrp = plrChar:WaitForChild("HumanoidRootPart")
local rotFrequency, rotIntensity = 8, .5 -- camera rotation on z axis (frequency is how fast, intensity is how strong)
local verFrequency, verIntensity = 16, .125 -- camera movement on y axis (frequency is how fast, intensity is how strong)
local defWalkSpeed = 16 -- default walk speed (so that whenever the walk speed increases, the walk bob gets faster)

local function GetCurve(frequency, intensity)
	return (math.sin(os.clock() * frequency) * intensity)
end


runs.Heartbeat:Connect(function(deltaTime)
	
	if viewport.Visible == false then return end

    --Viewport Camera
	
	charViewport.GunPos.CFrame = charViewport["Right Arm"].RightGripAttachment.WorldCFrame
	
	local MouseDelta = uis:GetMouseDelta()

	if MouseDelta.Magnitude ~= 0 then
		
		local factorX = 1
		if MouseDelta.X > 0 then
			factorX = MouseDelta.X / 10
		elseif MouseDelta.X < 0 then
			factorX = -MouseDelta.X / 10
		end
		
		local factorY = 1
		if MouseDelta.Y > 0 then
			factorY = MouseDelta.Y / 10
		elseif MouseDelta.Y < 0 then
			factorY = -MouseDelta.Y / 10
		end
		
		TurnY = Lerp(TurnY, math.clamp(MouseDelta.Y, -16, 16) * factorY, (10 * deltaTime))
		TurnX = Lerp(TurnX, math.clamp(MouseDelta.X, -16, 16) * factorX, (10 * deltaTime))

	else

		TurnX = Lerp(TurnX, 0, (10 * deltaTime))
		TurnY = Lerp(TurnY, 0, (10 * deltaTime))

	end
	
	local velocityY = hrp.AssemblyLinearVelocity.Y / defWalkSpeed / 10
	ts:Create(viewportCamera, camti, {CFrame = CFrame.new(Vector3.new(-math.rad(TurnX) , 1.6 + velocityY - math.rad(TurnY), -math.rad(TurnX))) * CFrame.Angles(-math.rad(TurnY), -math.rad(TurnX), 0)}):Play()

	
	
    --Player Camera
	
	
	local velocity = math.round(Vector3.new(hrp.AssemblyLinearVelocity.X, 0, hrp.AssemblyLinearVelocity.Z).Magnitude)
	if velocity == 0 then return end

	defWalkSpeed = plrChar.Humanoid.WalkSpeed
	turn2 = TurnX / 4
	
	if previousVelocity and previousVelocity ~= velocity then
		velocity = Lerp(previousVelocity, velocity, .25)
	end
	previousVelocity = velocity
	
	camera.CFrame *= CFrame.new(0, GetCurve(verFrequency, verIntensity) * velocity / defWalkSpeed, 0) * CFrame.Angles(0, 0, (math.rad(GetCurve(rotFrequency, rotIntensity) * velocity / defWalkSpeed) + math.rad(turn2) ))
	
end)
1 Like

How are you determining that this is taking too much FPS?
Have you looked into your micro profiler?
I would highly suggest avoiding tween service in renderstepped work.
You are creating tween objects every frame using the ts:Create method, the luau garbage collector isn’t gonna have fun time cleaning up 60 tween objects every frame.
I would highly recommend you look into Cframe:lerp() for render stepped work, and use tween service for code that runs once, like a button being pressed.

I learn best when there is an example, so here is some code used in typical colors 2, it might give you some ideas.

local function work(dt)
	
	dt = math.clamp(dt,0.0005,0.25) -- absurd framerates begone. 
	if not armsModel then return end
	local rootpart = armsModel:FindFirstChild("HumanoidRootPart")
	if not rootpart then return end
	local currentRootPartCFrame = rootpart.CFrame
	local currentCameraCFrame = camera.CFrame
	if chargetick.Value ~= 0 or ads.Value then
		return
	end

	if lastSpringUpdate + in30hz < os.clock() then --- forces the springs to not update target faster than 30hz

		local movementVector = (currentCameraCFrame.Position - prevCameraCFrame.Position) / (dt * 60)
		if movementVector ~= movementVector then movementVector = Vector3.new() end --- NAN fix, bc NAN ~= NAN
		local lateralMovement = movementVector:Dot(currentCameraCFrame.RightVector)
		local lateralMovementClamped = math.clamp(lateralMovement,-tiltClamp,tiltClamp)
		local tiltGoal = (-lateralMovementClamped * tiltMultiplier )* UserTiltMult

		local heightDiff = (currentCameraCFrame.Position.Y - prevCameraCFrame.Position.Y) / (dt * 60)
		if heightDiff ~= heightDiff then heightDiff = 0 end --- NAN ~= NAN
		local heightDiffClamped = math.clamp(heightDiff,-heightClamp,heightClamp)
		local heightGoal = (-heightDiffClamped * heightMultiplier) * UserHeightMult

		newSpringHeight.Target = heightGoal
		newSpringTilt.Target = tiltGoal
		prevCameraCFrame = currentCameraCFrame
		lastSpringUpdate = os.clock()
		
	end

	--- tilt left right
	local tilt = newSpringTilt.Position
	local tiltClamped = math.clamp(tilt,-2,2) -- we will usually never reach these but just in case.

	--- bounce up down
	local heightOffset = newSpringHeight.Position
	local heightOffsetClamped = math.clamp(heightOffset,-2,2) 

	local finalValue = currentRootPartCFrame * CFrame.new(0, heightOffset, 0) * CFrame.Angles(0, 0, tiltClamped * tiltInfluence)

	local lMult = (UserSwayMult*swayMultiplier) / (dt * 60) --- framerate correction.
	local rotation = currentCameraCFrame:toObjectSpace(lastCameraCF) --get cframe delta
	local x,y,z = rotation:ToOrientation() --I'm sure there are better ways to get rotation but this will work for now.
	swayOffset = swayOffset:Lerp(CFrame.Angles(math.sin(x)*swayMultiplier,math.sin(y)*lMult,0), 0.1) --calculate the sway using SIN
	armsModel:PivotTo(finalValue * swayOffset)--apply the sway
	lastCameraCF = currentCameraCFrame --update the last cframe
	
end

I hope this helps!

1 Like

Measured with some bad old notebook i have laying there and some testing, by using lerp now it has basically 0 impact, ty a lot!

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