Log inGet started

camera

Updated 6 September 2026

The camera namespace — 41 functions.

globals/camera/active

camera.active() -> string?

Entity id of the on-screen render camera this frame — whichever camera wins the viewport by priority (the editor fly-camera in edit mode, the gameplay camera in play). Render features, billboards, and input bases that must follow the human's on-screen view read this.

Returns string? — Entity id of the on-screen camera, or nil if none is active — including the frame after that camera's entity is despawned.

local camId = camera.active()

globals/camera/cut

camera.cut()

Declare that the camera on screen cuts: the next frame it draws stands somewhere it did not travel to. Motion vectors are the difference between where a surface projects now and where it projected on the camera's previous frame, and everything temporal reads that difference — the shutter reconstructs the frame by walking it, a temporal resolve reprojects its history along it. Across a cut that difference describes a displacement no surface made, so the frame is reconstructed from taps a whole screen away and belongs to neither shot. A declared cut leaves the camera with no previous frame for exactly one frame, which is the state its very first frame is already in, so every consumer reads zero motion across the cut. Declare it in the same step that places the camera at the new station; declaring it again before that frame draws still costs the one frame. Handing the viewport from one camera to another is already a cut without being declared one: the incoming camera stands where it always stood, and the engine performs the handover, so it is what states it.

camera.cut(); entity(camId).position = { 40, 6, -12 }

globals/camera/editor

camera.editor() -> string?

Entity id of the editor fly-camera (the EditorOnly authoring camera), or nil if the scene has none. This is the camera the editor viewport renders through, so it is the one a capture of the screen sees. Its pose is its entity transform: assign entity(id).position to move it and aim it with the camera toolbox's lookAt, which makes a screen capture repeatable instead of whatever pose the instance booted with.

Returns string? — Entity id of the editor camera, or nil.

local camId = camera.editor()
local id = camera.editor(); entity(id).position = { 12, 8, 12 }; tools.use("camera", "lookAt", id, { 0, 0, 0 })

globals/camera/editorOverride

camera.editorOverride() -> string?

Entity id currently overriding viewport selection, or nil when the viewport is decided by highest-priority-wins.

Returns string? — Entity id of the overriding camera, or nil.

local owner = camera.editorOverride()

globals/camera/get

camera.get(target: (string | EntityRef)) -> CameraReport?

One camera's report from the observation — the same record camera.list yields, for the camera the caller names. Takes an entity id, an entity name, or an entity proxy, the same way the camera tools do.

Parameters

  • target (string | EntityRef) — Entity id, entity name, or entity proxy of the camera to report on.

Returns CameraReport? — The report, or nil when nothing resolves or that camera has none.

local c = camera.get(camera.active()); print(c.frame.far, c.authored.far)
local c = camera.get("minimapCam"); print(c.rendering, c.reason)

globals/camera/list

camera.list() -> { CameraReport }

Every camera in the world as a compact row each, ordered the way the renderer resolves the on-screen camera: highest priority first. Reads the same observation camera.observe does, so a row can never disagree with the full report about whether a camera is enabled or which one drew.

Returns { CameraReport } — One row per camera entity, or an empty list before the first frame.

for _, c in ipairs(camera.list()) do print(c.name, c.enabled, c.rendering) end

globals/camera/main

camera.main() -> string?

Entity id of the main scene camera — the scene camera the viewport is drawn from, and while the editor fly-camera holds the screen, the scene camera that would take it. The gameplay/PlayerPrototype camera, an agent-placed scene camera, or a cutscene camera. Never the editor camera; nil if the scene has only the editor camera. It comes off the same selection the frame does, so writing a pose to it moves what is drawn whenever a scene camera is on screen. The scene camera with the highest authored priority takes it; cameras tied on priority settle on the order the frame visits them, so a scene that needs a specific camera — a prototype and the clone play makes of it both stand at 0 — states a distinct priority rather than resting on that order. For the camera drawn on screen whichever partition owns it, use camera.active().

