Cinematics

Usage guide of game.cinematic: authoring a sequence (shots, cast, timeline, cues, acting tracks, spoken lines), settings and defaults, playing from the game, events and hooks, engines.

Source: module-kits:kits/game_cinematic/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs game-cinematicMCP tool: gripforge_docs { "topic": "game-cinematic" }

Cinematics and cutscenes (game.cinematic) #

Real-time introductions and cutscenes made of authored camera shots, with titles, captions, letterbox, fades, staged characters and props, animated actors, timed audio and spoken lines. Readable offline: gripforge docs game-cinematic, or the MCP tool gripforge_docs (topic game-cinematic).

What it is for #

A cinematic is data: a sequence of shots that the player of your game plays over the live world. The kit pauses the simulation while the sequence advances on presentation time, releases the input, hides the HUD, then restores the gameplay camera under a short fade and resumes. Escape, the visible Skip button and the gamepad B button skip; the reduced-motion preference gives static framings. A sequence that is empty or invalid never locks the controls.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "game.cinematic" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "game.cinematic", config: { "once": false, "letterbox": 0.1 } }
gripforge_game_project      { project_id: "gkp_…", action: "data", collection: "cinematics",
                              documents: [{ "id": "intro", "subtitle": "PROLOGUE", "shots": [
                                { "id": "reveal", "duration": 6, "space": "ground", "from": [-20, 18, -30], "to": [-12, 12, -22],
                                  "target": [0, 2, 0], "fov": 48, "endFov": 43, "title": "Your game", "caption": "Your story begins here." } ] }] }

Without an authored intro, the player plays a generated three-shot introduction after Play (or after the world loads when ui.menu has no title screen); it waits for model loading and an attached presentation. Authoring a sequence with the id intro replaces it. The kit requires game.engine. The full reference is gripforge kit api game.cinematic.

Settings and defaults #

KeyDefaultMeaning
introintroid of the sequence played automatically
autoplaytrueplay it after Play
oncetrueonce per saved run; play(id, true) replays
skippabletrueEscape, Skip button, gamepad B
letterbox0.115bar height, as a fraction of the screen
fadeIn, fadeOut0.7, 0.6seconds
handoff0.35seconds of the fade back to the gameplay camera
hideHudtruehide the HUD while playing
hidePlayerfalsehide the player character
groundClearance1.2metres kept above the terrain by ground- and player-relative cameras

A mod can change all of them and add, patch or remove documents of the cinematics collection.

The sequence #

Up to 16 shots, 60 seconds each, 600 seconds in all; shot ids are unique. Coordinates are in metres.

Field of a shotMeaning
id, duration
spaceworld (absolute XYZ), ground (altitude above world.ground), player (offsets rotated by the player's yaw at the start), actor (with of: "<cast id>": offsets rotated by the actor's heading, for an over-the-shoulder shot)
from, to, target, targetTocamera start, end, look-at point, optional moving look-at
fov, endFovlens
easingsmooth (default) or linear
title, captiontext shown during the shot
actorswhat changes when the shot begins: { id, at?, facing?, anim? }

World and actor coordinates are exact; only ground- and player-relative cameras are kept above the terrain. Keep shots inside the loaded region of the world: a camera does not stream distant places. An optional audio names an existing audio.core sound and stops only that voice when leaving.

Cast #

