Szenen: Objekte über „Load scene“ hinweg behalten #165

Closed
opened 2026-09-19 11:41:51 +02:00 by meleki · 2 comments
Owner

Aus #39 („Persistente Objekte gibt es bewusst nicht — Identität, Duplikate und Elternschaft sind eigene Fragen und gehören in ein eigenes Issue“).

Befund

  • Beim Load scene geht nur der SharedState hinüber (globale Variablen und Maschinen). Jedes Objekt der alten Szene wird zerstört, auch die Spielfigur, die HUD-Leiste oder ein Musik-Objekt.
  • Heute baut man solche Objekte in jeder Szene neu und schiebt ihren Zustand über globale Variablen.

Zu bauen (Vorschlag)

  • Objekt-Property Keep on scene load: Das Objekt (mit Kindern, Behaviors und deren lokalem Zustand) überlebt den Wechsel.
  • Tests: Objekt überlebt samt Variablenstand; On start feuert nicht erneut; Rapier-Body wird in die neue Welt übernommen.

Offen (mit Vorschlag)

  1. Duplikate: Die neue Szene enthält dasselbe Objekt (gleiche Vorlage oder gleicher Name) auch. Vorschlag: das mitgebrachte gewinnt, das der neuen Szene wird nicht gebaut — Abgleich über die Objekt-Id der Vorlage.
  2. Elternschaft: Ein persistentes Kind eines nicht persistenten Elternteils. Vorschlag: nur Wurzelobjekte können persistent sein.
  3. Rückkehr in die Ursprungsszene: Vorschlag: wie Punkt 1.
Aus #39 („Persistente Objekte gibt es bewusst nicht — Identität, Duplikate und Elternschaft sind eigene Fragen und gehören in ein eigenes Issue“). ## Befund - Beim `Load scene` geht nur der `SharedState` hinüber (globale Variablen und Maschinen). Jedes Objekt der alten Szene wird zerstört, auch die Spielfigur, die HUD-Leiste oder ein Musik-Objekt. - Heute baut man solche Objekte in jeder Szene neu und schiebt ihren Zustand über globale Variablen. ## Zu bauen (Vorschlag) - Objekt-Property **Keep on scene load**: Das Objekt (mit Kindern, Behaviors und deren lokalem Zustand) überlebt den Wechsel. - Tests: Objekt überlebt samt Variablenstand; `On start` feuert nicht erneut; Rapier-Body wird in die neue Welt übernommen. ## Offen (mit Vorschlag) 1. **Duplikate:** Die neue Szene enthält dasselbe Objekt (gleiche Vorlage oder gleicher Name) auch. Vorschlag: das mitgebrachte gewinnt, das der neuen Szene wird nicht gebaut — Abgleich über die Objekt-Id der Vorlage. 2. **Elternschaft:** Ein persistentes Kind eines nicht persistenten Elternteils. Vorschlag: nur Wurzelobjekte können persistent sein. 3. **Rückkehr in die Ursprungsszene:** Vorschlag: wie Punkt 1.
meleki added the new label 2026-09-19 11:41:51 +02:00
admin added low-priority and removed new labels 2026-09-19 12:09:55 +02:00
admin added this to the Development project 2026-09-19 12:09:59 +02:00
  1. vorschlag passt
  2. vorschlag passt
  3. passt
1. vorschlag passt 2. vorschlag passt 3. passt
admin moved this to Next in Development on 2026-09-22 09:26:31 +02:00
Author
Owner

Done in 1aa824e, wiki b2d413b (Scenes, Architecture, Concepts, Sounds, Development).

What is new

  • Switch Keep on scene load, right under Enabled, on the asset and on a placed object, off by default. A top-level object with the switch survives a Load scene and moves into the next scene with everything below it:
    • it is the same object, with position, properties and variables as the run has them
    • its behaviors run on: no On destroy, no second On start, and a sound they play is not cut off
    • a running animation plays on
    • a dynamic rigidbody keeps its velocity and spin
  • Only top-level objects (answer to point 2): the inspector shows the row on top-level objects of a scene only. Further down the value counts for nothing.

Duplicates (points 1 and 3)
The object that was brought along wins. The next scene does not build two kinds of its own objects:

  • an object with the same id. That is the way back into the scene the object came from, and it gives one object, not two.
  • a top-level object of the same asset that is kept itself. So every level can place its own Player, and after a swap there is still exactly one. Objects of that asset without the switch are built as usual.

The second rule is my reading. The issue says "Abgleich über die Objekt-Id der Vorlage", and the id alone would have given two players as soon as level 2 places its own. Going back to the id only would be one line.

A reference in the next scene to its own object that was left out finds the one that came instead. That covers the object itself, not its children.

A finding, fixed along with it
Load scene did not build a scene as it is stored, but as the run had left it. Measured before the change: a moved object stayed moved after loading the scene again, a destroyed one stayed gone. The wiki said "rebuilt".

  • The run now remembers each scene before it first builds it and puts it back before it builds it again. The way back into the scene an object came from needs that.
  • In a released game the run gives every scene back on stop. Before, spawned objects stayed for "Click to play again".
  • The consequence: an edit made in a scene during Play is gone the next time that scene is loaded.

Decided here

  • Kept objects land behind the next scene's own objects.
  • The switch is not offered as an animation row.
  • Collisions are found anew. Two kept objects that were touching get their Enter again.
  • In the first frame after the swap a falling body stands still for one frame, then falls on with its old velocity.

