game.camera.director
The one thing that decides where the camera is.
local Director = require("game.camera.director")
Director.play("opening", { onEnd = function() ... end })
Director.kick(1.2) -- a course seated badly
Director.punch(2.0) -- a perfect seat: a snap on the lens
Director.shake(1.05, 1.35) -- the tower went
local p = Director.pose() -- what the rig should be this frame
TowerCamera applies what this hands back and Staging tells it what to play.
Neither of them works out a pose. Everything that can move the view — the
follow, a staged beat, the jolt when a course seats, the tremor on a tocsin
beat, the shake when the tower goes, the breath under all of it — is summed
here, in one place, so two of them can never fight over the same entity.
One clock, however many rigs
The camera rig is OwnerOnly on the player prototype, so every peer has one of
their own and the editor sees the prototype's. All of them apply the SAME pose
— a round is local to a peer and they are all watching one tower — but the
clock must only advance once a frame, or a beat runs at N times speed with N
players in the world. The first rig to register is the primary and is the only
one that ticks; the rest read. Staging ticks it instead when no rig is alive
at all, so a scene being authored with no player in it still finishes its
beats.
The same care applies to a single rig: an editing session with the simulation
running calls both update and editorUpdate on the same frame, so update
owns the clock wherever it runs and editorUpdate only takes over once a
couple of frames have gone by without one.
Holds
ring, won and the two losses hold their last frame rather than handing the
camera back — the ring push-in has to stay on the bell while the player waits
for the top of the swing, and an end card wants a composed frame behind it. A
held beat keeps a slow drift, a few degrees of orbit and a couple of degrees of
lens over six seconds, so the frame is alive without being a move.
Freezing a frame
Director.freeze("ring", 0.72) -- park exactly there
-- ... capture ...
Director.release()
freeze parks the camera on one frame of a beat and leaves it: the clock does
not run and the held frame does not drift, so the pose is exactly
Shots.eval(name, t, ctx) for as long as it is held. It is what makes a
capture of a moment inside a move repeatable, instead of a race against the
seconds between asking for the shot and the shutter. Authoring only — nothing
in a round calls it.
Reading it
Director.snapshot() reports the beat and its clock, whether it is holding,
how many rigs are registered, the damped top course, and the whole solved frame
— aspect, lens, distance, aim, and how much of each slide axis reads as lateral
movement at the current bearing.
Scoped to this part · feeds back into the world's score.