diff --git a/Architecture.md b/Architecture.md index a4beffb..8f5b911 100644 --- a/Architecture.md +++ b/Architecture.md @@ -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 diff --git a/Behaviors.md b/Behaviors.md index 9a152b7..56dfbf1 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -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)), diff --git a/Concepts.md b/Concepts.md index 006ede5..453ad06 100644 --- a/Concepts.md +++ b/Concepts.md @@ -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 diff --git a/Development.md b/Development.md index 63a6ae4..2bb3dbe 100644 --- a/Development.md +++ b/Development.md @@ -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 # 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 diff --git a/Scenes.md b/Scenes.md index d288bee..8e5561f 100644 --- a/Scenes.md +++ b/Scenes.md @@ -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 |