Skip to content

Camera

Camera is a CoreObject which is used both to configure Player Camera settings as well as to represent the position and rotation of the Camera in the world. Cameras can be configured in various ways, usually following a specific Player's view, but can also have a fixed orientation and/or position.

Each Player (on their client) can have a default Camera and an override Camera. If they have neither, camera behavior falls back to a basic third-person behavior. Default Cameras should be used for main gameplay while override Cameras are generally employed as a temporary view, such as a when the Player is sitting in a mounted turret.

Properties

Property Name Return Type Description Tags
followPlayer Player Which Player's view the camera should follow. Set to the local Player for a first or third person camera. Set to nil to detach. Read-Write
isOrthographic boolean Whether the camera uses an isometric (orthographic) view or perspective. Read-Write, Deprecated
fieldOfView number The field of view when using perspective view. Clamped between 1.0 and 170.0. Read-Write
viewWidth number The width of the view with an isometric view. Has a minimum value of 1.0. Read-Write
useCameraSocket boolean If you have a followPlayer, then use their camera socket. This is often preferable for first-person cameras, and gives a bit of view bob. Read-Write
currentDistance number The distance controlled by the Player with scroll wheel (by default). Read-Write, Client-Only
isDistanceAdjustable boolean Whether the Player can control their camera distance (with the mouse wheel by default). Creators can still access distance through currentDistance below, even if this value is false. Read-Write
minDistance number The minimum distance the Player can zoom in to. Read-Write
maxDistance number The maximum distance the Player can zoom out to. Read-Write
rotationMode RotationMode Which base rotation to use. Values: RotationMode.CAMERA, RotationMode.NONE, RotationMode.LOOK_ANGLE. Read-Write
hasFreeControl boolean Whether the Player can freely control their rotation (with mouse or thumbstick). Requires movement mode RotationMode.CAMERA. This has no effect if the camera is following a player. Read-Write
currentPitch number The current pitch of the Player's free control. Read-Write, Client-Only
minPitch number The minimum pitch for free control. Read-Write
maxPitch number The maximum pitch for free control. Read-Write
isYawLimited boolean Whether the Player's yaw has limits. If so, maxYaw must be at least minYaw, and should be outside the range [0, 360] if needed. Read-Write
currentYaw number The current yaw of the Player's free control. Read-Write, Client-Only
minYaw number The minimum yaw for free control. Read-Write
maxYaw number The maximum yaw for free control. Read-Write
useAsAudioListener boolean Whether the local player's audio should be attenuated and spatialized based on their view position while this is the active camera. Read-Write
audioListenerOffset Vector3 This property is deprecated. Please use the GetAudioListenerOffset() and SetAudioListenerOffset() functions instead. Read-Write, Deprecated
isCameraCollisionEnabled boolean When true, this camera will collide with objects that have camera collision enabled. When set to false, the camera will not collide with any objects. Defaults to true. Read-Write

Functions

Function Name Return Type Description Tags
GetPositionOffset() Vector3 An offset added to the camera or follow target's eye position to the Player's view. None
SetPositionOffset(Vector3) None An offset added to the camera or follow target's eye position to the Player's view. None
GetRotationOffset() Rotation A rotation added to the camera or follow target's eye position. None
SetRotationOffset(Rotation) None A rotation added to the camera or follow target's eye position. None
GetAudioListenerOffset() Vector3 Returns the local offset to the view position when using this camera as the audio listener. None
SetAudioListenerOffset(Vector3) None Sets the local offset to the view position when using this camera as the audio listener. None
Capture(CameraCaptureResolution, [table optionalParameters]) CameraCapture Captures an image at the specified resolution using this camera. Returns a CameraCapture object that may be used to display this image or refresh the capture. May return nil if the maximum number of capture instances at the desired resolution has been exceeded. The optional parameter table is currently unused. Client-Only

Examples

Example using:

Capture

This example uses the Camera's Capture() and Refresh() to implement a rear-view mirror that appears when the player is using a vehicle. For the UI image to look correct, it should have equal width and height, as well as Flip Horizontal enabled.

local CAMERA = script:GetCustomProperty("Camera"):WaitForObject()
local UI_ROOT = script:GetCustomProperty("Root"):WaitForObject()
local IMAGE = script:GetCustomProperty("UIImage"):WaitForObject()
local PLAYER = Game.GetLocalPlayer()

