GSmGpE6CwX2y9JjB25B8
We use cookies on this site to enhance your user experience

Camera Manipulation

Camera Manipulation

Aug 29 2019, 9:15 AM PST 15 min

Beyond basic articles/customizing the camera|camera customization, you can manipulate the game camera to create specific player/character view systems, lock the camera to a world position, create unique camera effects, and more.

Camera Properties

The Roblox Camera has several built-in properties, including:

Property Description
Camera/CFrame The BasePart/CFrame|CFrame of the camera. This property is frequently used to position and move the camera in game.
Camera/Focus The point in 3D space where the camera is looking. When pointing the camera in a specific direction, you should update this property because certain visuals will be more detailed depending on how close they are to the focus point.
Camera/FieldOfView The extent of the observable game world that can be seen on screen, measured between 1–120 degrees in the vertical direction (default is 70).

Scripting the Camera

The first step to camera manipulation is getting the Workspace/CurrentCamera|CurrentCamera object. Because each player’s camera is local to their device, this should be done in a LocalScript within StarterPlayerStarterPlayerScripts.

local camera = workspace.CurrentCamera

With the Workspace/CurrentCamera|CurrentCamera object stored, you can now achieve the following scenarios and many more.

Over-the-Shoulder

A basic over-the-shoulder camera, commonly found in third-person shooter games, can be achieved with the following script. This camera stays locked behind the character’s back and players use the mouse to turn (not directional input).

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

local camera = workspace.CurrentCamera
local cameraOffset = Vector3.new(2, 2, 8)
local player = Players.LocalPlayer

player.CharacterAdded:Connect(function(character)

	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
	humanoid.AutoRotate = false

	local cameraAngleX = 0
	local cameraAngleY = 0

	local function playerInput(actionName, inputState, inputObject)
		-- Calculate camera/player rotation on input change
		if inputState == Enum.UserInputState.Change then
			cameraAngleX = cameraAngleX - inputObject.Delta.X
			-- Reduce vertical mouse/touch sensitivity and clamp vertical axis
			cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
			-- Rotate root part CFrame by X delta
			rootPart.CFrame = rootPart.CFrame * CFrame.Angles(0, math.rad(-inputObject.Delta.X), 0)
		end
	end
	ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)

	RunService.RenderStepped:Connect(function()
		if camera.CameraType ~= Enum.CameraType.Scriptable then
			camera.CameraType = Enum.CameraType.Scriptable
		end
		local startCFrame = CFrame.new((rootPart.CFrame.Position)) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
		local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
		local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
		camera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
	end)
end)

local function focusControl(actionName, inputState, inputObject)
	-- Lock and hide mouse icon on input began
	if inputState == Enum.UserInputState.Begin then
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		UserInputService.MouseIconEnabled = false
		ContextActionService:UnbindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)
	end
end
ContextActionService:BindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)

Scope In/Out

A simple scope in/out camera system can be implemented using the following script. This example uses the middle mouse button to zoom in and out, and the ability of ContextActionService to create a virtual zoom button on mobile devices.

local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")

local camera = workspace.CurrentCamera
local player = Players.LocalPlayer

local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
local initialFieldOfView = camera.FieldOfView
local initialCameraPosition = camera.CFrame
local isZoomed = false
local inTween = false
local target

player.CharacterAdded:Connect(function(character)

	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
	local initialWalkSpeed = humanoid.WalkSpeed
	local initialJumpPower = humanoid.JumpPower

	local cameraAngleX = 0
	local cameraAngleY = 0

	local function moveInput(actionName, inputState, inputObject)
		if not isZoomed then return end
		if inputState == Enum.UserInputState.Change then
			cameraAngleX = math.clamp(cameraAngleX-inputObject.Delta.X*0.4, -90, 90)
			cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
			local camPosition = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -2))
			local camRotation = camPosition * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
			target = camRotation:ToWorldSpace(CFrame.new(0, 0, -20))
			camera.CFrame = CFrame.new(camRotation.Position, target.Position)
			camera.Focus = CFrame.new(target.Position)
		end
	end

	local function scopeInOut(actionName, inputState, inputObject)
		if inTween == true then return end
		if (inputObject.UserInputType == Enum.UserInputType.MouseButton3 and (inputState == Enum.UserInputState.Begin or inputState == Enum.UserInputState.End))
			or (inputObject.UserInputType == Enum.UserInputType.Touch and inputState == Enum.UserInputState.End) then
			inTween = true
			-- Zoom in
			if isZoomed == false then
				initialCameraPosition = camera.CFrame
				camera.CameraType = Enum.CameraType.Scriptable
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
				UserInputService.MouseIconEnabled = false
				-- Rotate root part CFrame according to camera direction
				local faceToward = camera.CFrame:Lerp(rootPart.CFrame, 1.1)
				rootPart.CFrame = CFrame.new(rootPart.CFrame.Position, Vector3.new(faceToward.Position.X, rootPart.Position.Y, faceToward.Position.Z))
				target = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -20))
				cameraAngleX, cameraAngleY = 0, 0
				humanoid.WalkSpeed = 0
				humanoid.JumpPower = 0
				wait(0.05)
				local camPosition = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -2))
				camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
				local tween = TweenService:Create(camera, tweenInfo, {CFrame=camPosition, FieldOfView=12})
				tween.Completed:Connect(function()
					ContextActionService:BindAction("MoveInput", moveInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
					ContextActionService:SetTitle("ScopeInOut", "–")
					camera.Focus = CFrame.new(target.Position)
					isZoomed = true
					inTween = false
				end)
				tween:Play()
			-- Zoom out
			elseif isZoomed == true then
				ContextActionService:UnbindAction("MoveInput", moveInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
				UserInputService.MouseBehavior = Enum.MouseBehavior.Default
				UserInputService.MouseIconEnabled = true
				local tween = TweenService:Create(camera, tweenInfo, {CFrame=initialCameraPosition, FieldOfView=initialFieldOfView})
				tween.Completed:Connect(function()
					ContextActionService:SetTitle("ScopeInOut", "+")
					camera.CameraType = Enum.CameraType.Custom
					humanoid.WalkSpeed = initialWalkSpeed
					humanoid.JumpPower = initialJumpPower
					isZoomed = false
					inTween = false
				end)
				tween:Play()
			end	
		end
	end
	ContextActionService:BindAction("ScopeInOut", scopeInOut, true, Enum.UserInputType.MouseButton3)
	ContextActionService:SetPosition("ScopeInOut", UDim2.new(0.65, 0, 0.1, 0))
	ContextActionService:SetTitle("ScopeInOut", "+")
end)

Rotate Around Object

To rotate the camera fully or partially around a part, experiment with the following script which features adjustable camera offset, rotation time, repetition count, easing styles, and more.

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

local target = workspace.Part  -- The object to rotate around
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
camera.Focus = target.CFrame
local rotationAngle = Instance.new("NumberValue")
local tweenComplete = false

local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15  -- Time in seconds
local rotationDegrees = 360
local rotationRepeatCount = -1  -- Use -1 for infinite repeats
local lookAtTarget = true  -- Whether the camera tilts to point directly at the target

local function updateCamera()
	local rotatedCFrame = CFrame.Angles(0, math.rad(rotationAngle.Value), 0)
	camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
	if lookAtTarget == true then
		camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
	end
end

-- Set up and start rotation tween
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
tween.Completed:Connect(function()
	tweenComplete = true
end)
tween:Play()

-- Update camera position while tween runs
RunService.RenderStepped:Connect(function()
	if tweenComplete == false then
		updateCamera()
	end
end)
Tags:
  • camera
  • view
  • control