Blockbuster 767
README.md
game.camera.cam
The shared state of BLOCKBUSTER 767's camera, and the seam between the two things that have an opinion about it.
ChaseCamera is the only writer of the camera rig's transform. Director
never moves the camera — it writes an override pose here, and ChaseCamera blends
its own chase solution toward that pose by the override's weight. One writer,
one place to look when a frame is wrong.
Each frame runs in one direction:
| order | who | does |
|---|---|---|
| 10 | ChaseCamera | solves the chase pose → Cam.chase, blends in Cam.override, adds the impulses, writes the rig and the fov, publishes Cam.applied |
| 20 | Director | reads Cam.chase, decides which beat is running, writes Cam.override |
Because the director can read the live chase pose, a staged move that has to
LAND on the game camera — the opening flythrough — simply makes Cam.chase its
last key and arrives exactly where the chase already is, with no seam.
What is in here
The frame. Cam.chase (the pure chase solution, before staging),
Cam.applied (what actually reached the rig), Cam.ready.
The override. Cam.setOverride(pose, weight, snap?) every frame a beat is
running; Cam.clearOverride() to hand the frame back. snap means this is a
cut: the pose is taken whole rather than blended.
The impulses, which ChaseCamera decays on its own so a caller only ever has
to kick them: Cam.pushShake(amount) (0.05 a tap, 1.0 the airframe folding),
Cam.pushFov(degrees), Cam.pushDistance(metres), and Cam.idleAmp, the
multiplier on the slow handheld sway.
The toy's size. Cam.span is the plane's longest dimension, measured off
whatever the plane-art slice built. Every distance in this slice is a multiple
of it, so a bigger or smaller 767 re-frames itself instead of filling the shot
or vanishing in it.
The shape of the screen. Every fov in this slice is authored at the filmed 3:4 and carried to the player's screen by two rules:
Cam.fovForAspect(v)— a screen at least as wide as the filmed one keeps the vertical fov, so the composition down the tall axis is identical and the extra room arrives at the sides. A screen TALLER than filmed opens the lens partway back toward a constant horizontal view:Cam.reclaim(0.55) says how much of the lost width the lens takes.Cam.distanceScale()— the rest of that loss, answered by standing further off. At 9:16 the pair give a 66.5° lens and 1.15× the distance; at 3:4, 16:9 and 1:1 they are both identities.
Cam.aspect() asks the live viewport every frame, so a window resized
mid-round re-frames on the next one. Set Cam.aspectOverride to a width/height
ratio to make the whole rig frame as if the screen were that shape without
resizing anything — set it, read Cam.chase, render that station at the
matching image size, set it back to nil. Nothing in the game writes it.
Small maths every beat wants: lerp, smooth, bell, blend, pose,
and readPoint, which pulls a world point out of an event payload whichever
shape it arrived in.
Cam.ensureDirector() puts the Director component on the scene's bare
mount_director entity. ChaseCamera calls it as it wakes and keeps asking until
it takes; it answers false rather than raising while the scene has no mount or
the type has not registered yet.