A sequence may carry up to 32 cast members, present only while it plays: { id, slot, variant?, at, facing?, space?, anim? } (a model of your game as bound, for example slot: "content", variant: "<archetype id>"), or { id, player: true, at } (a stand-in drawn with the player's model while the real player is hidden). kind: "prop" with size: [w, h, d] fits a model in a box of that size (at most 2000 m per axis); for a character, size fits the height with feet at at. A cast member can have parent: "earlier_cast_id": its poses then become local to that parent (an exhaust on a vehicle, a carried prop). Cast members are presentation entities without collision or gameplay, and are removed on every exit. A space exterior can sit in a separate staging area of the loaded scene and hard-cut to the real interior.

Animated timeline #

The optional timeline runs on presentation time, in seconds from the start of the sequence:

  • tracks: { actor, interpolation: "linear" | "smooth" | "cubic", keys: [{ time, at?, rotation?, scale? }] }, one track per actor, 2 to 128 keys, first time zero, strictly increasing. Rotation is degrees [pitch, yaw, roll] (shortest way per axis), scale from 0.001 to 100. The last key holds. Cubic positions can overshoot: check the clearance.
  • cues: { id, time, actor?, anim?, sound?, voice?, volume?, pitch?, stopAfter? }, up to 64, distinct ids. A clip cue restarts the actor's clip; sound names a bank entry of audio.core and the kit stops only its own voice (at stopAfter, on skip, end or cancel). During the pause, use a bank entry on the music or ui buses (or on voice with voiceDuringPause enabled); sfx and ambience stay muted. Every crossed cue fires cinematic.cue once.
  • shakes: [{ time, duration, amplitude: [x, y, z], frequency? }], deterministic decaying camera translation; suppressed by reduced motion.
  • poses, expressions, reaches (acting tracks, 2 to 128 keys each): poses rotate a named bone (bone, rotation degrees, weight, additive default true); expressions drive a named shape weight from 0 to 1; reaches bring a chain of three bones (upper arm, forearm, end effector) to a world target with an elbow pole. Bone and shape names belong to your model; a name alone creates no geometry. These are cosmetic: no forces, collisions or gameplay commands.

A speech cue { id, time, actor, voice: "<clip id>" } calls the dialogue.voice service on that staged actor (install dialogue.voice and audio.core, set voiceDuringPause: true, put the recording in the sound bank); it cannot be combined with sound, volume, pitch or stopAfter. Skip, cancel, end, scene exit and load stop cast speech.

Timeline sequences are supported by the native player and the Rust kit host; the web runtime in JavaScript rejects them instead of showing static actors.

{ "id": "departure",
  "cast": [{ "id": "vehicle", "slot": "content", "variant": "vehicle", "kind": "prop", "at": [0, 20, 0], "size": [8, 4, 12] }],
  "shots": [{ "id": "wide", "duration": 4, "from": [-20, 28, -25], "to": [-15, 25, -22], "target": [0, 20, 0] }],
  "timeline": { "tracks": [{ "actor": "vehicle", "interpolation": "cubic", "keys": [
      { "time": 0, "at": [-8, 20, 0], "rotation": [0, 0, -10] }, { "time": 2, "at": [0, 22, 6] , "rotation": [-5, 20, 18] },
      { "time": 4, "at": [12, 24, 18], "rotation": [0, 30, 0] }] }],
    "cues": [{ "id": "engine", "time": 0, "sound": "departure_engine", "stopAfter": 4 }] } }

Playing from your game #

HowWhat
Service game.cinematicattach(on?), play(id?, replay?), skip(), cancel(), active(), view(), camera(), sequences(), setReducedMotion(on)
Script verbs{ "play_cinematic": "arrival" } or { "type": "play_cinematic", "id": "arrival", "replay": true }; { "skip_cinematic": {} }
Variablescinematic.active, cinematic.id

Completed ids are saved; loading a save never replays the introduction. Active sequences are cancelled on load or scene exit so the player cannot stay stuck in a cutscene.

Events and hooks #

WhatNameDetail
Eventscinematic.started { id }, cinematic.shot { id, shot, index }, cinematic.cue { id, cue, time, actor, entity, audio }
Eventcinematic.finished { id, reason }emitted exactly once, reason is completed or skipped. Put required story changes here, so skipping still gives the intended final state
Eventcinematic.cancelled { id }load, scene exit or teardown; the sequence is not marked completed
Hookcinematic.start { id, replay }before a sequence starts; a tap may change id to another loaded sequence, or cancel (play returns false)
Hookcinematic.skip { id, shot, elapsed }before a skip; a cancel keeps it playing

cancel() is the safety exit and cannot be intercepted.

{ "id": "no_skip_finale", "hook": "cinematic.skip", "match": { "id": "finale" }, "cancel": true }

Engines #

The web and native players play it; the timeline, acting tracks and voiced cues run in the Rust kit host and are presented by the native player. Other engine presenters are not certified for them.

When it does not do what you expect #

SymptomCause and fix
The intro never playsautoplay: false, once: true and it already played in this save, or the sequence id differs from intro
A story change is missing after Skipit was attached to a shot or a cue: move it to cinematic.finished
An actor does not movethe sequence has no timeline track for it, or it runs in the JavaScript web runtime
A cue sound is silentit is on an sfx or ambience bus (muted during the pause): use music, ui or voice
The camera dips under the groundworld space ignores the clearance: use ground space, or raise the points

First-person awakening (1.7) #

Add wakeUp to a sequence to open curved eyelids over the live world, blink and gradually regain focus. Install the example below in cinematics, then call play("wake_up"). Set letterbox: 0 and fadeIn: 0. The player model is hidden automatically; camera positions do not move the gameplay character or switch its controller.

<!-- test: kits/game_engine/host/tests/game_cinematic.rs -->

{
  "id": "wake_up",
  "wakeUp": {
    "closedDuration": 0.4,
    "openDuration": 3.2,
    "focusDuration": 5,
    "blur": 0.018,
    "blinkAt": 1.5,
    "blinkDuration": 0.35
  },
  "shots": [
    {
      "id": "eyes_open",
      "duration": 7,
      "space": "player",
      "from": [0, 1.65, 0],
      "to": [0, 1.65, 0],
      "target": [0, 1.9, 6],
      "targetTo": [0, 1.65, 6],
      "fov": 70
    }
  ]
}

All durations are seconds. Opening and focus start after closedDuration; blur is a fraction of screen height (0–0.04, zero disables blur). Supply both blinkAt and blinkDuration, or neither. Opening, focus and blink must finish within the sequence. Adjust shots to your scene. A menu pause holds the effect; replay restarts it; skip, cancellation and handoff remove it. Reduced motion uses a simple fade without blur or blinking. reducedMotion() reads the preference; cinematic.motion { on } reports changes.

This effect is implemented in the shared Rust presenter and validated in Bevy WebGPU and WebGL2. Other engine presenters and the historical JavaScript exporter are not certified for it. The furnished infirmary is an independent example, not a dependency of the kit.