Camera
Manages viewport priority, render-to-texture, and capture. State is stored in the native `Camera` ECS component; the Rust camera system handles render scheduling and render targets.
Camera
Manages viewport priority, render-to-texture, and capture. State is stored in the native Camera ECS component; the Rust camera system handles render scheduling and render targets.
Public fields: fov, near, far, priority, textureHandle (the guid of the texture the camera renders into; empty = main viewport), renderLayers (which render layers this camera draws — a space-separated spec of names, e.g. "all", "all !ui", or "default sky"; ui/sky/debug/EditorUI are built-in layers), postProcessing (whether this camera runs the post-process chain), debugChannel, plus the behavior slot below.
Methods: :lookAt(target) (entity id string OR {x, y, z} table), :render(), :capture(), :setTargetTexture(tex?) (a renderer.texture.create handle to render into, or nil for the viewport).
entity(id).component.add("Camera", { fov = 90, priority = 10 })
entity(id).component.get("Camera"):capture()
The scene's play-mode camera is reached as layers.active.camera, a handle that
reads and writes the fields above on whichever entity currently carries them.
layers.active.camera.entity is the entity ref for that camera and
layers.active.camera.entityId its id string, so a script that needs to attach
something to the camera — an AudioListener, a child entity — goes through the
ref:
local cam = layers.active.camera
cam.fov = 70 -- the camera's settings
cam.entity.component.add("AudioListener") -- the entity carrying them
How the camera moves: behavior and follow
A Camera does not move itself. behavior names a component that does, and setting it attaches that component to this entity. Clearing it detaches whatever was attached.
local cam = entity(id).component.get("Camera")
cam.behavior = asset.ref("@builtin::controller.orbital_follow", "component")
cam.follow = playerBody
follow is the standard slot every shipped behavior reads. Set the follow target on the Camera, not on the behavior, so swapping behaviors keeps it. A behavior that finds its own follow field empty falls back to this one, which is what lets a rig keep tracking the player across a behavior swap.
followResolves answers whether that slot names an entity that is live — true while it names a live one or names nothing at all, false once the target is despawned or the id names no entity. A rig whose target does not resolve holds its last pose, and this is the field that tells it from a rig posed correctly on a subject that has not moved. camera.get carries the same value beside follow, and a write naming an id with no entity behind it draws a warning where it lands.
cam.followResolves -- false once the followed entity is gone
tools.use("camera", "get", id).followResolves -- the same answer off the tool
The shipped behaviors live under @builtin::controller.*: orbital_follow, third_person_follow, first_person, free, orbit, chase, isometric, rts, birds_eye, side_scroller, cinematic, menu.
Writing your own
Any component can be a camera behavior. Write one that moves its own entity and attach it the same way:
cam.behavior = asset.ref("MyChaseCam", "component")
follow lives on the Camera, so a behavior gets no property notification of its own when the target changes. Declare onFollowChanged(newFollow, oldFollow) to be told the moment it does — the Camera calls it on the component it attached, which is what lets a rig re-pose on the new subject at once. A behavior that reads Camera.follow on its own schedule declares nothing and is attached the same way.
typed function public:onFollowChanged(newFollow: any, oldFollow: any)
-- pose this entity against the new target
end
To make it appear in the discovery catalog alongside the shipped ones, declare the cameraBehavior tag in the component's .metadata:
{ "tags": ["cameraBehavior"] }
The tag governs discovery, not attachment. A tagged component is listed by layers.active.camera.behaviors and is what tooling offers when something asks "which camera behaviors exist"; an untagged component attaches just as well and simply stays out of that list. Tag the ones you want other people (and agents) to find.
for name, ref in pairs(layers.active.camera.behaviors) do
print(name, ref.identity)
end
Texture colour space
textureColorSpace = "display" applies the display transform when rendering into a texture. "linear" writes scene-linear values instead. postProcessing independently controls the effects chain in either mode. Floating-point targets retain values above one; normalized targets clamp to their representable range. The main viewport uses display encoding.
Scoped to this part · feeds back into the world's score.