local OFFSET_UP = 150
local OFFSET_BACK = -310

local camCapture = nil

function Capture()
    if camCapture and camCapture:IsValid() then
        camCapture:Refresh()
    else
        camCapture = CAMERA:Capture(CameraCaptureResolution.MEDIUM)
        IMAGE:SetCameraCapture(camCapture)
    end
end

function Tick()
    if Object.IsValid(PLAYER.occupiedVehicle) then
        -- Rotate the camera so it's looking back
        local rot = PLAYER.occupiedVehicle:GetWorldRotation()
        local q = Quaternion.New(rot)
        local upVector = q:GetUpVector()
        local forwardVector = q:GetForwardVector()
        rot = Rotation.New(-forwardVector, upVector)
        CAMERA:SetWorldRotation(rot)
        -- Position the camera relative to the vehicle
        local pos = PLAYER.occupiedVehicle:GetWorldPosition()
        pos = pos + upVector * OFFSET_UP + forwardVector * OFFSET_BACK
        CAMERA:SetWorldPosition(pos)
        -- Update the image
        Capture()
        -- Player is in a vehicle, enable visibility of the mirror
        UI_ROOT.visibility = Visibility.INHERIT
    else
        -- Hide the rear-view mirror, as the player is not in a vehicle
        UI_ROOT.visibility = Visibility.FORCE_OFF
    end
end

See also: CameraCapture.Refresh | UIImage.SetCameraCapture | Game.GetLocalPlayer | Player.occupiedVehicle | Quaternion.GetForwardVector | CoreObject.visibility


Example using:

GetPositionOffset

SetPositionOffset

The following example implements a camera shake based on movement of the camera's z-axis. This script should be placed as a child of the game's camera. The shake script doesn't know "when" to shake--that decision comes from elsewhere in the project, where the event Events.BroadcastToPlayer(player, "CameraShake") should be called to initiate the effect.

local CAMERA = script.parent

-- ShakePower, Frequency, and DecaySpeed can be customized with custom properties
local SHAKE_POWER = script:GetCustomProperty("ShakePower") or 40
local FREQUENCY = script:GetCustomProperty("Frequency") or 90
local DECAY_SPEED = script:GetCustomProperty("DecaySpeed") or 12

local amplitude = 0
local startPositionOffset = Vector3.New()

function Tick(deltaTime)
    if amplitude > 0 then
        amplitude = CoreMath.Lerp(amplitude, 0, deltaTime * DECAY_SPEED)

        -- Shake on the Z-axis (position)
        local z = math.sin(time() * FREQUENCY) * amplitude
        local pos = Vector3.New(0, 0, z)
        CAMERA:SetPositionOffset(pos + startPositionOffset)
    end
end

function StartShake(multiplier)
    amplitude = SHAKE_POWER

    if multiplier then
        amplitude = amplitude * multiplier
    end

    startPositionOffset = CAMERA:GetPositionOffset()
end

-- To initiate a shake, call the "CameraShake" event elsewhere in the game
Events.Connect("CameraShake", StartShake)

See also: CoreObject.parent | Vector3.New | CoreMath.Lerp | Events.Connect | CoreLua.Tick


Example using:

GetRotationOffset

SetRotationOffset

The following example implements a camera shake based on rotation of the camera's pitch. This script should be placed as a child of the game's camera. The shake script doesn't know "when" to shake--that decision comes from elsewhere in the project, where the event Events.BroadcastToPlayer(player, "CameraShake") should be called to initiate the effect.

local CAMERA = script.parent

-- ShakePower, Frequency, and DecaySpeed can be customized with custom properties
local SHAKE_POWER = script:GetCustomProperty("ShakePower") or 10
local FREQUENCY = script:GetCustomProperty("Frequency") or 90
local DECAY_SPEED = script:GetCustomProperty("DecaySpeed") or 12

local amplitude = 0
local startRotationOffset = Rotation.New()

function Tick(deltaTime)
    if amplitude > 0 then
        amplitude = CoreMath.Lerp(amplitude, 0, deltaTime * DECAY_SPEED)

        -- Shake on the pitch axis (rotation)
        local pitch = math.sin(time() * FREQUENCY) * amplitude
        local rot = Rotation.New(0, pitch, 0)
        CAMERA:SetRotationOffset(rot + startRotationOffset)
    end