Returns string? — Entity id of the main scene camera, or nil — including the frame after that camera's entity is despawned, before the scene elects another.

local camId = camera.main(); local cam = camId and entity(camId)

globals/camera/motionTally

camera.motionTally() -> { frames: number, withoutHistory: number }

Frames the camera on screen has drawn, and how many of them had no previous frame to difference their motion vectors against — its first frame, every declared cut, and every frame the viewport changes hands on. Both counts are monotonic across the session, so two readings either side of a run say what happened in between.

Returns { frames: number, withoutHistory: number }{ frames, withoutHistory }.

local before = camera.motionTally().withoutHistory

globals/camera/observe

camera.observe() -> CameraObservation?

Every camera in the world, for the frame that has just been drawn. Answers "why is this camera not showing what I expect" in one call: rendering says whether each camera drew and reason names the single cause when it did not — "disabled", "entityInactive", "targetMissing", "noLayers", "outranked", "notDrawn". Each camera carries both projections: authored is what the Camera component holds and frame is what the renderer actually built, with mismatch naming every field the two disagree on — so a clip range or a lens the frame did not use is one field read. frame, viewProj, frustum and the cost numbers describe a camera that drew; every cost is for that one frame. One snapshot is published per drawn frame, from after the frame is drawn, so a read describes the last frame rather than the world at the instant of the call — a write and a read in one script step return the frame that ran before the write. Put a task.wait() between them to compare a camera either side of a change; frame counts the frames observed, so a poll can wait for it to advance.

Returns CameraObservation? — The observation, or nil before the first frame has been drawn.

local obs = camera.observe(); for _, c in ipairs(obs.cameras) do print(c.name, c.rendering, c.reason) end
local dark = {}; for _, c in ipairs(camera.observe().cameras) do if not c.rendering then table.insert(dark, c.name .. ": " .. tostring(c.reason)) end end

globals/camera/setEditorOverride

camera.setEditorOverride(entityId: string?)

Give one camera the viewport outright, or pass nil to clear it. While set, that camera IS the on-screen camera and priority is never consulted, so no authored priority can take the viewport from it — which is what makes an authoring camera safe to fly over a scene holding a camera at any priority. An override naming a camera that is despawned or disabled falls back to highest-priority-wins rather than blanking the screen.

Parameters

  • entityId string (optional) — Entity id of the camera to route the viewport to, or nil to clear.
camera.setEditorOverride(camera.editor())
camera.setEditorOverride(nil)

globals/camera/viewData

camera.viewData(target: ((string | EntityRef)?)?) -> CameraView?

Camera render data. Called with no argument it is the active viewport camera's data for this frame: world position, which projection it drew and the field describing that frame, viewport pixel size, the 6 world-space frustum planes (the same inward-pointing, normalized planes the renderer culls with), and the view-projection matrix. The camera state a render feature needs for camera-relative work — LOD selection, frustum culling, billboards. Render features also get it as ctx.camera. Called with an entity id it is that camera's data, read from the frame's camera observation, and carries the identity the bare form has no room for: which camera it describes, which frame it was built for, what it rendered into, and the render layers it resolved to.

Parameters

  • target ((string | EntityRef)?) (optional) — Entity id, name, or proxy of the camera to read, or nil for the viewport camera.

Returns CameraView? — The camera view data, or nil when that camera drew no frame.

local c = camera.viewData(); if c then print(c.position.x, c.fovY) end
local minimap = camera.viewData("minimapCam"); print(minimap.output.target, minimap.viewport.w)

modules/camera/README

require("@builtin/modules/api/engine/camera") -- camera (also available as global 'camera')

Script-facing camera queries: the main scene camera, the on-screen render camera, the editor fly-camera, per-frame view data, and the camera observation — which cameras drew this frame, with what projection, into what, and at what cost. Public Luau surface over the __camera Internal FFI namespace.

Usage: local camera = require("@builtin/modules/api/engine/camera") Also available as global: camera

