A game can pause itself and set its own time scale (#167)
Until now only the editor toolbar could pause a running game. A pause menu inside the game had no way to stop the world while its own graph kept running, and there was no slow motion. - Five new nodes: Pause game, Resume game, Is game paused, Set time scale and Time scale. Pause and resume take effect on the next frame. - A game pause stops world time, physics, animations, particles and shader time. Only behaviors with the new "Run while paused" switch keep running. - Set time scale 0 freezes the world but graphs keep running, as agreed in the issue. - Play and Stop reset the time scale to 1, Load scene keeps it. The pause belongs to the scene, so Load scene and Restart unpause. - The toolbar pause stays separate and also freezes the menu. When the game pauses itself the status line reads "The game paused itself". - While paused, collision events are not reported, sounds keep playing, and the editor run ignores both nodes with a log line. Findings: - The first status line was longer than any before and got cut with an ellipsis. It is shorter now. - The e2e harness did not know the key "p". It does now. Tests: - ./check.sh: 133 files, 1968 tests, exit 0 (before 132 / 1952) - npm run build: exit 0 - GM_STEPS=18 fresh: ALL PASSED, 33 checks (before 26) - 16 new unit tests: 8 catalog cases, game-pause.test.ts, editor run - 7 new e2e checks in block 18i, driven by real keys in the game - Six counter-checks all bite: behavior filter, collision lock, world time, editor run, clock reset and the game clock in the browser - Check total 1217 in README is computed (1210 + 7), CI measures it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7063c8d074
commit
3427f84a55
22 files changed
+570
-26
No files matched your search
+22
-2
@@ -30,7 +30,7 @@ import { createEmptyProject, findScene, type Project } from '../src/core/model/P
|
||||
import type { SceneObject } from '../src/core/model/SceneObject';
|
||||
import { deserializeProject, serializeProject } from '../src/core/store/serialize';
|
||||
import type { AudioSink } from '../src/runtime/systems/Audio';
|
||||
import { BehaviorSystem } from '../src/runtime/systems/BehaviorSystem';
|
||||
import { BehaviorSystem, type TimeScaled } from '../src/runtime/systems/BehaviorSystem';
|
||||
import { readyPhysics } from '../src/runtime/physics/engine';
|
||||
import { PhysicsSystem } from '../src/runtime/physics/PhysicsSystem';
|
||||
import { CollisionSystem } from '../src/runtime/systems/CollisionSystem';
|
||||
@@ -202,6 +202,9 @@ export type FakeContext = NodeContext & {
|
||||
silenced: string[];
|
||||
/** The scenes the context was asked to load, in order. */
|
||||
loaded: string[];
|
||||
/** The time scale and the pause the context holds, as the game nodes left them. */
|
||||
scale: number;
|
||||
paused: boolean;
|
||||
};
|
||||
|
||||
/** A NodeContext double: self/owner handles, `speed = 100`, dt 0.5, moves applied directly, `log` and `stop` recorded. */
|
||||
@@ -245,6 +248,16 @@ export function fakeContext(overrides: Partial<NodeContext> = {}): FakeContext {
|
||||
ctx.stopped = true;
|
||||
},
|
||||
restart: () => {},
|
||||
scale: 1,
|
||||
paused: false,
|
||||
timeScale: () => ctx.scale,
|
||||
setTimeScale: (scale) => {
|
||||
ctx.scale = scale;
|
||||
},
|
||||
isPaused: () => ctx.paused,
|
||||
setPaused: (paused) => {
|
||||
ctx.paused = paused;
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
return ctx;
|
||||
@@ -284,6 +297,8 @@ export interface WorldOptions {
|
||||
behaviors?: boolean;
|
||||
/** Where `Play sound` ends up; without one the run plays to nobody (see `SILENT_AUDIO`). */
|
||||
audio?: AudioSink;
|
||||
/** The clock `Set time scale` turns; without one the run is treated like the editor run. */
|
||||
clock?: TimeScaled;
|
||||
}
|
||||
|
||||
export interface StartedWorld {
|
||||
@@ -314,7 +329,12 @@ export function startWorld(fixture: SceneFixture, options: WorldOptions = {}): S
|
||||
if (collisions) world.addSystem(collisions);
|
||||
if (animations) world.addSystem(animations);
|
||||
const behaviors = options.behaviors
|
||||
? new BehaviorSystem(fixture.project, input, { collisions, animations, audio: options.audio })
|
||||
? new BehaviorSystem(fixture.project, input, {
|
||||
collisions,
|
||||
animations,
|
||||
audio: options.audio,
|
||||
clock: options.clock,
|
||||
})
|
||||
: null;
|
||||
if (behaviors) world.addSystem(behaviors);
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user