follow
The solver behind the chase camera of *First Light: Goura*. `FloorCam.component` owns the state and the fields; this module owns the maths.
game.staging.follow
The solver behind the chase camera of First Light: Goura. FloorCam.component
owns the state and the fields; this module owns the maths.
local Follow = require("game.staging.follow")
local cam = Follow.new()
Follow.advance(cam, tuning, player.state(), dt)
local pose = Follow.solve(cam, tuning, subject, dt)
What it solves
The camera stands a fixed distance behind and above Tawpy — those are
metres and they do not change with the screen. What does change with the
screen is where it aims: the pitch is solved every frame so she lands at
subjectScreenY down the frame under the live field of view.
That is the whole reason the framing survives a change of shape. A wide screen
gets a shorter vertical fov (Follow.fov() blends FL.CAMERA.fovTall at 3:4
into FL.CAMERA.fovWide at 16:10 and holds outside that range), and a shorter
fov with a fixed aim point would slide her down and off the bottom. Solving the
pitch from the fraction instead keeps her in the lower third at every aspect,
and hands whatever room is left over to the lane ahead.
Follow.fov() reads ui.screenSize() through FL.screen(), so it answers to
the live viewport rather than to the one this was authored on.
What it feels like
| Behaviour | How |
|---|---|
| Lead | The aim leans lateralLead metres the way she is heading, smoothed. |
| Lag | Sideways the camera chases at lateralChase, up the lane at chase. The difference is what makes a side-step read. |
| Breath | While she gathers a flurry the rig eases in and the lens tightens; the release throws it back and opens up. |
| Push | A boom dollies in and narrows the lens, then springs home. |
| Drift | A hand-held sway proportional to her speed, small enough to feel and not to notice. |
The breath and the push are three critically-damped springs — a dolly, a lift
and a lens — plus a per-state bias the springs sit on. Follow.impulse(st, kind, power) kicks them; the kinds are boom, flurry, step, seen, answered
and heartbeat.
State
Follow.new() returns the state table; the module holds none of its own, so a
hot reload of this file costs nothing. Follow.unseat(st) forgets where the
camera was, so the next solve seats it outright — what a beat or a cutscene
calls when it hands the view back, instead of gliding in from wherever the
cutscene left the rig.
Scoped to this part · feeds back into the world's score.