Variables and state machines can reset with every scene (#164)
A shared variable or machine used to live either per object or for the
whole run. Levels need a third lifetime: shared like global, but built
fresh from the declarations whenever a scene is loaded.
- New scope `scene` for Variables assets and state machines. A missing
field keeps the old meaning: `global` for assets, `local` for
machines, so existing projects do not change.
- Returning to a scene that was visited before is fresh as well. There
is no memory per scene.
- A `scene` machine announces its start state again after a scene swap,
so `On state entered` in the new scene hears it. A global machine
stays quiet because it never left its state.
- The rule lives in two pure functions in `sharedState.ts`
(`carriedForward`, `restartedAssets`). BehaviorSystem only calls them.
- The inspector shows a Scope dropdown on Variables assets and a third
step on the machine dropdown, with a note on what `scene` means.
Decisions:
- `scene` and `persistent` exclude each other (variant A). The choice
dialog was cancelled, so this is an assumption to revisit. A
scene-scoped asset hides the Persistent switch, and switching to
`scene` drops the flag (undo restores it). The guard sits in
`isPersistent` itself, so no path can save values the scope throws
away anyway.
Findings:
- `clickText('Scene')` hits the Scene tab, not the menu entry. One e2e
check was red because of that, and its partner was falsely green
because it compared `undefined` with `undefined`. Both use `clickLast`
now.
Tests:
- ./check.sh: 144 files, 2142 tests, CHECK EXIT 0 (before 143 / 2133)
- 9 new unit tests in tests/core/scene-scope.test.ts
- e2e 32 fresh: ALL PASSED, 17 checks
- e2e 1-3,17,18,26,32,35 fresh: ALL PASSED, 213 checks
- 3 new e2e checks (35: scene-scoped asset resets on swap, 32h: dropdown,
note, hidden Persistent switch)
- Counter-checks bite: ignoring `restarted` fails 2 unit tests and the
new check in 35, the old announcement rule fails 1, dropping the scope
guard in `isPersistent` fails 1. Originals restored, cmp IDENTICAL.
- Check sum 1307 (1304 + 3) is computed. Last CI measurement: 1293.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6e3e8d23c5
commit
d7c0b7cfdb
12 files changed
+487
-43
No files matched your search
@@ -2,6 +2,7 @@ import {
|
||||
adoptDeclarations,
|
||||
asNumber,
|
||||
buildSharedState,
|
||||
carriedForward,
|
||||
declaredVariables,
|
||||
emptySharedState,
|
||||
type EntityHandle,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
isHandleEnabled,
|
||||
type NodeContext,
|
||||
type PhysicsControl,
|
||||
restartedAssets,
|
||||
restoreSaved,
|
||||
type SaveArea,
|
||||
savedSnapshot,
|
||||
@@ -171,6 +173,8 @@ export class BehaviorSystem implements System {
|
||||
private sharedState: SharedState = emptySharedState();
|
||||
/** Handed over by the previous world of the same run; consumed by the next `start`. */
|
||||
private carried: SharedState | null = null;
|
||||
/** Which of the carried machines this scene starts over, so their entry is announced again. */
|
||||
private restarted: ReadonlySet<string> = new Set();
|
||||
/** Moves whose events are still owed; drained by `flushStateChanges` between exec flows. */
|
||||
private pending: StateChange[] = [];
|
||||
/** The declarations as last read; an edit is what differs from these (see `syncDeclarations`). */
|
||||
@@ -224,9 +228,10 @@ export class BehaviorSystem implements System {
|
||||
* the world is started.
|
||||
*/
|
||||
carryOver(state: Readonly<SharedState>): void {
|
||||
// The bags themselves travel as they are – a running value is the point – while the map around
|
||||
// them is copied, so the old system cannot still be writing into what the new one reads.
|
||||
this.carried = { variables: { ...state.variables }, states: { ...state.states } };
|
||||
// The bags of what is `global` travel as they are – a running value is the point – while what is
|
||||
// `scene` is built again from the declarations, so a level counts from zero (#164).
|
||||
this.carried = carriedForward(this.project, state);
|
||||
this.restarted = restartedAssets(this.project);
|
||||
}
|
||||
|
||||
start(world: World): void {
|
||||
@@ -245,9 +250,11 @@ export class BehaviorSystem implements System {
|
||||
// the move is queued here and every instance the first sync creates hears that one entry.
|
||||
// Not after a scene swap: the machine is already standing where it stands, and announcing the
|
||||
// entry again would be a move that never happened.
|
||||
if (!carried)
|
||||
for (const [machineId, start] of Object.entries(this.sharedState.states))
|
||||
if (start) this.pending.push({ machineId, from: '', to: start, source: null });
|
||||
// A `scene` machine is the exception after a swap: it really was entered again, just now.
|
||||
for (const [machineId, start] of Object.entries(this.sharedState.states))
|
||||
if (start && (!carried || this.restarted.has(machineId)))
|
||||
this.pending.push({ machineId, from: '', to: start, source: null });
|
||||
this.restarted = new Set();
|
||||
this.syncInstances(world);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user