The play loop
Every Patterplay runtime (JavaScript, Unity, Unreal, Godot) works the same way. Learn the shape here once; each engine’s quickstart is then mostly install notes and local naming.
Engine and flow
Section titled “Engine and flow”- An Engine is built from a loaded bundle. It holds the shared
story state (your
@patterand@sceneproperties, visit counts) and it’s what you save and load. - A Flow is one position cursor walking through the story. You open a flow at a starting address (a scene, optionally a block); most games run one flow, but you can run several (a main thread plus a side conversation) off the same engine.
engine = Engine(bundle) // shared stateflow = engine.openFlow("main", scene) // a cursor at a starting pointThe method names differ slightly per engine (openFlow / OpenFlow / open_flow) but the
idea is identical.
Advancing, one step at a time
Section titled “Advancing, one step at a time”You drive the flow by asking for the next step and rendering it. A step is one of five kinds:
| Step | Meaning | What it carries |
|---|---|---|
| line | A character speaks | the speaker, the (localised) display name, the text, direction, Game Data, tags |
| text | Narration the player reads | the text, Game Data, tags |
| gameEvent | A host-facing cue, no spoken text | an id + Game Data (play a sound, move the camera) |
| choice | The player must pick | a list of options (each with prompt text + an eligible flag) |
| end | The flow finished | : |
A minimal loop: advance until a choice or the end, rendering each beat as it comes.
loop: step = flow.advance() switch step.type: "line": show(step.characterName ?? step.character, step.text) "text": show(step.text) "gameEvent": doHostCue(step.id, step.gameData) "choice": presentOptions(step.options); break // wait for the player "end": finish(); breakWhen the player picks, call choose with the option’s id, then resume advancing:
flow.choose(optionId)// ...back to the loopOptions that fail their condition come back ineligible rather than missing (so you can grey
them out), unless the author marked them to hide entirely. That eligible flag is yours to
render however suits your UI.
Reading and writing state
Section titled “Reading and writing state”The engine exposes the story’s properties by reference:
engine.getProperty("@gold") // read @patter / @scene stateengine.setProperty("@gold", 10) // write it (e.g. from a shop your game runs)Your game also supplies the @world values the story reads (threat level, location). If you
don’t bind one, the runtime falls back to the value the project declared as its default. The
details (including reading typed Game Data and tags off each step) are in
Save/load & Game Data.
Save and load
Section titled “Save and load”The whole run: every flow’s position, the shared state, visit counts, even the seeded random generator’s place in its sequence: serialises in one call and restores in one call. Every runtime handles save/load the same way, so a save made by one engine round-trips exactly. See your engine’s quickstart for the local call and Save/load & Game Data for the shape.
Localisation
Section titled “Localisation”In Embedded mode the text on each step is already resolved to the current language, and you can switch language live. In IDs-only mode the step carries ids instead of text, and you resolve them through your own localisation system. The runtime API is the same either way; only where the words come from changes. → Localisation
- Get it running on your engine: JavaScript · Unity · Unreal · Godot.
- The deep JavaScript API: The Engine API.
- Save/load, Game Data, tags, host events: Save/load & Game Data.
MIT-licensed open source · Made by Ian Thomas · patterkit.com