Picking a camera
This page continues Packages and kits. It describes how a game picks the camera it plays through.
A scene picks its camera by one name, and every camera, the engine’s or a kit’s, registers under a name, so a new camera is one registration and an agent lists and picks cameras from the scene file, the CLI and the MCP.
Today a scene mounts one of three components: <Camera> with a CameraPreset of Default, Shooter or Classic; <SideCamera>; or <ChaseCamera>, behind the player or along a track. Each repeats two checks: headless it draws nothing, and while the devtools fly it draws <FreeCamera>, the devtools’ fly camera, instead. An agent learns which cameras exist only from the camera page, and nothing at runtime says which camera a scene draws or lets an agent switch it.
The registry
Section titled “The registry”A camera is a component that mounts controls or a lens with makeDefault, built on useCamera where it orbits. The following list describes the mechanism:
- Registering.
registerCamera(name, { component, description })names a camera and returns theDisposerthat forgets it, asregisterAvatarandregisterMapdo. The engine registers its own cameras beside their components; a kit or a game registers its own in the file that defines it. - Picking. A scene mounts
<Camera type="side" />.Camerabecomes the one camera element: it finds the registration, passes its other props to that camera’s component, draws nothing headless, and draws the fly camera while the devtools fly, once for every camera. With notypeit draws the default orbit, so the scene template’s<Camera follow={CameraTarget.ViewTarget} />keeps working. A type nobody registered throws, and the message names the registered ones. - Props. Each type’s props are typed through one interface that a registration augments, as React Three Fiber types its elements through
ThreeElements, so<Camera type="side" azimuth={0} />typechecks and<Camera type="side" track={track} />does not. - The engine’s names. A string enum,
CameraType, holds the engine’s own:Default,ShooterandClassic, which replaceCameraPreset, plusSide,ChaseandFly. A kit’s or a game’s name is a plain string beside them.SideCamera,ChaseCameraandFreeCamerastop being exports, so an agent meets one way to pick a camera;useCamerastays the rig a game’s own camera hangs on. - Listing.
registeredCamerasis aReadonlyMapfrom name to registration, asregisteredBehavioursis. The devtools store holds the type the page draws, so a dump and the MCP’sdescribe_entityread it. - Switching. A scene switches camera mid-scene by rendering another type, such as a cutscene’s, from its own state. The switch is a cut. A blend between two cameras waits for a game that asks for one.
The CLI and the MCP pick against a running game, because a kit’s or a game’s cameras register in the game’s own code:
spawnite play cameralists the running page’s cameras with each one’s name and description, andspawnite play camera <type>switches the page to that camera for a look, as the devtools’ fly switch does, without changing the scene’s file.- The MCP’s
list_camerasandpick_camerado the same through the playtest. - To change the camera a scene ships, an agent edits its
typeprop, one word in the scene file.
The devtools’ Focus test runs over registeredCameras, so a new camera joins that test with its registration.
The chase camera
Section titled “The chase camera”The chase camera that follows the player’s facing stays engine code, registered as chase, because every third-person game can use it. The one along a track, TrackChaseCamera with aimAlongTrack, findTrackRider and trackCameraSettings, moves to the ride kit, which registers it as ride, and ChaseCamera loses its track prop. Its framing is tuned to sled’s ride, which the next racer rewrites as it rewrites the mover. It is also the first camera the engine does not ship, so it proves that a kit’s camera is one registration.
Where a rider stands
Section titled “Where a rider stands”The track trigger stays engine code and tests a rider by its track coordinates, which today sit on the mover’s trait beside its speed, steer and tuning. So the engine keeps the coordinates as a trait of their own: the metres along the centreline, across it and above it, beside TrackRefTrait. The kit’s mover writes them each step; the trigger, the AI tree and the ride camera read them. The engine’s step stops copying the player’s input into the mover, and the kit’s mover reads the step’s input in its own system at the step’s input place.
For the trait: TrackPosition, recommended, TrackCoordinates or TrackFollow. Unreal keeps a distance along a USplineComponent in the actor that follows it, Unity’s SplineAnimate holds a NormalizedTime, and Godot’s PathFollow3D holds progress, h_offset and v_offset, the closest match.
The names
Section titled “The names”The following table lists each new export’s candidates, the recommended one in bold, and what Roblox, Unreal, Unity and Godot call it:
| Export | Candidates | Roblox | Unreal | Unity | Godot |
|---|---|---|---|---|---|
| The registration | registerCamera, defineCamera, addCameraType |
None: a camera module the PlayerModule requires |
A UCameraComponent on an actor, Lyra’s ULyraCameraMode |
A CinemachineCamera placed in the scene |
A Camera3D node in a scene |
| The pick | <Camera type>, <Camera kind>, <Camera mode> |
Camera.CameraType |
SetViewTargetWithBlend |
The highest Priority, which the brain takes |
Camera3D.current, make_current() |
| The engine’s names | CameraType, CameraKind, CameraName |
Enum.CameraType |
None: a class | None: a component type | None: a node |
| The list | registeredCameras, listCameras(), cameraTypes |
Enum.CameraType:GetEnumItems() |
None | CinemachineCore counts the active cameras |
None |
| The CLI command | spawnite play camera [type], spawnite camera, spawnite play view |
None | The console’s ToggleDebugCamera |
None | The editor’s camera override |
| The MCP tools | list_cameras, pick_camera, set_camera, camera |
None | None | None | None |
type follows Roblox’s CameraType, the name a model already writes for choosing a camera, and registerCamera follows the engine’s other registries.
What the others do
Section titled “What the others do”- Roblox gives the workspace one
CurrentCameraand picks its behaviour withCameraTypeand what it follows withCameraSubject.CameraTypeis a closed enum; a custom camera setsScriptableand moves the camera each frame. - Unity with Cinemachine places a virtual camera per shot, and the brain on the real camera draws the one with the highest priority and blends between them. Without Cinemachine, a game enables one
Camera. - Unreal draws the player controller’s view target, set with
SetViewTargetWithBlend, through thePlayerCameraManager; Lyra stacks camera modes on it. - Godot draws the viewport’s current
Camera3D, set withcurrentormake_current(). - PlayCanvas renders every enabled camera component in
priorityorder. - Phaser keeps a camera manager per scene,
this.cameras, withmainand any cameras a scene adds.
This platform matches Roblox and Godot: one camera draws, and a scene names it. It differs in two ways, for the agent. The set of cameras is open, because a kit and a game add theirs, where Roblox’s enum is closed. The pick is a name, not a priority, because a name is one value an agent reads and writes, and a priority is an order it has to reason about across every camera in the scene.