modules/camera/active

active(): string?

Entity id of the on-screen render camera this frame — whichever camera wins the viewport by priority (the editor fly-camera in edit mode, the gameplay camera in play). Render features, billboards, and input bases that must follow the human's on-screen view read this.

local camId = camera.active()

modules/camera/cut

cut()

Declare that the camera on screen cuts: the next frame it draws stands somewhere it did not travel to. Motion vectors are the difference between where a surface projects now and where it projected on the camera's previous frame, and everything temporal reads that difference — the shutter reconstructs the frame by walking it, a temporal resolve reprojects its history along it. Across a cut that difference describes a displacement no surface made, so the frame is reconstructed from taps a whole screen away and belongs to neither shot. A declared cut leaves the camera with no previous frame for exactly one frame, which is the state its very first frame is already in, so every consumer reads zero motion across the cut. Declare it in the same step that places the camera at the new station; declaring it again before that frame draws still costs the one frame. Handing the viewport from one camera to another is already a cut without being declared one: the incoming camera stands where it always stood, and the engine performs the handover, so it is what states it.

camera.cut(); entity(camId).position = { 40, 6, -12 }

modules/camera/editor

editor(): string?

Entity id of the editor fly-camera (the EditorOnly authoring camera), or nil if the scene has none. This is the camera the editor viewport renders through, so it is the one a capture of the screen sees. Its pose is its entity transform: assign entity(id).position to move it and aim it with the camera toolbox's lookAt, which makes a screen capture repeatable instead of whatever pose the instance booted with.

local camId = camera.editor()
local id = camera.editor(); entity(id).position = { 12, 8, 12 }; tools.use("camera", "lookAt", id, { 0, 0, 0 })

modules/camera/editorOverride

editorOverride(): string?

Entity id currently overriding viewport selection, or nil when the viewport is decided by highest-priority-wins.

local owner = camera.editorOverride()

modules/camera/get

get(target: (string | EntityRef)): CameraReport?

One camera's report from the observation — the same record camera.list yields, for the camera the caller names. Takes an entity id, an entity name, or an entity proxy, the same way the camera tools do.

Parameters

  • target (string | EntityRef) — Entity id, entity name, or entity proxy of the camera to report on.
local c = camera.get(camera.active()); print(c.frame.far, c.authored.far)
local c = camera.get("minimapCam"); print(c.rendering, c.reason)

modules/camera/list

list(): { CameraReport }

Every camera in the world as a compact row each, ordered the way the renderer resolves the on-screen camera: highest priority first. Reads the same observation camera.observe does, so a row can never disagree with the full report about whether a camera is enabled or which one drew.

for _, c in ipairs(camera.list()) do print(c.name, c.enabled, c.rendering) end

modules/camera/main

main(): string?

Entity id of the main scene camera — the scene camera the viewport is drawn from, and while the editor fly-camera holds the screen, the scene camera that would take it. The gameplay/PlayerPrototype camera, an agent-placed scene camera, or a cutscene camera. Never the editor camera; nil if the scene has only the editor camera. It comes off the same selection the frame does, so writing a pose to it moves what is drawn whenever a scene camera is on screen. The scene camera with the highest authored priority takes it; cameras tied on priority settle on the order the frame visits them, so a scene that needs a specific camera — a prototype and the clone play makes of it both stand at 0 — states a distinct priority rather than resting on that order. For the camera drawn on screen whichever partition owns it, use camera.active().

local camId = camera.main(); local cam = camId and entity(camId)

modules/camera/motionTally

motionTally(): { frames: number, withoutHistory: number }

Frames the camera on screen has drawn, and how many of them had no previous frame to difference their motion vectors against — its first frame, every declared cut, and every frame the viewport changes hands on. Both counts are monotonic across the session, so two readings either side of a run say what happened in between.

local before = camera.motionTally().withoutHistory

modules/camera/observe

observe(): CameraObservation?