end

function StartShake(multiplier)
    amplitude = SHAKE_POWER

    if multiplier then
        amplitude = amplitude * multiplier
    end

    startRotationOffset = CAMERA:GetRotationOffset()
end

-- To initiate a shake, call the "CameraShake" event elsewhere in the game
Events.Connect("CameraShake", StartShake)

See also: CoreObject.parent | Rotation.New | CoreMath.Lerp | Events.Connect | CoreLua.Tick


Example using:

currentPitch

currentYaw

This sample explores the parallel between the player's rotation, the camera's rotation and the camera's view angles expressed in the currentPitch and currentYaw properties. The camera's "free control" and "rotation mode" are adjusted so the view angle properties give useful information--that's because if "free control" is disabled the view angles always return zero. This script expects to be in a client context. Results will vary depending on player settings (for example Facing Mode) as well as other camera settings.

function Tick()
    local player = Game.GetLocalPlayer()
    local rot = player:GetWorldRotation()

    UI.PrintToScreen("Player:")
    UI.PrintToScreen("pitch = " .. rot.y)
    UI.PrintToScreen("yaw = " .. rot.z)
    UI.PrintToScreen("")

    local camera = World.FindObjectsByType("Camera")[1]
    camRot = camera:GetWorldRotation()
    camera.rotationMode = RotationMode.CAMERA
    camera.hasFreeControl = true

    UI.PrintToScreen("Camera:")
    UI.PrintToScreen("pitch = " .. camRot.y)
    UI.PrintToScreen("yaw = " .. camRot.z)
    UI.PrintToScreen("")

    UI.PrintToScreen("View Angles:")
    UI.PrintToScreen("pitch = " .. camera.currentPitch)
    UI.PrintToScreen("yaw = " .. camera.currentYaw)

    UI.PrintToScreen("")
    UI.PrintToScreen("(view angle yaw) - (player yaw) = " .. (camera.currentYaw - rot.z))

    Task.Wait(3)
end

See also: Game.GetLocalPlayer | Player.GetWorldRotation | UI.PrintToScreen | Rotation.y | World.FindObjectsByType | CoreObject.GetWorldRotation | Camera.rotationMode | Task.Wait


Example using:

fieldOfView

isOrthographic

currentDistance

isDistanceAdjustable

The following example implements a zoom/scoping effect that activates by holding the secondary action (right mouse button, by default). The effect smoothly interpolates a few camera properties, in addition to making the player invisible to the local view, so they don't obstruct the camera during the zoom. This kind of mechanic is generally attached to a weapon, but in this case the script expects to be a child of the camera directly. No equipment is involved in this example.

local CAMERA = script.parent

-- These could be added as custom properties
local TARGET_FOV = script:GetCustomProperty("TargetFOV") or 10
local LERP_SPEED = script:GetCustomProperty("LerpSpeed") or 20

local startFov = CAMERA.fieldOfView
local startPositionOffset = CAMERA:GetPositionOffset()
local startIsDistanceAdjustable = CAMERA.isDistanceAdjustable
local pressedDistance = 0

local player = Game.GetLocalPlayer()

local isPressing = false

function Tick(deltaTime)
    -- Interpolate FOV, Position and Distance
    local fov = CAMERA.fieldOfView
    local pos = CAMERA:GetPositionOffset()

    local t = deltaTime * LERP_SPEED

    if isPressing then
        fov = CoreMath.Lerp(fov, TARGET_FOV, t)
        pos = Vector3.Lerp(pos, Vector3.ZERO, t)

        if pos.size < 4 then
            -- As the camera approaches the destination, turn off the player
            player.isVisibleToSelf = false
        end
    else
        fov = CoreMath.Lerp(fov, startFov, t)
        pos = Vector3.Lerp(pos, startPositionOffset, t)
        -- Turn the player back on
        player.isVisibleToSelf = true
    end

    CAMERA.fieldOfView = fov
    CAMERA:SetPositionOffset(pos)

    -- Interpolate distance
    local distance = CAMERA.currentDistance

    if isPressing then
        distance = CoreMath.Lerp(distance, 0, t)
    else
        if not CAMERA.isDistanceAdjustable then
            if math.abs(distance - pressedDistance) < 1 then
                distance = pressedDistance
                CAMERA.isDistanceAdjustable = startIsDistanceAdjustable
            else
                distance = CoreMath.Lerp(distance, pressedDistance, t)
            end
        end
    end

    CAMERA.currentDistance = distance
