Trying to make movable Security Camera System

I want to be able to simply move my camera in all 4 directions with the Ui buttons but the camera needs to keeps its flat orientation at all times.

No matter what I do, I can never get the orientation of the camera to stay flat at all times.
I have attached a video.

I have already tried multiple different solutions such as having a loop always set the orientation.

I tried fixing this a couple months ago and got no where so I have just come back to it to see if I could fix it with no success.

local player = game.Players.LocalPlayer
local button = script.Parent.View
local Camera = workspace.CurrentCamera
local NextCam = script.Parent.Next
local PrevCam = script.Parent.Prev
local Left = script.Parent.Directional.Left
local Right = script.Parent.Directional.Right
local Up = script.Parent.Directional.Up
local Down = script.Parent.Directional.Down

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local cameraCount = 0
local currentCameraIndex = 1
local cameras = {}
local initialOffset = CFrame.Angles(-math.rad(0), 0, 0)

for _, CameraModel in ipairs(game.Workspace.Cameras:GetDescendants()) do
	if CameraModel:IsA("Model") and CameraModel.Name == "Camera" then
		local cameraPart = CameraModel:FindFirstChild("Camera")
		if cameraPart and cameraPart:IsA("BasePart") then
			cameraCount = cameraCount + 1 
			local value = Instance.new("NumberValue")
			value.Parent = CameraModel
			value.Value = cameraCount
			value.Name = "CameraNumber"
			cameras[cameraCount] = cameraPart
		end
	end
end

local function switchToCamera(index)
	if cameras[index] then
		Camera.CameraType = Enum.CameraType.Scriptable
		Camera.CFrame = cameras[index].CFrame * initialOffset
		Camera.FieldOfView = 120
		print("Switched to Camera " .. index)
	else
		warn("Camera " .. index .. " not found.")
	end
end

local Viewing = false

local function synchronizeCamera()
	while Viewing do
		if cameras[currentCameraIndex] then
			Camera.CFrame = cameras[currentCameraIndex].CFrame
		end
		wait()
	end
end

local function rotateCamera(direction)
	local rotationSpeed = math.rad(1)
	if cameras[currentCameraIndex] then
		local rotation = CFrame.new()
		if direction == "Up" then
			rotation = CFrame.Angles(-rotationSpeed, 0, 0)
		elseif direction == "Down" then
			rotation = CFrame.Angles(rotationSpeed, 0, 0)
		elseif direction == "Right" then
			rotation = CFrame.Angles(0, -rotationSpeed, 0)
		elseif direction == "Left" then
			rotation = CFrame.Angles(0, rotationSpeed, 0)
		end
		cameras[currentCameraIndex].CFrame = cameras[currentCameraIndex].CFrame * rotation
	end
end

local function startRotate(direction)
	local connection
	connection = RunService.RenderStepped:Connect(function()
		if UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) then
			rotateCamera(direction)
		else
			connection:Disconnect()
		end
	end)
end

Up.MouseButton1Down:Connect(function()
	startRotate("Up")
end)

Down.MouseButton1Down:Connect(function()
	startRotate("Down")
end)

Right.MouseButton1Down:Connect(function()
	startRotate("Right")
end)

Left.MouseButton1Down:Connect(function()
	startRotate("Left")
end)

button.MouseButton1Click:Connect(function()
	print("Clicked")
	if Viewing then
		Viewing = false
		NextCam.Visible = false
		PrevCam.Visible = false
		Camera.CameraType = Enum.CameraType.Custom
		Camera.FieldOfView = 70
		print("Camera changed to Custom")
	else
		Viewing = true
		NextCam.Visible = true
		PrevCam.Visible = true
		switchToCamera(currentCameraIndex)
		synchronizeCamera()
	end
end)

NextCam.MouseButton1Click:Connect(function()
	if Viewing then
		currentCameraIndex = currentCameraIndex % cameraCount + 1
		switchToCamera(currentCameraIndex)
	end
end)

3 Likes

You can use one of my previous solutions in this case.
This method uses spherical coordinates.
Just put the radius to zero (or omit it entirely).

Theta is left/right and phi is up/down
This code is an example so you can understand how it works
If you feel lazy (like me) you can also just copy it I don’t mind

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local currentCamera = workspace.CurrentCamera
currentCamera.CameraType = Enum.CameraType.Scriptable

local subjectPosition = Vector3.new(0, 10, 0)

local sensitivity = 0.5
local radius = 0 -- You can also omit radius from the function but for ease of use it's put to 0
local theta = 0
local phi = 90

function SphericalCoordinate(r: number, theta: number, phi: number)
	local Position = Vector3.new(
		r * math.sin(phi) * math.cos(theta),
		r * math.cos(phi),
		r * math.sin(phi) * math.sin(theta)
	)

	local Hat_r = Vector3.new(
		math.sin(phi) * math.cos(theta),
		math.cos(phi),
		math.sin(phi) * math.sin(theta)
	)

	local Hat_theta = Vector3.new(
		-math.sin(theta),
		0,
		math.cos(theta)
	)

	local Hat_phi = Vector3.new(
		math.cos(phi) * math.cos(theta),
		-math.sin(phi),
		math.cos(phi) * math.sin(theta)
	)

	return {
		Position = Position,
		UpVector = -Hat_phi,
		LeftVector = Hat_theta,
		FrontVector = -Hat_r,
		DownVector = Hat_phi,
		RightVector = -Hat_theta,
		BackVector = Hat_r
	}
end

UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
	if gameProcessedEvent then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end

	UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
end)

UserInputService.InputEnded:Connect(function(input: InputObject, gameProcessedEvent: boolean)
	if gameProcessedEvent then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end

	UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end)

RunService.RenderStepped:Connect(function()
	for index, value in pairs(UserInputService:GetKeysPressed()) do
		if value.KeyCode == Enum.KeyCode.W then
			phi = (phi + 2 * sensitivity) % 360
		end
		if value.KeyCode == Enum.KeyCode.A then
			theta = (theta + -2 * sensitivity) % 360
		end
		if value.KeyCode == Enum.KeyCode.S then
			phi = (phi + -2 * sensitivity) % 360
		end
		if value.KeyCode == Enum.KeyCode.D then
			theta = (theta + 2 * sensitivity) % 360
		end
	end

	local sphericalCoordinate = SphericalCoordinate(
		radius,
		theta * math.pi / 180,
		phi * math.pi / 180
	)

	currentCamera.CFrame = CFrame.fromMatrix(
		sphericalCoordinate.Position + subjectPosition,
		sphericalCoordinate.RightVector, 
		sphericalCoordinate.UpVector, 
		sphericalCoordinate.BackVector
	)
end)

Previous posts


Hope this is what you were looking for.
If you have any questions feel free to ask me.
Hope you have a good day/night!

1 Like

That is exactly what I was looking for, thank you for the help, I didn’t think it was that simple to do.

1 Like

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