Document the game pause and time scale nodes (#167)

- Concepts: new section "The game pauses itself" and the "Run while
  paused" row.
- Behaviors: the five new nodes in the node list.
- Scenes: what Load scene and Restart do with pause and time scale.
- Architecture: the second pause next to the toolbar pause.
- Development: check total 1217.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
adminandClaude Opus 5 committed 2026-09-21 10:27:17 +02:00
1 parent 1d530fe900
commit 396314f38d
5 files changed
+37 -6

No files matched your search

+9
@@ -264,6 +264,15 @@ its sounds at normal speed — a tape being slowed is a sound effect of its own,
"half speed" — so the audio device is left alone and only what the world *does* is scaled.
A pause is the exception, because a voice playing over a still image is nobody's idea of paused:
the audio context is suspended and resumes mid-note.
**The game's own pause is a flag on the world, not a second clock** (#167). `Pause game` sets
`World.paused`; `World.tick` reads it once per frame (`frozen`), and a frozen frame neither moves
the world time nor ticks a system unless it says `runsWhilePaused` — only the `BehaviorSystem`
does, and it then runs just the instances whose object has `runWhilePaused` on, without collision
and animation events. The editor holds the global clock for such a frame too, so particles and
shaders freeze with the world, and hands the behaviors the step the clock *would* have taken
(`PlaySession.tick(step, awake)`). `Set time scale` writes `GameClock.scale` through the clock a
played game gives the `BehaviorSystem` (`GameSystemOptions.clock`); the editor run gets none,
which is how it ignores the node. The sounds stay out of it: a pause menu may have its music.
**Everything the graph does to the outside world goes through the context, audio included.**
`NodeContext.playSound` takes an asset id, not a file: resolving it — and folding the asset's own
+1
@@ -119,6 +119,7 @@ shown on the node and editable in the inspector.
direction; every play is a voice of its own, so two in a row overlap, and the voice ends with the
behavior that started it unless `detached` says otherwise),
`Load scene` (swaps the running scene once the frame is over — see [Scenes](Scenes)), `Log`, `Stop game`, `Restart game`,
`Pause game`, `Resume game`, `Set time scale` with the data nodes `Is game paused` and `Time scale` (see [Concepts](Concepts), *The game pauses itself*),
`Apply force`, `Apply impulse`, `Set velocity`, `Set angular velocity`, `Set gravity` (see [Physics](Physics)),
`Raycast` (what a ray meets first on the layers it looks at: object, collider, point, normal, distance — see [Colliders](Colliders#raycast)),
`Raycast all` (everything on that line instead, as parallel lists sorted by distance, with a `count` and a `max hits` — see [Colliders](Colliders#raycast-all)),
+21
@@ -16,6 +16,7 @@
| **Behavior** | Logic as an asset: a **node graph** plus the **variables** it declares. Placed like anything else (a diamond symbol in the scene); its parent is the **owner** the graph acts on. |
| **Variables asset** | Declarations and nothing else — no graph, no position, never placed. A `Variables` node naming it reads and writes **one value per key for the whole run**, however many graphs point at it. The values start at the declared ones on every Play and are never written back, so playing cannot change the project; while a run is on the asset's inspector shows them live and an edit there reaches the run at once. Two such assets may declare the same name without meeting. See [Behaviors](Behaviors). |
| **Run in editor** | A switch only behaviors have (default off). With it on the behavior keeps running while the project is **not** playing — and unlike Play it then edits the *saved* model: what it moves or writes is a real change, autosaved and not on the undo stack. See *Running without Play*. |
| **Run while paused** | The other switch only behaviors have (default off). While a graph has paused the game (`Pause game`), only behaviors with it on are ticked — the pause menu, which is also the only thing that can resume. See *The game pauses itself*. |
| **Collider** | A shape (box or circle) for collision detection, placed as a **child** of the object it belongs to (its *body*). Drawn as a green outline in edit mode only. |
| **Collision layer** | One of up to 16 named layers of the project (**menu → Project settings…**). A collider or tilemap names the layer it is on and the layers it **collides with**; two of them meet only when each one names the layer of the other. A new project has one layer, *Default*, which every collider is on and meets — so nothing collides differently until a layer is named. See [Colliders](Colliders#collision-layers). |
| **Rigidbody** | Placed as a **child** of an object, it hands that object to the physics engine while playing: it falls with the scene's gravity, bounces and rests on colliders, pushed by what it meets. At most one per object. See [Physics](Physics). |
@@ -138,6 +139,26 @@ shaders and the Animation tab's preview stand still with the world rather than c
it. That clock starts a run at second 0 and goes back to 0 when the run ends, which is why the
scene view looks freshly loaded after Stop.
**The game pauses itself.** The toolbar's hold is the editor's: nothing runs under it, not even a
graph. A game that wants a pause menu, a slow motion or a frozen moment during a dialog does it
from its own graphs (#167):
| Node | What it does |
| --- | --- |
| `Pause game` | From the next frame on the world time, physics, animations, particles and shader time stand still, and only behaviors with **Run while paused** are ticked. They get their keys, clicks, timers and `On update` as usual; collisions are not reported, because nothing moves that could collide. Sounds play on. |
| `Resume game` | Lets it run on from where it stood, from the next frame. |
| `Is game paused` | True while it is paused, so one key can do both (`Is game paused` → `Branch` → `Resume game` / `Pause game`). |
| `Set time scale` | How fast the game runs: 1 is real time, 0.5 slow motion, 2 twice as fast. Movement, physics, animations, particles and shader time follow it. At 0 everything is frozen, but the graphs keep running with a `dt` of 0 — that is the difference to a pause. |
| `Time scale` | The scale as it was last set. |
The two are kept apart on purpose: a pause stops the game but not its menu, a time scale of 0
stops time but no graph. **A pause belongs to the scene**, so a `Load scene` from a pause menu
arrives in an unpaused level, and so does a `Restart game`. **The time scale belongs to the run**:
it carries over a `Load scene`, and Play and Stop set it back to 1 — also for the editor's
previews, which share the clock. The toolbar's hold works on top of either one and leaves both as
they are. In the editor run both are ignored with a line in the console: an edited scene that stops
for no visible reason, or an editor whose previews crawl, would help nobody.
**Running without Play.** A behavior switched to **Run in editor** goes on running while the
project is not playing (`src/editor/EditorRun.ts`, ticked by the Editor next to the play session).
The switch is an ordinary property, so it can be set on the behavior asset and overridden per
+1 -1
@@ -13,7 +13,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
php tools/make-user.php <name> <password> # an account; every tool signs in (see Accounts)
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1210 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1217 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
+5 -5
@@ -82,8 +82,8 @@ are looking at, brought to life; swapping it would mean editing a scene you are
## Restart, stop, load — what each one keeps
| | Objects | Shared variables | Global machines | Clock | Sounds |
| --- | --- | --- | --- | --- | --- |
| **Load scene** | rebuilt | kept | kept | keeps running | end with their behavior |
| **Restart game** | rebuilt | back to declared | back to their start state | back to 0 | all end |
| **Stop** (`Esc`) | gone | discarded | discarded | back to 0 | all end |
| | Objects | Shared variables | Global machines | Clock | Time scale | Pause game | Sounds |
| --- | --- | --- | --- | --- | --- | --- | --- |
| **Load scene** | rebuilt | kept | kept | keeps running | kept | lifted | end with their behavior |
| **Restart game** | rebuilt | back to declared | back to their start state | back to 0 | back to 1 | lifted | all end |
| **Stop** (`Esc`) | gone | discarded | discarded | back to 0 | back to 1 | lifted | all end |