A side-scrolling view keeps the camera at a fixed position relative to the side of the character, giving the world a two-dimensional feel.
Scripting the Camera
-
Expand StarterPlayer, and in StarterPlayerScripts add a LocalScript named CameraManager.
-
At the top of the script, get the Players service, and then in a new variable get the local player.
local Players = game:GetService("Players") local player = Players.LocalPlayer
-
Create a function called
updateCamera
. This will hold the logic needed to get and set a new position for the camera.local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() end
-
Inside the function, get the player’s character model and check if it exists by using an if statement.
local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() local character = player.Character if character then end end
Client-side Camera
Only a player can see their own camera configuration, so it is always controlled using LocalScripts.
Pointing the Camera
All character models contain a part named HumanoidRootPart, which can be used to get the character’s position in the world. This will be the position the camera points at.
-
Use
FindFirstChild
to get the HumanoidRootPart and check it exists using an if statement.local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then end end end
-
The position of the HumanoidRootPart is actually 2 studs below the player’s head. To fix this, add a new
Vector3
with a height of 2 studs to the root’s position.local Players = game:GetService("Players") local player = Players.LocalPlayer local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) end end end
Tip: Separate individual numbers into variables to make them easier to tweak later.
Setting the Camera Position
The camera will also need a position. To give the player’s view a 2D side-scrolling look, the camera will need to look directly at the side of the character.
-
Place the camera to the side of the player by adding depth to just the Z axis of the camera’s position using a Vector3.
local player = Players.LocalPlayer local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) end end end
Update CurrentCamera
Now that the variables for the camera’s position and the camera’s target are ready, it’s time to update the camera’s position. The player’s camera can be accessed through the CurrentCamera property of Workspace. The camera has a CFrame
property to determine its position.
You can use CFrame.lookAt
to update the camera. It takes two positions and creates a CFrame located at the first position pointed towards the second.
-
Use
CFrame.lookAt
to create a CFrame that is positioned atcameraPosition
and pointed towardrootPosition
.local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition) end end end
Sync the Camera
The last step is to run this function repeatedly to keep the camera in sync with the player. The image the player sees is constantly refreshing. The split second it takes to do all of the calculations necessary is called the render step.
RunService:BindToRenderStep
makes it simple to execute a function on every frame by accepting these three parameters:
name
- The name of this binding, which should be unique so it won’t clash with other functions of the same name.priority
- The higher the number, the higher the priority. This function should run after Roblox’s default camera update, so the priority is set to 1 level higher than the internal camera’s RenderPriority.function
- The function to be bound to the render step.
-
Use
RunService:BindToRenderStep
to bind theupdateCamera
function to the render step.local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition) end end end RunService:BindToRenderStep("SidescrollingCamera", Enum.RenderPriority.Camera.Value + 1, updateCamera)
-
Playtest your code. Use the A and D keys to move your character from side to side.
Previous Page Getting Started Next Page Creating an Isometric Camera