Every camera in the world, for the frame that has just been drawn. Answers "why is this camera not showing what I expect" in one call: rendering says whether each camera drew and reason names the single cause when it did not — "disabled", "entityInactive", "targetMissing", "noLayers", "outranked", "notDrawn". Each camera carries both projections: authored is what the Camera component holds and frame is what the renderer actually built, with mismatch naming every field the two disagree on — so a clip range or a lens the frame did not use is one field read. frame, viewProj, frustum and the cost numbers describe a camera that drew; every cost is for that one frame. One snapshot is published per drawn frame, from after the frame is drawn, so a read describes the last frame rather than the world at the instant of the call — a write and a read in one script step return the frame that ran before the write. Put a task.wait() between them to compare a camera either side of a change; frame counts the frames observed, so a poll can wait for it to advance.

local obs = camera.observe(); for _, c in ipairs(obs.cameras) do print(c.name, c.rendering, c.reason) end
local dark = {}; for _, c in ipairs(camera.observe().cameras) do if not c.rendering then table.insert(dark, c.name .. ": " .. tostring(c.reason)) end end

modules/camera/setEditorOverride

setEditorOverride(entityId: string?)

Give one camera the viewport outright, or pass nil to clear it. While set, that camera IS the on-screen camera and priority is never consulted, so no authored priority can take the viewport from it — which is what makes an authoring camera safe to fly over a scene holding a camera at any priority. An override naming a camera that is despawned or disabled falls back to highest-priority-wins rather than blanking the screen.

Parameters

  • entityId string? (optional) — Entity id of the camera to route the viewport to, or nil to clear.
camera.setEditorOverride(camera.editor())
camera.setEditorOverride(nil)

modules/camera/viewData

viewData(target: ((string | EntityRef)?)): CameraView?

Camera render data. Called with no argument it is the active viewport camera's data for this frame: world position, which projection it drew and the field describing that frame, viewport pixel size, the 6 world-space frustum planes (the same inward-pointing, normalized planes the renderer culls with), and the view-projection matrix. The camera state a render feature needs for camera-relative work — LOD selection, frustum culling, billboards. Render features also get it as ctx.camera. Called with an entity id it is that camera's data, read from the frame's camera observation, and carries the identity the bare form has no room for: which camera it describes, which frame it was built for, what it rendered into, and the render layers it resolved to.

Parameters

  • target ((string | EntityRef)?) (optional) — Entity id, name, or proxy of the camera to read, or nil for the viewport camera.
local c = camera.viewData(); if c then print(c.position.x, c.fovY) end
local minimap = camera.viewData("minimapCam"); print(minimap.output.target, minimap.viewport.w)

tools/camera/bindToPlayer

camera.bindToPlayer(camera?: (EntityRef | string)) -> BindToPlayerResult

Make the joining player use this camera in play — bind it to the active scene's PlayerPrototype. The prototype's camera role must be a descendant of its subtree (it is cloned with the player on spawn), so this re-parents the camera into the prototype (keeping its world pose) and sets the prototype's camera ref to it. This is how you choose the player's camera: pair it with a static behavior (camera.set { behavior = "@builtin::controller.menu" }) for a fixed PS1-style camera the player moves within, or a follow behavior (orbital_follow, third_person_follow, first_person, …) for a tracking one. On spawn the player's body is bound as the camera's follow target; a static behavior ignores that and holds its pose. Errors when the scene has no PlayerPrototype (a player-less scene has no player camera to bind). Returns { prototype, camera }.

Parameters

  • camera (EntityRef | string) (optional)

Returns BindToPlayerResult

"fixedCam"

tools/camera/create

camera.create(name: string, position?: (vec3 | number), opts?: CameraCreateOpts) -> shared.CameraInfo

Spawn a new camera entity at a position, with any starting parameters. opts accepts fov, near, far, priority, renderLayers (a layer-name spec like "all !ui"), debugChannel, follow (an entity the camera behavior tracks), behavior (a cameraBehavior-tagged component), lookAt (a world position or entity to aim at), and target (render-to-texture: true or { width, height, name } renders offscreen; omit for the viewport). The scene's primary camera is spawned for you from the PlayerPrototype — use this for ADDITIONAL cameras (a security view, a cutscene angle, a render-texture feed). Returns the new camera's full parameter record.

