How can I make this Camera Script perform better without lag/fps drops?

Introduction

Hello everyone! My name is HeIIoISteaIUsernames. I’ve been working on my game for almost a week now, and one of its main functions is the Camera/Room system (you’ll see why it’s called that below.)

About the Camera/Room System (I need a better name tbh)

Essentially what it does is that if a Player is inside a room (via the floor, which is a part), it will activate camera to focus on the player through these two parts:

2
The camera perspective is top-down, as you can see here:

If you need another visual of how the system works, here ya go:

“How did you do this?” you ask? With the power of Raycasting!!

Here’s what the script (local script) does:

  • Creates a function called checkForRoom() and sets up these 5 variables:
 local isInRoom, roomName, cam1, cam2, floorType
  • Creates a ray with an ignore list inside the HumanoidRootPart, pointing downwards
  • Checks if the ray has hit a specific part and if the part’s name is “Floor” or “Floor2”. If the part exists then we set isInRoom to true
  • Checks if the cameraParts (cameraMain and cameraStatic) exist if isInRoom == true. If both those parts exist, and if the cameraStatic’s Active value is set to true or false, then we set cam1 to cameraMain, and cam2 to cameraStatic.
  • We then return those five variables from earlier through a table for further use
  • Later on in the script, we set up a RenderStepped event, accessed the returned table from the checkForRoom() function, and did some magical awesome stuff! YAY!!
Source Code
--room system using raycasting

local function checkForRoom()
	local ray = Ray.new(Character:WaitForChild('HumanoidRootPart').Position, Vector3.new(0, -2, 0)* 5)
	local room, roomStuff
	local isInRoom, roomName, cam1, cam2, floorType
	local hit, pos = workspace:FindPartOnRayWithIgnoreList(ray, {Character, game.Workspace:WaitForChild("World").Props, game.Workspace:WaitForChild("World").Interactables})
	
	if hit then
		--checking for ground
		
		if hit.Name == "Floor" then --bottomFloor for advanced camera manipulation
			floorType = hit.Name
			room = hit
			if room:WaitForChild("RoomNecessities") then
				roomStuff = room:WaitForChild("RoomNecessities")
				if roomStuff.IsARoom.Value == true then
					--we found a floor!
					isInRoom = true
					roomName = roomStuff.RoomName.Value
				else
					isInRoom = false
					roomName = nil
				end
			end
			
			-- checking for camera
			
			if isInRoom == true and roomName ~= nil then
				local b = hit.Parent
				local camera = b:WaitForChild('Camera')
				local camMain, camStatic = camera:WaitForChild('cameraMain'), camera:WaitForChild('cameraStatic')
				
				if camMain then
					--camera1 exists, we got our camera!
					cam1 = camMain
				end
				
				if camStatic then
					if camStatic:WaitForChild('Active').Value == true then
						cam2 = camStatic
					else
						cam2 = camStatic
					end
				end	
			end
		elseif hit.Name == 'Floor2' then --topFloor for advanced camera manipulation
			room = hit
			floorType = hit.Name
			if room:WaitForChild("RoomNecessities") then
				roomStuff = room:WaitForChild("RoomNecessities")
				if roomStuff.IsARoom.Value == true then
					--we found a floor!
					isInRoom = true
					roomName = roomStuff.RoomName.Value
				else
					isInRoom = false
					roomName = nil
				end
			end
			
			-- checking for camera
			
			if isInRoom == true and roomName ~= nil then
				local b = hit.Parent
				local camera = b:WaitForChild('Camera')
				local camMain, camStatic = camera:WaitForChild('cameraMain'), camera:WaitForChild('cameraStatic')
				
				if camMain then
					--camera1 exists
					cam1 = camMain
				end
				
				if camStatic then
					if camStatic:WaitForChild('Active').Value == true then
						cam2 = camStatic
					else
						cam2 = camStatic
					end
				end	
			end		
		else
			isInRoom = false
			roomName = nil
			cam1 = nil
			cam2 = nil
			floorType = nil
		end
	end
	if isInRoom and cam1 then
		
		if cam2 ~= nil then
			if cam2:WaitForChild('Active').Value == true then
				return {
					RoomStatusType = 'Enter';
					IsInRoom = isInRoom;
					RoomName = roomName;
					CameraMain = cam1;
					CameraStatic = cam2;
					FloorType = floorType
				}
			else
				return {
					RoomStatusType = 'Enter';
					IsInRoom = isInRoom;
					RoomName = roomName;
					CameraMain = cam1;
					CameraStatic = cam2;
					FloorType = floorType
				}
			end
		else
			return {
					RoomStatusType = 'Enter';
					IsInRoom = isInRoom;
					RoomName = roomName;
					CameraMain = cam1;
					CameraStatic = cam2;
					FloorType = floorType
				}	
		end 
	else
		return {
				RoomStatusType = 'Exit';
				IsInRoom = isInRoom;
				RoomName = roomName;
				CameraMain = cam1;
				CameraStatic = cam2;
				FloorType = floorType
			}
	end
