2D: the 2.5D cinematic camera
A 2D game can turn 3D for its big moments: a boss intro, a reveal, a finale. The flat view swings into a real perspective camera that pushes in, orbits, dollies or tilts round a target, with depth of field, a light change and letterbox bars, then lands back exactly on the 2D view and play carries on.
How it works
- Every layer becomes a depth plane. A backdrop that scrolls at
depths of the world's speed sits D (1/s - 1) behind the play plane (D: the distance at which a 30 degree camera seesview_height), so the parallax the game already has becomes real depth. Tilemaps, sprites and 2D particles stay on the play plane, a hair apart by layer (layer_depth). A shot'sdepthsmoves single objects nearer or further. - Sprites are cards: with
cards "face"(default) they turn to face the camera,"flat"keeps them in their plane. - The swing starts and ends on the 2D picture exactly (the lift is built so the 2D view's own pose sees every point where the 2D frame drew it), so there is no pop in or out.
- Depth of field uses blurred copies of the art (made in the background; level 1 to 6), crossfaded by how far a plane is from the target. Big planes are drawn in pieces so textures stay put in perspective.
- The result is still an ordinary 2D frame, so the Metal and OpenGL players draw it the same way.
- While a shot plays the player's controls are held (
hold: falselets play run).
Using it
Scripts:
game.cinematic25d({ target: "Goblin_King", move: "boss_intro", duration: 4.5, dof: 0.7, light: "dramatic" }) // -> { ok, reason }; every script hears onMessage 'cinematic25d_finished'; game.cinematic25dPlaying
The kit kits/cinematic25d.js does it when the hero comes near: put it on the boss (or in its script's also) with params cine_move, cine_duration, cine_range, cine_amount, cine_dof, cine_light, cine_once.
Sequences (Sequences/<name>.sequence.json) take a cinematic25d list; nested shots bring theirs in shifted:
"cinematic25d": [ { "t": 1.0, "target": "Goblin_King", "move": "orbit", "duration": 3.5, "amount": 40 } ]
Agents: add_cinematic25d (into a sequence, play: true in the running game, preview: n for stills across the shot). scrub_sequence shows a sequence's 2.5D shot at any time.
Shot fields
| field | meaning (default) |
|---|---|
target | object framed, orbited and focused (the screen's middle) |
move | push_in, orbit, dolly, tilt, reveal, boss_intro, hold (orbit) |
duration | whole shot in seconds, blends included (3.5) |
blend_in, blend_out | seconds to swing in and back (a quarter of the shot, at most 1.1) |
amount | move strength: degrees, or a share of the view for dolly/push_in |
distance | how close, as a share of the 2D view's distance (about 0.5) |
fov | field of view in the shot (34) |
dof | depth of field 0...1 (0.6) |
light | dramatic, warm, cold, dark, none (dramatic) |
letterbox | bar height each side as a share of the screen (0.09) |
cards | face or flat (face) |
hold | hold the player's controls (true) |
layer_depth, depths | depth between layers on the play plane; per-object depth overrides |
Limits
- Painter's order stays the 2D order (right for planes stacked toward the camera); a
depthsoverride can't put an object behind something drawn after it. - Shots fit the camera's level bounds or active room, including on a
stage_25d. Near an edge, the camera shifts inward; if the view is wider than the room, it moves closer. Both the 3D stage and its sprites use that same view. Bounds constrain the play plane, so backdrop art still needs to cover its authored depth plane. - Depth of field on sprite sheets and tilesets is capped at a small blur (neighbouring frames would bleed in).