Under the hood

  • src/runtime/keptObjects.ts holds the rules as pure functions: keptEntities, objectsToBring, bringObjects.
  • World.instantiate takes a handed entity as it is instead of making one. BehaviorSystem.release and adopt hand the running instances over, PhysicsSystem.resume the movement of the bodies.
  • GameRun copies a scene's objects and groups before the first build, GameRun.end puts every scene back.

Tests (on melektaus-atx)

gate        199 files, 2944 tests, CHECK EXIT 0 (2929 + 15)
e2e (full)  ALL PASSED - 1625 checks, E2E EXIT 0 (1619 + 6)
smokes      24 / 19 / 21, SMOKES EXIT 0
  • 15 unit tests in tests/core/keep-on-scene-load.test.ts, 13 for the switch and 2 for the clean build.
  • Six e2e checks in module 35: the switch is set with a real mouse, the child has no row. After the swap the object is in the new scene, its counter still reads 1, and its sound plays on. After Stop the stored scenes are unchanged.
  • Eleven tests with fixed row lists only count the new schema row.
  • Screenshot looked at: the Game view shows three boxes after the swap, the child has no row in the inspector.
Counter-check Result
16 in the unit tests all work in the end
Browser, build went through 4 of the 6 new checks fail
  • One unit counter-check did not work at first (same id not left out): the world drops doubles by id, but the scene would have held the object twice. It has its own test now.
  • The unit counter-checks ran on stein2, not in the container.
  • The 1619 before this change are measured (CI run 277 for 3e8c88c is green). CI run 278 for 1aa824e was not waited for.
  • Not looked at in the browser: the switch as a port of the Properties node (the wiki says so from the code), and a released game with a kept object.
Done in `1aa824e`, wiki `b2d413b` (*Scenes*, *Architecture*, *Concepts*, *Sounds*, *Development*). **What is new** - Switch **Keep on scene load**, right under *Enabled*, on the asset and on a placed object, off by default. A top-level object with the switch survives a `Load scene` and moves into the next scene with everything below it: - it is the same object, with position, properties and variables as the run has them - its behaviors run on: no `On destroy`, no second `On start`, and a sound they play is not cut off - a running animation plays on - a dynamic rigidbody keeps its velocity and spin - **Only top-level objects** (answer to point 2): the inspector shows the row on top-level objects of a scene only. Further down the value counts for nothing. **Duplicates (points 1 and 3)** The object that was brought along wins. The next scene does not build two kinds of its own objects: - an object with **the same id**. That is the way back into the scene the object came from, and it gives one object, not two. - a top-level object of **the same asset that is kept itself**. So every level can place its own `Player`, and after a swap there is still exactly one. Objects of that asset without the switch are built as usual. The second rule is my reading. The issue says "Abgleich über die Objekt-Id der Vorlage", and the id alone would have given two players as soon as level 2 places its own. Going back to the id only would be one line. A reference in the next scene to its own object that was left out finds the one that came instead. That covers the object itself, not its children. **A finding, fixed along with it** `Load scene` did not build a scene as it is stored, but as the run had left it. Measured before the change: a moved object stayed moved after loading the scene again, a destroyed one stayed gone. The wiki said "rebuilt". - The run now remembers each scene before it first builds it and puts it back before it builds it again. The way back into the scene an object came from needs that. - In a released game the run gives every scene back on stop. Before, spawned objects stayed for "Click to play again". - The consequence: an edit made in a scene during Play is gone the next time that scene is loaded. **Decided here** - Kept objects land behind the next scene's own objects. - The switch is not offered as an animation row. - Collisions are found anew. Two kept objects that were touching get their `Enter` again. - In the first frame after the swap a falling body stands still for one frame, then falls on with its old velocity. **Under the hood** - `src/runtime/keptObjects.ts` holds the rules as pure functions: `keptEntities`, `objectsToBring`, `bringObjects`. - `World.instantiate` takes a handed entity as it is instead of making one. `BehaviorSystem.release` and `adopt` hand the running instances over, `PhysicsSystem.resume` the movement of the bodies. - `GameRun` copies a scene's objects and groups before the first build, `GameRun.end` puts every scene back. **Tests** (on `melektaus-atx`) ``` gate 199 files, 2944 tests, CHECK EXIT 0 (2929 + 15) e2e (full) ALL PASSED - 1625 checks, E2E EXIT 0 (1619 + 6) smokes 24 / 19 / 21, SMOKES EXIT 0 ``` - 15 unit tests in `tests/core/keep-on-scene-load.test.ts`, 13 for the switch and 2 for the clean build. - Six e2e checks in module 35: the switch is set with a real mouse, the child has no row. After the swap the object is in the new scene, its counter still reads 1, and its sound plays on. After Stop the stored scenes are unchanged. - Eleven tests with fixed row lists only count the new schema row. - Screenshot looked at: the Game view shows three boxes after the swap, the child has no row in the inspector. | Counter-check | Result | |---|---| | 16 in the unit tests | all work in the end | | Browser, build went through | 4 of the 6 new checks fail | - One unit counter-check did not work at first (same id not left out): the world drops doubles by id, but the scene would have held the object twice. It has its own test now. - The unit counter-checks ran on stein2, not in the container. - The 1619 before this change are measured (CI run 277 for `3e8c88c` is green). CI run 278 for `1aa824e` was not waited for. - Not looked at in the browser: the switch as a port of the `Properties` node (the wiki says so from the code), and a released game with a kept object.
admin moved this to Done in Development on 2026-10-01 17:54:33 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#165