end

RunService.RenderStepped:Connect(function()
	Camera.CameraType = Enum.CameraType.Scriptable
	
	--coroutine.wrap(function()
		local b = checkForRoom()
		local type1, isInRoom, roomName, roomCameraM, roomCameraS, floorType = b['RoomStatusType'], b['IsInRoom'], b['RoomName'], b['CameraMain'], b['CameraStatic'], b['FloorType']
		
		if type1 == "Enter" and isInRoom == true then
			if roomCameraM and roomCameraS then
					if floorType == 'Floor2' then
						Camera.FieldOfView  = Settings.CameraFOV.Value
						wait(.2)
						Camera.CFrame = Camera.CFrame:Lerp(roomCameraS.CFrame, .2)
						RunService.RenderStepped:Wait()
					elseif floorType == 'Floor' then
						Camera.FieldOfView  = Settings.CameraFOV.Value
						wait(.2)
						Camera.CFrame = Camera.CFrame:Lerp(roomCameraM.CFrame, .2)
						RunService.RenderStepped:Wait()
					end
				end
			end		
				
		if type1 == "Exit" and isInRoom == false then
			Camera.FieldOfView  = Settings.CameraFOV.Value
			local humanoidRootPart = Character:FindFirstChild('HumanoidRootPart')			
			wait(.2)
			
			Camera.CFrame =	Camera.CFrame:Lerp(CFrame.new(humanoidRootPart.Position + Vector3.new(0, 20, 2)) * CFrame.Angles(math.rad(-90), 0, 0),.2)
			RunService.RenderStepped:Wait()
		end
	--end)()
end)

Now you might be asking, “Well, what’s wrong with this?”

Well, here are the problems (performance-wise):

  • FPS DROPS (The longer you stay in-game, the FPS will decrease)
  • LAG SPIKES (the same situation as the FPS drops problem)
  • INSANELY HIGH PING (I don’t know if I should worry about this or not.)

Proof (the ping when the picture below was uploaded was around 200 ms):

Ending

To me, I feel like the Camera/Room System script is working nicely, but the performance in-game feels laggy and poorly optimized. I’m trying to improve the code so it works wonders at 60FPS, but I’m stuck on what to do. I’ve considered using Region3 on the server to check if a player is inside a certain part, and then send the data through RemoteEvent/RemoteFunction and have the camera focus on the player, but I don’t know if this could be efficient.

Questions:

  • How can I improve the script so that it runs smoothly?
  • Is there a way I can do this efficiently without putting stress on the client?
  • Did I overlook some parts in the code?

Thank you for reading all of this! Must’ve been a nightmare, huh? :heart:

2 Likes

I think you should use TweenService
so for example

local tween = game:GetService("TweenService"):Create(Camera, TweenInfo.new(0.3--[[Time of tween]])), {CFrame = CFrame.new(humanoidRootPart.Position + Vector3.new(0, 20, 2)) * CFrame.Angles(math.rad(-90), 0, 0),.2)})

This will make it really smooth and good

1 Like

Every time I implement TweenService onto my camera script, I can’t even move properly for some reason.

I appreciate your help though.

1 Like

Uh, that doesn’t makes sense, lol! it shouldn’t happen I think

That’s what I thought first too. Could you check my code and see what’s wrong? The output doesn’t display any errors.

if type1 == "Enter" and isInRoom == true then
			if roomCameraM and roomCameraS then
				if floorType == 'Floor2' then
					local tween = TweenService:Create(Camera, TweenInfo.new(.5, Enum.EasingStyle.Quart, Enum.EasingDirection.In, 0, false, 0), {CFrame = roomCameraS.CFrame})
					tween:Play()
				elseif floorType == 'Floor' then
					local tween = TweenService:Create(Camera, TweenInfo.new(.5, Enum.EasingStyle.Quart, Enum.EasingDirection.In, 0, false, 0), {CFrame = roomCameraM.CFrame})
					tween:Play()
				end
			end
		elseif type1 == "Exit" and isInRoom == false then
			local tween = TweenService:Create(Camera, TweenInfo.new(0.3, Enum.EasingStyle.Quart, Enum.EasingDirection.In, 0, false, 0), {CFrame = CFrame.new(hrp.Position + Vector3.new(0, 20, 2)) * CFrame.Angles(math.rad(-90), 0, 0)})
			tween:Play()
		end		
1 Like