end

function OnActionPressed(player, action)
    if action == "Aim" then
        isPressing = true

        CAMERA.isDistanceAdjustable = false
        pressedDistance = CAMERA.currentDistance
    end
end

function OnActionReleased(player, action)
    if action == "Aim" then
        isPressing = false
    end
end

Input.actionPressedEvent:Connect(OnActionPressed)
Input.actionReleasedEvent:Connect(OnActionReleased)

See also: CoreObject.parent | Camera.GetPositionOffset | Game.GetLocalPlayer | CoreMath.Lerp | Vector3.Lerp | Player.isVisibleToSelf | Input.actionPressedEvent | Event.Connect | CoreLua.Tick


Example using:

followPlayer

In this example, players can change their view to look at another player by pressing the secondary action (default is right mouse button). The script expects to be a child of the game's camera, which is usually in a client context.

local CAMERA = script.parent

function NextPlayer()
    local allPlayers = Game.GetPlayers()

    if CAMERA.followPlayer == nil then
        CAMERA.followPlayer = allPlayers[1]
    else
        for index,player in ipairs(allPlayers) do
            if CAMERA.followPlayer == player then
                local selectedPlayer
                if index == #allPlayers then
                    selectedPlayer = allPlayers[1]
                else
                    selectedPlayer = allPlayers[index + 1]
                end

                if selectedPlayer == Game.GetLocalPlayer() then
                    CAMERA:Detach()
                    CAMERA.followPlayer = selectedPlayer
                else
                    CAMERA.followPlayer = nil
                    CAMERA:AttachToPlayer(selectedPlayer, "root")
                    CAMERA:SetPosition(Vector3.New(0,0,200))
                end
                return
            end
        end
    end
end

function OnActionPressed(player, action)
    if action == "Aim" then
        NextPlayer()
    end
end

Input.actionPressedEvent:Connect(OnActionPressed)

See also: CoreObject.parent | Game.GetPlayers | Vector3.New | Input.actionPressedEvent | Event.Connect


Example using:

rotationMode

hasFreeControl

minPitch

maxPitch

isYawLimited

minYaw

maxYaw

minDistance

maxDistance

In this example of an advanced spectator implementation, suitable for a third-person game, players are able to look through the view of others by pressing the secondary action (default is right mouse button). This example demonstrates how the spectator can be constrained (or not) to the look angle of the player they are following. If the CONSTRAIN_SPECTATOR_LOOK constant is set to true, then players will not be able to rotate the camera freely while they are spectating.

local CAMERA = script.parent

local CONSTRAIN_SPECTATOR_LOOK = false
local PITCH_CONSTRAINED_ANGLE = 15
local YAW_CONSTRAINED_ANGLE = 15

local defaultMinPitch = CAMERA.minPitch
local defaultMaxPitch = CAMERA.maxPitch
local defaultIsYawLimited = CAMERA.isYawLimited
local defaultMinYaw = CAMERA.minYaw
local defaultMaxYaw = CAMERA.maxYaw
local defaultDistance = CAMERA.currentDistance
local defaultIsDistanceAdjustable = CAMERA.isDistanceAdjustable
local defaultMinDistance = CAMERA.minDistance
local defaultMaxDistance = CAMERA.maxDistance

local followedPlayer = Game.GetLocalPlayer()