Parameters

  • name string
  • position (vec3 | number) (optional)
  • opts CameraCreateOpts (optional)

Returns shared.CameraInfo

"securityCam", { 0, 6, 12 }, { fov = 50, renderLayers = "all !ui" }
"feedCam", { x = 2, y = 3, z = 2 }, { target = { width = 512, height = 512 }, lookAt = "player" }

tools/camera/frame

camera.frame(camera?: (EntityRef | string), target: EntityRef | string, opts?: CameraFrameOpts) -> shared.CameraInfo

Position and aim a camera to frame a target entity in one step: place the camera at distance from the target on the yaw/pitch angle, then look at it. opts accepts distance (default 10), yaw (default 0), and pitch (default 20), all degrees. Pass nil for the first argument to frame with the active camera. Returns the camera's updated parameter record.

Parameters

  • camera (EntityRef | string) (optional)
  • target EntityRef | string
  • opts CameraFrameOpts (optional)

Returns shared.CameraInfo

"securityCam", "player", { distance = 8, yaw = 30, pitch = 15 }
nil, "statue"

tools/camera/get

camera.get(camera?: (EntityRef | string)) -> shared.CameraInfo

Read the full parameter record for one camera — position, rotation, fov, near, far, priority, render-layer spec, debug channel, render-target guid, behavior, and follow target — beside whether it is enabled (the switch that decides whether it renders at all), whether it is rendering, the reason it is not, the projection the renderer built for it in frame, and mismatch naming every field where that frame disagrees with the record above. Pass a camera name/id/proxy, or nothing to read the active (on-screen) camera, falling back to the scene's main camera.

Parameters

  • camera (EntityRef | string) (optional)

Returns shared.CameraInfo

"securityCam"

tools/camera/list

camera.list() -> { shared.CameraRow }

