framing
`Framing.solve(width, height)` — the climb's follow camera, solved from the live screen rather than tuned for one.
game.rules.framing
Framing.solve(width, height) — the climb's follow camera, solved from
the live screen rather than tuned for one.
local Framing = require("game.rules.framing")
local s = ui.screenSize()
local f = Framing.solve(s.width, s.height)
-- f.fov, f.cameraDistance, f.shoulderX / shoulderY / shoulderZ,
-- f.aimPitch, f.minPitch, f.maxPitch, f.verticalArmLength, f.sensitivity
Two rules do the work.
The field of view is chosen from how much of the world has to fit
across the frame. A camera's fov is vertical, so a tall screen showing
the same vertical angle as a wide one sees far less to the sides; on 3:4
the spire's own width would fill the frame and the way ahead would be a
wall. Solving the vertical angle backwards from TARGET_HFOV keeps the
climb readable sideways and hands the tall screen the extra vertical reach
as a bonus — the lava below and the rim above in one picture.
The pilgrim's place in the frame is a fraction of the frame. The rig's
shoulderOffset.y raises the point the camera aims at, which lowers the
subject on screen; how far depends on how much world the frame covers, so
the offset is computed from the visible height at the follow distance. The
pilgrim then sits at SUBJECT_HEIGHT_FRACTION on every screen.
| Screen | vfov | hfov | distance | aim height |
|---|---|---|---|---|
| 1080x1440 (the filmed 3:4) | 74.0 | 58.9 | 4.75 | 2.17 |
| 1920x1080 | 44.0 | 71.4 | 5.88 | 1.92 |
| 2400x1080 | 44.0 | 83.8 | 5.90 | 1.93 |
| 1080x2400 | 74.0 | 37.5 | 4.70 | 2.16 |
| 1440x1440 | 62.0 | 62.0 | 5.03 | 2.05 |
MAX_VFOV binds on a very tall phone, where the horizontal angle narrows
rather than the lens bending — a deliberate trade for a game whose subject
is what is above and below you.
game.rules.Rules installs the result on the local player's camera and
re-solves whenever the screen changes shape.
Scoped to this part · feeds back into the world's score.