Is this an efficient way to manipulate a players camera?


The coding below is a Local Script that contains code to either animate a camera rig, and connect a players camera to said rig… or tween the players camera between two positions from a list, in the order provided.

I feel that this coding is overly complex when it doesn’t need to be, and there’s a much more efficient way to achieve the goal of the system.

I’ve thought of using task, or coroutines to do the same thing… but am unsure of the best way to do said idea correctly.

I want to improve the code any way possible, even if it’s something as simple as cleaning the coding up.

local Connections = {CameraTrack = {}}
["Change Camera Subject"] = {
	Description = "",
	Function = function(CameraType,CameraProperties,Info,Frames)
		local Camera = game.Workspace.CurrentCamera

		--[[This is to reset the camera whenever the event is fired.]]
		if Connections.CameraTrack[1] and typeof(Connections.CameraTrack[1]) == "RBXScriptConnection" then

			--[[This is to stop any, and all animations that could be player on the CameraRig]]
			if game.Workspace:FindFirstChild("Camera_Rig") then
				for _,v in pairs(game.Workspace["Camera_Rig"].Humanoid:GetPlayingAnimationTracks())do v:Stop(0) end
			end; 

			--[[This disconnects the connection]]
			Connections.CameraTrack[1]:Disconnect();Connections.CameraTrack[1] = nil
		end

		if Connections.CameraTrack[1] and typeof(Connections.CameraTrack[1]) == "Instance" then 

			--[[This is meant to cancel any camera tweens in motion, and nulify the function running]]
			Connections.CameraTrack[1]:Cancel();Connections.CameraTrack[1] = nil; Connections.CameraTrack[2] = true
		end

			--[[Coding starts here,
			Hard Camera = A camera that goes off of the positions of multiple parts already placed and hidden away in Server Storage.
			Animate Camera = A Camera Rig is set up with a humanoid, and is used to animate a camera however you see fit.
			]]

		if string.lower(CameraType) == "hard camera" then

			if not Frames then return end
			for NumPosition,Frame in pairs(Frames) do
				local bool = true
				if Frames[(NumPosition + 1)] ~= nil then
					--[Variables]
					if Connections.CameraTrack[2] == true then
						break
					end

					--[[This sets the cameras starting position.]]
					Camera.CFrame = CFrame.new(Frame.Position) * CFrame.Angles(math.rad(Frame.Orientation.X), math.rad(Frame.Orientation.Y), math.rad(Frame.Orientation.Z));
					Camera.CameraType = CameraProperties["Camera Type"];
					Camera.FieldOfView = CameraProperties["FoV"];

					--[[This is so I can make sure the CFrame is valid, this gets the next frames CFrame.]]
					local newCFrame;
					newCFrame = {CFrame = Frames[NumPosition + 1].CFrame}
					--[[This runs the camera]]
					if newCFrame == nil then continue end
					warn("CREATING NEW TWEEN".." to "..tostring(Frames[(NumPosition + 1)]["Angle Name"]))
					Connections.CameraTrack[1] = game:GetService("TweenService"):Create(Camera,TweenInfo.new(Frame.Camera_Speed,table.unpack(Info)),newCFrame)
					Connections.CameraTrack[1]:Play()
					Connections.CameraTrack[1].Completed:Wait()
					bool = false;Connections.CameraTrack[1] = nil
				end
			end

			--[[If there is nothing after the current frame, it shall fix the camera to be normal.]]
			Connections.CameraTrack[1] = nil;Connections.CameraTrack[2] = false
			Camera.CameraType = Enum.CameraType.Custom;Camera.FieldOfView = 70

		elseif string.lower(CameraType) == "animate camera" then

			--[[This is a bunch of instances that either create, or need to be created, alongside variables.]]
			local CameraRig = Info["Camera_Rig"];local CameraAnimation,PlayerAnimation = Instance.new("Animation");
			local CameraHum = CameraRig.Humanoid or CameraRig:WaitForChild("Humanoid")

			Camera.CameraType = CameraProperties["Camera Type"]; --[[This sets the current Cameras Type]]
			CameraAnimation.AnimationId = Info["Camera_Animation"]; --[[This is the animationID sent from the server.]]
			local CamTrack = CameraHum:LoadAnimation(CameraAnimation); --[[This loads the animation.]]
			CamTrack:Play() --[[This plays the animation.]]

			--[[This changes the Cameras Position when the CFrame of the CameraRig's primary part moves, I think..?]]
			Connections.CameraTrack[1] = game:GetService("RunService").RenderStepped:Connect(function()
				if CamTrack.TimePosition > .1 then --[[I'm unsure how to prevent a small pause in-between when the Camera moves to the correct position.]]
					Camera.CFrame = CameraRig.PrimaryPart.CFrame
				end
			end)

			--[[This is to set off particle emmitters when reached.]]
			CamTrack:GetMarkerReachedSignal("Pyro"):Connect(function(paramString)

			end)

			--[[This is to do an action/animation when the Marker "Queue Player" is reached.]]
			CamTrack:GetMarkerReachedSignal("Queue Player"):Connect(function(paramString)

				--[[This checks if there's a player to animate inside the Info provided from the server]]
				if not Info["Animate_Player"] then return "No Animated Player(s)" end

				--[[This is to animate the player when the Marker is reached.]]
				local Char = game.Workspace[Info["Animate_Player"][2].Name] or game.Workspace:WaitForChild(Info["Animate_Player"][2].Name,30)
				local PlayerHum = Char.Humanoid or Char:WaitForChild("Humanoid",30)


				if Info["Animate_Player"] and Info["Animate_Player"][1] then 
					PlayerAnimation = Instance.new("Animation");PlayerAnimation.AnimationId = Info["Player_Animation"];
					local PlayerTrack = PlayerHum:LoadAnimation(PlayerAnimation);PlayerTrack:Play()
				end
			end)

			--[[This stops any, and all connection after the animation has completed.]]
			CamTrack.Stopped:Connect(function()
				Connections.CameraTrack[1]:Disconnect();Camera.CameraType = Enum.CameraType.Custom;Camera.FieldOfView = 70;Camera.CameraSubject = game.Players.LocalPlayer.Character.Humanoid
			end)

		end
		return "Completed"
	end,
}

Any recommendations is appreciated

2 Likes

For starters, instead of accessing Connections.CameraTrack[1] multiple times, you should store it in a variable.

Also:

local bool = true

-- ...

Connections.CameraTrack[1].Completed:Connect(function()
	bool = false;Connections.CameraTrack[1] = nil
end)
repeat
	task.wait(.05)
until not bool

Should be:

Connections.CameraTrack[1].Completed:Wait()
Connections.CameraTrack[1] = nil

My rule is that if you are repeating wait until something, there is almost always, if not just plain always, a better/cleaner way to do it.

Also correct me if I am wrong but does CFrame.new(Frames[(NumPosition + 1)].Position) * CFrame.Angles(math.rad(Frames[(NumPosition + 1)].Orientation.X), math.rad(Frames[(NumPosition + 1)].Orientation.Y), math.rad(Frames[(NumPosition + 1)].Orientation.Z)) not just boil down to Frames[NumPosition + 1].CFrame?

1 Like

I never thought of using the Wait() function to clean up the code, and I entirely forgot I sent the CFrame to the function. Thank you for those!!

Could you elaborate on what you mean by a variable? I’m assuming you mean Connections.CameraTrack["STARTING_FRAME"] or local Starter_Connection = Connections.CameraTrack[1]

I’ve updated my post above to fit these improvements.

1 Like

By variable I mean instead of repeating Connections.CameraTrack[1] multiple times, which involves indexing tables twice, a more optimized way would be to store local CameraTrack1 = Connections.CameraTrack[1] and then just use CameraTrack1.

1 Like

Gotcha, I shall update accordingly shortly. Anything else you’d recommend?

if game.Workspace:FindFirstChild("Camera_Rig") then
	for _,v in pairs(game.Workspace["Camera_Rig"].Humanoid:GetPlayingAnimationTracks()) do v:Stop(0) end
end

Should be:

local CameraRig = workspace:FindFirstChild("Camera_Rig")
if CameraRig then
	for _,v in pairs(CameraRig.Humanoid:GetPlayingAnimationTracks()) do v:Stop(0) end
end

Because you should access things once and store them in a variable instead of accessing them more times than necessary.

1 Like

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