List every camera in the scene, enumerated from the live ECS so a just-spawned, hidden, or runtime camera is never missed, sorted by descending priority (the order the renderer resolves the on-screen camera). Each row marks whether it is the active camera (drawn this frame), whether it is enabled — the per-camera switch that decides whether it competes for the viewport or renders into a target at all — whether it is rendering and the reason it is not, whether it is the player camera (the one the PlayerPrototype uses — the joining player's view), and its role"main" (highest-priority active non-editor), "editor" (the editor fly-camera), or "" — plus its priority, fov, render-layer spec, and render-target guid. For the projection the renderer actually built for each camera, and what each cost, read camera.observe().

Returns { shared.CameraRow }

tools/camera/lookAt

camera.lookAt(camera?: LookAtRef, target?: LookAtRef) -> shared.CameraInfo

Aim a camera at a world position or at another entity. The target is an entity name/id or a position { x, y, z } / { x =, y =, z = }. Pass a single target to aim the active camera, or a camera plus a target to aim a specific one. Returns the camera's updated parameter record.

Parameters

  • camera LookAtRef (optional)
  • target LookAtRef (optional)

Returns shared.CameraInfo

"securityCam", "player"
{ 10, 0, 5 }

tools/camera/set

camera.set(camera?: (EntityRef | string | CameraSetParams), params?: CameraSetParams) -> shared.CameraInfo

Change one or more parameters on a camera in a single call. Accepts fov, near, far, priority, debugPass (the diagnostic view this camera renders, BY NAME — "final" (lit), "normal", "depth", a content view like "lightmap", …; renderer.debugPass.list() enumerates them; the live viewport draws the active camera's pass), renderLayers (a layer-name spec like "all !ui" — a bare name includes a layer, !name excludes it), behavior (a cameraBehavior-tagged component ref), and follow (the entity the behavior tracks). debugChannel accepts the raw numeric channel for the same effect. Pass no camera (or nil) to change the active camera; pass { ... } as the only argument for the same. Returns the camera's updated parameter record.

Parameters

  • camera (EntityRef | string | CameraSetParams) (optional)
  • params CameraSetParams (optional)

Returns shared.CameraInfo

"securityCam", { fov = 50, priority = 5 }
{ debugPass = "normal" }
{ debugPass = "final" }   -- back to the lit image

typed/builtin//modules/api/engine/camera/camera/active

camera.active() -> string?

Entity id of the on-screen render camera this frame — whichever camera wins the viewport by priority (the editor fly-camera in edit mode, the gameplay camera in play). Render features, billboards, and input bases that must follow the human's on-screen view read this.

Returns string? — Entity id of the on-screen camera, or nil if none is active — including the frame after that camera's entity is despawned.

local camId = camera.active()

typed/builtin//modules/api/engine/camera/camera/cut

camera.cut()

Declare that the camera on screen cuts: the next frame it draws stands somewhere it did not travel to. Motion vectors are the difference between where a surface projects now and where it projected on the camera's previous frame, and everything temporal reads that difference — the shutter reconstructs the frame by walking it, a temporal resolve reprojects its history along it. Across a cut that difference describes a displacement no surface made, so the frame is reconstructed from taps a whole screen away and belongs to neither shot. A declared cut leaves the camera with no previous frame for exactly one frame, which is the state its very first frame is already in, so every consumer reads zero motion across the cut. Declare it in the same step that places the camera at the new station; declaring it again before that frame draws still costs the one frame. Handing the viewport from one camera to another is already a cut without being declared one: the incoming camera stands where it always stood, and the engine performs the handover, so it is what states it.

camera.cut(); entity(camId).position = { 40, 6, -12 }

typed/builtin//modules/api/engine/camera/camera/editor

camera.editor() -> string?

Entity id of the editor fly-camera (the EditorOnly authoring camera), or nil if the scene has none. This is the camera the editor viewport renders through, so it is the one a capture of the screen sees. Its pose is its entity transform: assign entity(id).position to move it and aim it with the camera toolbox's lookAt, which makes a screen capture repeatable instead of whatever pose the instance booted with.

Returns string? — Entity id of the editor camera, or nil.

local camId = camera.editor()
local id = camera.editor(); entity(id).position = { 12, 8, 12 }; tools.use("camera", "lookAt", id, { 0, 0, 0 })

typed/builtin//modules/api/engine/camera/camera/editorOverride

camera.editorOverride() -> string?

Entity id currently overriding viewport selection, or nil when the viewport is decided by highest-priority-wins.

Returns string? — Entity id of the overriding camera, or nil.

local owner = camera.editorOverride()

typed/builtin//modules/api/engine/camera/camera/get

camera.get(target: (string | EntityRef)) -> CameraReport?

One camera's report from the observation — the same record camera.list yields, for the camera the caller names. Takes an entity id, an entity name, or an entity proxy, the same way the camera tools do.

typed/builtin//modules/api/engine/camera/camera/list

camera.list() -> { CameraReport }

Every camera in the world as a compact row each, ordered the way the renderer resolves the on-screen camera: highest priority first. Reads the same observation camera.observe does, so a row can never disagree with the full report about whether a camera is enabled or which one drew.

Returns { CameraReport } — One row per camera entity, or an empty list before the first frame.

for _, c in ipairs(camera.list()) do print(c.name, c.enabled, c.rendering) end

typed/builtin//modules/api/engine/camera/camera/main

camera.main() -> string?

Entity id of the main scene camera — the scene camera the viewport is drawn from, and while the editor fly-camera holds the screen, the scene camera that would take it. The gameplay/PlayerPrototype camera, an agent-placed scene camera, or a cutscene camera. Never the editor camera; nil if the scene has only the editor camera. It comes off the same selection the frame does, so writing a pose to it moves what is drawn whenever a scene camera is on screen. The scene camera with the highest authored priority takes it; cameras tied on priority settle on the order the frame visits them, so a scene that needs a specific camera — a prototype and the clone play makes of it both stand at 0 — states a distinct priority rather than resting on that order. For the camera drawn on screen whichever partition owns it, use camera.active().

Returns string? — Entity id of the main scene camera, or nil — including the frame after that camera's entity is despawned, before the scene elects another.

local camId = camera.main(); local cam = camId and entity(camId)

typed/builtin//modules/api/engine/camera/camera/motionTally

camera.motionTally() -> { frames: number, withoutHistory: number }

Frames the camera on screen has drawn, and how many of them had no previous frame to difference their motion vectors against — its first frame, every declared cut, and every frame the viewport changes hands on. Both counts are monotonic across the session, so two readings either side of a run say what happened in between.

Returns { frames: number, withoutHistory: number }{ frames, withoutHistory }.

local before = camera.motionTally().withoutHistory

typed/builtin//modules/api/engine/camera/camera/observe

camera.observe() -> CameraObservation?

Every camera in the world, for the frame that has just been drawn. Answers "why is this camera not showing what I expect" in one call: rendering says whether each camera drew and reason names the single cause when it did not — "disabled", "entityInactive", "targetMissing", "noLayers", "outranked", "notDrawn". Each camera carries both projections: authored is what the Camera component holds and frame is what the renderer actually built, with mismatch naming every field the two disagree on — so a clip range or a lens the frame did not use is one field read. frame, viewProj, frustum and the cost numbers describe a camera that drew; every cost is for that one frame. One snapshot is published per drawn frame, from after the frame is drawn, so a read describes the last frame rather than the world at the instant of the call — a write and a read in one script step return the frame that ran before the write. Put a task.wait() between them to compare a camera either side of a change; frame counts the frames observed, so a poll can wait for it to advance.

Returns CameraObservation? — The observation, or nil before the first frame has been drawn.

local obs = camera.observe(); for _, c in ipairs(obs.cameras) do print(c.name, c.rendering, c.reason) end
local dark = {}; for _, c in ipairs(camera.observe().cameras) do if not c.rendering then table.insert(dark, c.name .. ": " .. tostring(c.reason)) end end

typed/builtin//modules/api/engine/camera/camera/setEditorOverride

camera.setEditorOverride(entityId: string?)

Give one camera the viewport outright, or pass nil to clear it. While set, that camera IS the on-screen camera and priority is never consulted, so no authored priority can take the viewport from it — which is what makes an authoring camera safe to fly over a scene holding a camera at any priority. An override naming a camera that is despawned or disabled falls back to highest-priority-wins rather than blanking the screen.

Parameters

  • entityId string (optional) — Entity id of the camera to route the viewport to, or nil to clear.
camera.setEditorOverride(camera.editor())
camera.setEditorOverride(nil)

typed/builtin//modules/api/engine/camera/camera/viewData

camera.viewData(target: ((string | EntityRef)?)?) -> CameraView?

Camera render data. Called with no argument it is the active viewport camera's data for this frame: world position, which projection it drew and the field describing that frame, viewport pixel size, the 6 world-space frustum planes (the same inward-pointing, normalized planes the renderer culls with), and the view-projection matrix. The camera state a render feature needs for camera-relative work — LOD selection, frustum culling, billboards. Render features also get it as ctx.camera. Called with an entity id it is that camera's data, read from the frame's camera observation, and carries the identity the bare form has no room for: which camera it describes, which frame it was built for, what it rendered into, and the render layers it resolved to.

Parameters

  • target ((string | EntityRef)?) (optional) — Entity id, name, or proxy of the camera to read, or nil for the viewport camera.

Returns CameraView? — The camera view data, or nil when that camera drew no frame.

local c = camera.viewData(); if c then print(c.position.x, c.fovY) end
local minimap = camera.viewData("minimapCam"); print(minimap.output.target, minimap.viewport.w)
  • api
  • reference