camera
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
entityIdstring(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
entityIdstring?(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
namestringposition(vec3 | number)(optional)optsCameraCreateOpts(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)targetEntityRef | stringoptsCameraFrameOpts(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
cameraLookAtRef(optional)targetLookAtRef(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)paramsCameraSetParams(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
entityIdstring(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)