A game that is more than one scene needs a way from one to the next, so the graph can load one (#39)
game-maker / suite (push) Failing after 11m6s
game-maker / suite (push) Failing after 11m6s
Load scene sets a flag on the World like Stop and Restart do; the swap happens after the tick, and the last wish of a tick wins. Exactly one thing crosses over: the SharedState, so global variables and global machines keep what they know while everything local is built anew. Loading the scene that is already running rebuilds it, which is what "reset the level" means when the score must survive. A run starts either from the start scene (a new project field, set from the asset panel and marked with an arrow in the list) or from whatever scene is open. The toggle next to Play says which question is asked and remembers the answer in localStorage: it is a way of working, not part of the game. Sounds no longer end because a scene ends. A voice belongs to the behavior that started it and stops right after On destroy, so the scene swap is no longer a special case at all. For the enemy that plays its explosion and destroys itself in the same breath there is now a detached input on Play sound. The scene selector stays usable while a run plays, because a double click in the asset panel already did the same thing unguarded. It moves what the editor looks at, not what the run plays.
This commit is contained in:
1 parent
30090fc48e
commit
1e9d21cd5d
30 files changed
+976
-57
No files matched your search
@@ -93,6 +93,11 @@ export class PlaySession {
|
||||
return this.world?.time ?? 0;
|
||||
}
|
||||
|
||||
/** The scene the run is in – not the editor's active one, which a `Load scene` leaves alone. */
|
||||
get sceneId(): string {
|
||||
return this.world?.sceneId ?? '';
|
||||
}
|
||||
|
||||
syncFromModel(): void {
|
||||
this.world?.syncFromModel();
|
||||
this.behaviors?.syncDeclarations();
|
||||
@@ -104,7 +109,7 @@ export class PlaySession {
|
||||
}
|
||||
|
||||
play(): void {
|
||||
const scene = this.state.activeScene;
|
||||
const scene = this.state.sceneToPlay();
|
||||
if (!scene) return;
|
||||
ui().focus.blur();
|
||||
// A run starts at second zero, whatever the editor's previews have been counting up to.
|
||||
@@ -120,6 +125,26 @@ export class PlaySession {
|
||||
if (world.stopRequested) this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the running scene for another one, inside the same run: the world is torn down and built
|
||||
* again, while what belongs to the run – the clock, the shared variables, the global machines –
|
||||
* carries on. The sounds of the old scene end with it, because the behaviors playing them are
|
||||
* destroyed; a voice that was started detached is nobody's and plays on.
|
||||
*/
|
||||
private loadScene(sceneId: string): void {
|
||||
const old = this.world;
|
||||
const runtime = this.state.runtimeProject;
|
||||
if (!old || !runtime) return;
|
||||
const carried = this.behaviors?.shared ?? null;
|
||||
old.destroy();
|
||||
const world = World.instantiate(runtime, sceneId);
|
||||
this.world = world;
|
||||
this.behaviors = addGameSystems(world, runtime, this.input, undefined, soundLibrary, carried);
|
||||
this.host.gameView.setWorld(world);
|
||||
this.state.setNotice(this.playNotice(), 'quiet');
|
||||
if (world.stopRequested) this.stop();
|
||||
}
|
||||
|
||||
private playNotice(): string {
|
||||
if (this.state.paused) return 'Paused · Ctrl+Enter resumes · Esc stops';
|
||||
return this.world?.activeCamera()
|
||||
@@ -168,6 +193,10 @@ export class PlaySession {
|
||||
this.play();
|
||||
return;
|
||||
}
|
||||
if (world.loadSceneRequested) {
|
||||
this.loadScene(world.loadSceneRequested);
|
||||
return;
|
||||
}
|
||||
// Mirror what the systems did into the runtime copy so the panels show live values.
|
||||
if (world.syncToModel().structure) this.state.preview();
|
||||
this.state.runtimeTick();
|
||||
|
||||
Reference in new issue
Block a user