Skip to content

SceneTime

Defined in: SceneTime.ts:108

Per-scene time-effect arbitration: hitstop, slow motion / bullet time, freeze frames, and speed-ups that would corrupt each other if every caller wrote scene.timeScale directly (the “restore to what?” bug).

Resolved via the scene-scoped SceneTimeKey; the engine registers one instance per scene.

const time = this.use(SceneTimeKey);
time.freezeFor(0.08); // hitstop
const slow = time.scaleBy(0.25, { key: "slowmo" }); // bullet time
slow.release();

Composition: each key is a channel. Within a channel the latest active request wins (a newer request masks an older still-active one and reveals it again on expiry); across channels the winning factors multiply. A freeze is a ×0 factor, so it dominates arithmetically. scene.timeScale stays the game’s persistent speed knob — this service reads it as an input and never writes it: effectiveScale = scene.timeScale × Π(channel winners).

Request timers age on raw frame time, before any systems run, and only while the scene is active — a stack-paused scene holds its effects (note: that means pause-menu time does not consume a hitstop). Effects are transient: they release on scene exit and are not saved; games re-issue them after loading a snapshot.

new SceneTime(scene): SceneTime

Defined in: SceneTime.ts:122

Scene

SceneTime

get activeLabels(): readonly string[]

Defined in: SceneTime.ts:184

Display labels of all active requests, in creation order per channel.

readonly string[]


get effectiveScale(): number

Defined in: SceneTime.ts:131

The scale every non-excluded consumer runs at: scene.timeScale × Π(channel winners). Physics always steps under this full value (exclusions never apply to the shared world).

number


get elapsed(): number

Defined in: SceneTime.ts:142

Simulation seconds elapsed in this scene: raw frame time scaled by SceneTime.effectiveScale, accrued once per rendered frame and only while the scene is active. A stack-paused scene, a timeScale of 0, and an active freeze all hold it. Starts at 0 each time the scene is entered and is not saved. SceneTime.fixedElapsed is the fixed-timestep reading.

number


get fixedElapsed(): number

Defined in: SceneTime.ts:174

Simulation seconds elapsed in this scene on the fixed timestep: one fixedTimestep × SceneTime.effectiveScale increment per fixed step the loop runs, accrued only while the scene is active.

Stamp a gameplay time from fixed-step code against this reading and compare it there. SceneTime.elapsed moves with the rendered frame, so the same window spans a different number of simulation steps run to run.

Holds under the same conditions as SceneTime.elapsed: stack pause, a timeScale of 0, and an active freeze. Starts at 0 each time the scene is entered and is not saved.

This reading and elapsed advance on different cadences, so at any moment they can differ by one or more fixed steps in either direction. The loop’s fixed-step accumulator is engine-wide: a scene entered mid-run starts counting against the time already in it. A frame that hits maxFixedStepsPerFrame leaves its unrun steps for the following frames. Time waiting in the accumulator is converted at the scale in force when its step runs, not at the scale of the frame it arrived in. Stamp and compare against the same reading — subtracting one from the other does not give a meaningful lag.

The increment uses the whole-scene SceneTime.effectiveScale, so it does not follow entity.timeScale or an excludeUpdates exclusion. An entity running at its own rate should time itself against its ProcessComponent, which composes both.

number


get isFrozen(): boolean

Defined in: SceneTime.ts:179

True while SceneTime.effectiveScale is 0.

boolean

_releaseAll(): void

Defined in: SceneTime.ts:344

Internal

Release every request. Called by the engine on scene exit.

void


_tick(dt): void

Defined in: SceneTime.ts:298

Internal

Age request timers by raw frame time. Called by the engine at the start of earlyUpdate for each active scene, so a request created later in the frame is not aged until the next frame. Masked entries keep aging. Also prunes destroyed entities from exclusion sets.

number

void


_tickFixed(fixedDt): void

Defined in: SceneTime.ts:336

Internal

Accrue one fixed step of simulation time. Called by the engine once per fixed step for each active scene. Request timers age once per frame in _tick, never here.

number

void


effectiveScaleForUpdates(entity): number

Defined in: SceneTime.ts:198

The scale entity’s component updates, ProcessComponent, and particle emitters run at: like SceneTime.effectiveScale, but a channel whose winner excludes the entity contributes 1. entity.timeScale is not included — the update pipeline composes it on top.

Entity

number


freezeFor(duration, options?): TimeEffectHandle

Defined in: SceneTime.ts:233

Freeze the scene (a ×0 factor) for duration real-time seconds. Returns the same handle shape as scaleBy for an early release. Freezes are whole-scene by design — a shared physics world has no per-entity time, so freeze requests take no excludeUpdates.

number

SceneTimeFreezeOptions

TimeEffectHandle


scaleBy(factor, options?): TimeEffectHandle

Defined in: SceneTime.ts:208

Add a scale request. factor must be finite and > 0 (freezing goes through SceneTime.freezeFor); factors above 1 speed the scene up — physics catch-up is capped at ~8 sub-steps per frame.

number

SceneTimeScaleOptions

TimeEffectHandle