function SpectatePlayer(player)
    if player == Game.GetLocalPlayer() then
        -- Attach back to the local player
        CAMERA:Detach()
        CAMERA.followPlayer = player
        CAMERA.rotationMode = RotationMode.LOOK_ANGLE
        -- We could set 'hasFreeControl' back to its original value, but we
        -- don't have to, as it has no effect when 'followPlayer' is set.

        -- Revert spectator constraints
        CAMERA.minPitch = defaultMinPitch
        CAMERA.maxPitch = defaultMaxPitch
        CAMERA.isYawLimited = defaultIsYawLimited
        CAMERA.minYaw = defaultMinYaw
        CAMERA.maxYaw = defaultMaxYaw
        CAMERA.minDistance = defaultMinDistance
        CAMERA.maxDistance = defaultMaxDistance
        CAMERA.isDistanceAdjustable = defaultIsDistanceAdjustable
    else
        -- Attach to another player
        CAMERA.followPlayer = nil
        CAMERA:AttachToPlayer(player, "root")
        CAMERA:SetPosition(Vector3.New(0,0,179))
        CAMERA.rotationMode = RotationMode.CAMERA
        CAMERA.hasFreeControl = true

        -- Apply spectator constraints if desired
        if CONSTRAIN_SPECTATOR_LOOK then
            CAMERA.minPitch = -PITCH_CONSTRAINED_ANGLE
            CAMERA.maxPitch = PITCH_CONSTRAINED_ANGLE
            CAMERA.isYawLimited = true
            CAMERA.minYaw = -YAW_CONSTRAINED_ANGLE
            CAMERA.maxYaw = YAW_CONSTRAINED_ANGLE
            CAMERA.currentDistance = defaultDistance
            CAMERA.minDistance = defaultDistance
            CAMERA.maxDistance = defaultDistance
        end
    end
end

function NextPlayer()
    local allPlayers = Game.GetPlayers()

    if followedPlayer == nil then
        CAMERA.followPlayer = allPlayers[1]
    else
        for index, player in ipairs(allPlayers) do
            if followedPlayer == player then
                -- Select who the next player is
                local selectedPlayer
                if index == #allPlayers then
                    selectedPlayer = allPlayers[1]
                else
                    selectedPlayer = allPlayers[index + 1]
                end

                followedPlayer = selectedPlayer

                SpectatePlayer(selectedPlayer)
                return
            end
        end
    end
end

function OnActionPressed(player, action)
    if action == "Aim" then
        NextPlayer()
    end
end

Input.actionPressedEvent:Connect(OnActionPressed)

See also: Camera.followPlayer | CoreObject.parent | Game.GetLocalPlayer | Vector3.New | Input.actionPressedEvent | Event.Connect


Example using:

useCameraSocket

The following client script allows players in a first-person game to turn on/off the head-bob effect that is associated with the camera being attached to the camera socket. To toggle the head-bob press 0.

-- Action name to use from a binding set.
local ACTION_NAME = script:GetCustomProperty("ActionName")

function OnActionPressed(player, action)
    if action == ACTION_NAME then
        local camera = player:GetActiveCamera()
        camera.useCameraSocket = not camera.useCameraSocket
    end
end

Input.actionPressedEvent:Connect(OnActionPressed)

See also: Player.GetActiveCamera | Input.actionPressedEvent | CoreObject.GetCustomProperty | Event.Connect


Example using:

viewWidth

In this example, designed to work with a top-down orthographic camera, the view is zoomed in when the secondary action is pressed (default is right mouse button). Works best in a client context.

local CAMERA = script.parent

-- This script only works for orthographics cameras
CAMERA.isOrthographic = true

-- These could be added as custom properties
local TARGET_VIEW_WIDTH = script:GetCustomProperty("TargetViewWidth") or 300
local LERP_SPEED = script:GetCustomProperty("LerpSpeed") or 20

local startViewWidth = CAMERA.viewWidth

local player = Game.GetLocalPlayer()

local isPressing = false

function Tick(deltaTime)
    -- Interpolate the View Width
    local viewWidth = CAMERA.viewWidth

    local t = deltaTime * LERP_SPEED

    if isPressing then
        viewWidth = CoreMath.Lerp(viewWidth, TARGET_VIEW_WIDTH, t)
    else
        viewWidth = CoreMath.Lerp(viewWidth, startViewWidth, t)
    end

    CAMERA.viewWidth = viewWidth
end

function OnActionPressed(player, action)
    if action == "Aim" then
        isPressing = true
    end
end

function OnActionReleased(player, action)
    if action == "Aim" then
        isPressing = false
    end
end

Input.actionPressedEvent:Connect(OnActionPressed)
Input.actionReleasedEvent:Connect(OnActionReleased)

See also: CoreObject.parent | Camera.isOrthographics | Game.GetLocalPlayer | CoreMath.Lerp | Input.actionPressedEvent | Event.Connect | CoreLua.Tick



Last update: June 23, 2022