Document Keep on scene load (#165)
- Scenes: new section on the switch, what travels, the duplicate rules and what does not travel. Load scene builds a scene as it is stored. - Architecture: how a kept entity moves into the next world, and that a run keeps what each scene stored before it built it. - Concepts and Sounds: one sentence each on kept objects. - Development: the e2e sum is 1625.
1 parent
412b94a064
commit
b2d413bd60
5 files changed
+86
-11
No files matched your search
+25
@@ -458,6 +458,31 @@ the `SharedState` handed to the next `BehaviorSystem` through `carryOver` — wh
|
||||
line between "belongs to the run" and "belongs to the scene". Carrying it over also suppresses the
|
||||
entry event of the global machines, because standing where you already stand is not a move.
|
||||
|
||||
**A kept object is the same entity in the next world (#165).** `GameRun.loadScene` does it in four
|
||||
steps, and the rules are pure functions in `runtime/keptObjects.ts`. `keptEntities` picks the
|
||||
top-level entities with the switch on and everything below them. `objectsToBring` copies their
|
||||
stored objects, parented as the run has them — a released game never writes the run back, so the
|
||||
scene's own `parentId` may be stale. `bringObjects` puts the copies into the next scene and takes
|
||||
out what they stand in for (same id, or a kept top-level object of the same asset). Then
|
||||
`World.instantiate` is handed the entities: when the scene's object for one comes up, the world
|
||||
takes the entity as it is instead of making one, and only notes what the scene says of it — so the
|
||||
next `syncToModel` writes the run's values into the scene rather than the scene's replacing them.
|
||||
The ids the scene left out answer through `findByObjectId` as the entity standing in.
|
||||
|
||||
What is not on the entity is handed over next to it, as one `KeptState`:
|
||||
`BehaviorSystem.release` takes the running instances out before the old world is destroyed — which
|
||||
is all it takes for them to hear no `On destroy` and keep their sounds — together with the graph
|
||||
index their interpreters read and, from the physics, how their dynamic bodies move. The next
|
||||
system `adopt`s them before it starts, so they are no new instances and hear no `On start`; their
|
||||
trace is cleared and timed by the new world, which starts at second zero. `PhysicsSystem.resume`
|
||||
gives each body the velocity when it is made, and forgets the rest after the first sync.
|
||||
|
||||
**A run keeps what each scene stored before it built it.** `GameRun` copies a scene's objects and
|
||||
groups the first time it builds it, and puts them back before it builds it again: the run writes
|
||||
into the scene it is in (`syncToModel`, baking, `spawn`), and a level loaded twice must not start
|
||||
from what was left of it. `GameRun.end` puts every such scene back, which the player calls on stop
|
||||
because it plays the project itself; the editor's Play runs on a copy it throws away.
|
||||
|
||||
**Where a run begins and which of the two questions Play asks are both project fields.**
|
||||
`project.startSceneId` is content — it travels with the project, and a dangling id reads as none
|
||||
rather than being cleaned up, so undoing a deletion brings it back. `project.playFrom` (the switch)
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
| **Instance** | An object resolved from a placed container's template. Its **values** can be edited — position, name, property overrides — and only what actually deviates is stored, as a sparse `instanceEdits` entry on the container object keyed by the template path. Everything else keeps following the template. Its **structure** cannot: it can not be removed, re-parented or given a collider, and nothing of the scene can be parented into it. ↺ ▾ resets to the nearest base, which inside a container is the **template as the containers in between leave it**; ↑ ▾ offers **Apply to container** (write the value into the template, where every other instance still at the old value picks it up) and/or **Apply to asset** (which drops the template's override as well). With one base left the menu collapses to a plain ↺ / ↑, with none the buttons disappear. Gold marks the same thing the buttons offer. |
|
||||
| **Unpack** | Writes a placed container's content into the scene as ordinary objects (hierarchy context menu, or the inspector's *Content* section). The ids survive it, so the selection does; from then on the structure is editable and the objects no longer follow the template. |
|
||||
| **Start scene** | The scene a run *begins* with, named on the project rather than on any asset (right-click a scene in the Assets panel → *Set as start scene*, `▶` marks it in the list; *Clear start scene* takes it back). It is a different question from which scene is **active**, which is the one you have open in the editor — the switch next to Play says which of the two Play asks. Without a start scene named, both are the same thing. See [Scenes](Scenes). |
|
||||
| **Load scene** | The node that swaps the running scene for another one, inside the same run. Everything of the old scene is destroyed — its objects, their behaviors, and the sounds those behaviors were playing — while what belongs to the **run** carries on: the values of the Variables assets, the states of the global machines, and the clock. See [Scenes](Scenes). |
|
||||
| **Load scene** | The node that swaps the running scene for another one, inside the same run. Everything of the old scene is destroyed — its objects, their behaviors, and the sounds those behaviors were playing — while what belongs to the **run** carries on: the values of the Variables assets, the states of the global machines, and the clock. A top-level object with **Keep on scene load** switched on travels into the next scene instead, with everything below it. See [Scenes](Scenes). |
|
||||
| **Edit mode** | The Scene tab draws the project model; panels, grid, gizmos and drag-to-move are active. |
|
||||
| **Play mode** | The editor switches to a **runtime copy** of the project (same ids). A `World` is instantiated from the scene the switch next to Play names — the start scene, else the active one (one entity per scene object, resolved properties as mutable state) and shown in the **Game** tab. Systems tick it and mirror their changes into the copy; every panel stays open and edits the copy, which the world picks up immediately. **Stop discards world and copy** — the saved model is never touched. |
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun
|
||||
# has and the web server's container has not (SKIP there)
|
||||
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
|
||||
php tests/php/releases.test.php # the release store (#266), the same way
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1619 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1625 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
|
||||
|
||||
+56
-8
@@ -49,11 +49,11 @@ door, a game-over screen. Its `scene` input takes a scene the way `Play sound` t
|
||||
On collision (Exit) → Load scene (Level 2)
|
||||
```
|
||||
|
||||
**What ends.** Everything of the old scene: its objects, the behaviors on them, and the sounds
|
||||
those behaviors were playing. That last one is not a rule of its own — a voice belongs to the
|
||||
behavior that started it (see [Sounds](Sounds)), and those behaviors are destroyed like everything
|
||||
else in the scene. Music that should carry across a swap is started again in the new scene, or
|
||||
played **detached**.
|
||||
**What ends.** Everything of the old scene that is not kept (see below): its objects, the
|
||||
behaviors on them, and the sounds those behaviors were playing. That last one is not a rule of its
|
||||
own — a voice belongs to the behavior that started it (see [Sounds](Sounds)), and those behaviors
|
||||
are destroyed like everything else in the scene. Music that should carry across a swap is played by
|
||||
a kept object, started again in the new scene, or played **detached**.
|
||||
|
||||
**What carries on.** What belongs to the *run* rather than to the scene:
|
||||
|
||||
@@ -62,8 +62,8 @@ played **detached**.
|
||||
- the **clock**: `Time` keeps counting, only a restart puts it back to zero.
|
||||
|
||||
A **local** machine's state does not: it belongs to a behavior instance, and that instance is
|
||||
gone. Nor does any object — there are no persistent objects. Something that has to travel between
|
||||
scenes travels as a shared variable.
|
||||
gone — unless its object is kept. An object travels only with **Keep on scene load** switched on;
|
||||
everything else that has to get from one scene to the next travels as a shared variable.
|
||||
|
||||
**When it happens.** Once the frame is over, never mid-flow: the graph that asked runs to its end
|
||||
first, in the world it is still standing in. Half a frame in the old world and half in the new one
|
||||
@@ -74,16 +74,64 @@ happens.
|
||||
picture as a restart, except that the shared variables are kept, which is what makes a life
|
||||
counter work.
|
||||
|
||||
**A scene is always built as it is stored.** Whatever the run did in it on an earlier visit —
|
||||
moved a box, destroyed a coin, spawned an enemy — is gone when the scene is loaded again, whether
|
||||
that is the scene it is in or one it comes back to. Until #165 a run in the editor built it from
|
||||
what it had left behind instead. The same holds for what you edit in a scene *while playing*: such
|
||||
an edit was never saved, and the next load of that scene does not have it either.
|
||||
|
||||
**An id naming no scene** does nothing and says so in the console, rather than tearing down the
|
||||
world it is standing in half way through.
|
||||
|
||||
**In the editor run it is ignored** (a warning in the console). The editor run *is* the scene you
|
||||
are looking at, brought to life; swapping it would mean editing a scene you are not looking at.
|
||||
|
||||
## Keep on scene load
|
||||
|
||||
A top-level object with **Keep on scene load** switched on is not destroyed by a `Load scene`. It
|
||||
moves into the next scene as it is, with everything below it:
|
||||
|
||||
- **the same objects**, where the run has them: position, every property and variable. An object
|
||||
held in a variable somewhere still names it,
|
||||
- **its behaviors, running on**: no `On destroy` in the old scene and no second `On start` in the
|
||||
new one, timers, variables and local machines stand where they stood, and a sound such a behavior
|
||||
plays is not cut off,
|
||||
- **its animations**, playing on from where they are,
|
||||
- **how its body moves**: a dynamic rigidbody arrives with the velocity and spin it had.
|
||||
|
||||
The switch sits right under *Enabled*, on the asset and on a placed object, so a `Player` asset can
|
||||
say it once for all its placements. Like every property it can be set while the game runs, with a
|
||||
`Properties` node whose port is on *Set*.
|
||||
|
||||
**Only a top-level object of a scene can be kept.** A child would arrive without the parent it is
|
||||
placed in, so the inspector shows the switch on top-level objects only, and a value an asset brings
|
||||
to an object further down counts for nothing. What hangs below a kept object comes along whatever
|
||||
its own switch says.
|
||||
|
||||
**Duplicates: the one that was brought along wins.** A kept object lands in the next scene after
|
||||
that scene's own objects. Two kinds of the scene's own objects are then not built, each with
|
||||
everything below it:
|
||||
|
||||
- an object with **the same id**. That is the scene the object came from, loaded again: coming back
|
||||
to level 1 gives one player, not the travelled one plus the stored one,
|
||||
- a top-level object of **the same asset that is kept itself**. That is what lets every level place
|
||||
its own `Player` — so each level can be played on its own from the editor — and still hold exactly
|
||||
one after a swap. An object of that asset that is *not* kept is built as usual: five placed crates
|
||||
stay five, whatever crate was brought along.
|
||||
|
||||
Whatever in the scene named its own object by a reference — an *Object* variable picked in the
|
||||
inspector, a row of a scene animation — finds the object that came instead. That covers the object
|
||||
itself, not what hung below the one left out.
|
||||
|
||||
**What does not travel.** Collisions are found anew: a kept object that touches something in the
|
||||
new scene gets an `Enter` for it, also for what it brought along itself. `Ignore one-way` ends with
|
||||
the scene. The picture of a moving body stands still for the one frame the new scene starts in.
|
||||
An object the run kept is back where it was stored once the run stops, like everything a run does.
|
||||
|
||||
## Restart, stop, load — what each one keeps
|
||||
|
||||
| | Objects | Shared variables | Global machines | Clock | Time scale | Pause game | Sounds |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| **Load scene** | rebuilt | kept | kept | keeps running | kept | lifted | end with their behavior |
|
||||
| **Load scene** | rebuilt as stored, kept ones travel | 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 |
|
||||
+3
-1
@@ -69,7 +69,9 @@ afterwards on its own.
|
||||
ends when that behavior is destroyed — a sound is something a thing in the world is *doing*, not a
|
||||
thing of its own. Destroy an enemy and the engine it was running stops with it; no `Stop sound` in
|
||||
an `On destroy` needed, and none forgotten. The same rule is what ends the sounds of a scene that
|
||||
[Load scene](Scenes) swaps away: every behavior in it is destroyed.
|
||||
[Load scene](Scenes) swaps away: every behavior in it is destroyed. A behavior on an object with
|
||||
*Keep on scene load* is not, so its sound plays on through the swap — the plain way to have music
|
||||
that does not restart with every level.
|
||||
|
||||
**`detached` is the way out, and it exists for one shape.** An object that plays its own death
|
||||
sound and destroys itself in the same flow would cut that sound off in the instant it started.
|
||||
|
||||
Reference in new issue
Block a user