@yagejs/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +624 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +361 -23
- package/dist/index.d.ts +361 -23
- package/dist/index.js +620 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -85,6 +85,7 @@ __export(index_exports, {
|
|
|
85
85
|
Inspector: () => Inspector,
|
|
86
86
|
InspectorKey: () => InspectorKey,
|
|
87
87
|
KeyframeAnimator: () => KeyframeAnimator,
|
|
88
|
+
LoadingScene: () => LoadingScene,
|
|
88
89
|
LogLevel: () => LogLevel,
|
|
89
90
|
Logger: () => Logger,
|
|
90
91
|
LoggerKey: () => LoggerKey,
|
|
@@ -100,6 +101,8 @@ __export(index_exports, {
|
|
|
100
101
|
QueryResult: () => QueryResult,
|
|
101
102
|
SERIALIZABLE_KEY: () => SERIALIZABLE_KEY,
|
|
102
103
|
Scene: () => Scene,
|
|
104
|
+
SceneHookRegistry: () => SceneHookRegistry,
|
|
105
|
+
SceneHookRegistryKey: () => SceneHookRegistryKey,
|
|
103
106
|
SceneManager: () => SceneManager,
|
|
104
107
|
SceneManagerKey: () => SceneManagerKey,
|
|
105
108
|
Sequence: () => Sequence,
|
|
@@ -132,6 +135,7 @@ __export(index_exports, {
|
|
|
132
135
|
getSerializableType: () => getSerializableType,
|
|
133
136
|
interpolate: () => interpolate,
|
|
134
137
|
isSerializable: () => isSerializable,
|
|
138
|
+
resolveTransition: () => resolveTransition,
|
|
135
139
|
serializable: () => serializable,
|
|
136
140
|
trait: () => trait
|
|
137
141
|
});
|
|
@@ -458,13 +462,16 @@ var Logger = class {
|
|
|
458
462
|
|
|
459
463
|
// src/EngineContext.ts
|
|
460
464
|
var ServiceKey = class {
|
|
461
|
-
constructor(id) {
|
|
465
|
+
constructor(id, options) {
|
|
462
466
|
this.id = id;
|
|
467
|
+
this.scope = options?.scope ?? "engine";
|
|
463
468
|
}
|
|
464
469
|
id;
|
|
465
470
|
static {
|
|
466
471
|
__name(this, "ServiceKey");
|
|
467
472
|
}
|
|
473
|
+
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
474
|
+
scope;
|
|
468
475
|
};
|
|
469
476
|
var EngineContext = class {
|
|
470
477
|
static {
|
|
@@ -510,6 +517,50 @@ var SystemSchedulerKey = new ServiceKey("systemScheduler");
|
|
|
510
517
|
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
511
518
|
var AssetManagerKey = new ServiceKey("assetManager");
|
|
512
519
|
|
|
520
|
+
// src/SceneHooks.ts
|
|
521
|
+
var SceneHookRegistry = class {
|
|
522
|
+
static {
|
|
523
|
+
__name(this, "SceneHookRegistry");
|
|
524
|
+
}
|
|
525
|
+
hooks = [];
|
|
526
|
+
register(hooks) {
|
|
527
|
+
this.hooks.push(hooks);
|
|
528
|
+
return () => {
|
|
529
|
+
const idx = this.hooks.indexOf(hooks);
|
|
530
|
+
if (idx !== -1) this.hooks.splice(idx, 1);
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/** Run all `beforeEnter` hooks serially. */
|
|
534
|
+
async runBeforeEnter(scene) {
|
|
535
|
+
for (const h of this.hooks) {
|
|
536
|
+
await h.beforeEnter?.(scene);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
runAfterExit(scene) {
|
|
540
|
+
for (const h of this.hooks) {
|
|
541
|
+
try {
|
|
542
|
+
h.afterExit?.(scene);
|
|
543
|
+
} catch (err) {
|
|
544
|
+
const logger = scene.context.tryResolve(LoggerKey);
|
|
545
|
+
if (logger) {
|
|
546
|
+
logger.error("core", "Scene afterExit hook threw", {
|
|
547
|
+
scene: scene.name,
|
|
548
|
+
error: err
|
|
549
|
+
});
|
|
550
|
+
} else {
|
|
551
|
+
console.error(
|
|
552
|
+
`[yage] Scene afterExit hook threw for scene "${scene.name}":`,
|
|
553
|
+
err
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
var SceneHookRegistryKey = new ServiceKey(
|
|
561
|
+
"sceneHookRegistry"
|
|
562
|
+
);
|
|
563
|
+
|
|
513
564
|
// src/EventToken.ts
|
|
514
565
|
var EventToken = class {
|
|
515
566
|
constructor(name) {
|
|
@@ -731,10 +782,11 @@ var Component = class {
|
|
|
731
782
|
_cleanups;
|
|
732
783
|
/**
|
|
733
784
|
* Access the entity's scene. Throws if the entity is not in a scene.
|
|
734
|
-
* Prefer this over `this.entity.scene
|
|
785
|
+
* Prefer this over threading through `this.entity.scene` in component
|
|
786
|
+
* code.
|
|
735
787
|
*/
|
|
736
788
|
get scene() {
|
|
737
|
-
const scene = this.entity.
|
|
789
|
+
const scene = this.entity.tryScene;
|
|
738
790
|
if (!scene) {
|
|
739
791
|
throw new Error(
|
|
740
792
|
"Cannot access scene: entity is not attached to a scene."
|
|
@@ -749,20 +801,43 @@ var Component = class {
|
|
|
749
801
|
get context() {
|
|
750
802
|
return this.scene.context;
|
|
751
803
|
}
|
|
752
|
-
/**
|
|
804
|
+
/**
|
|
805
|
+
* Resolve a service by key, cached after first lookup. Scene-scoped values
|
|
806
|
+
* (registered via `scene._registerScoped`) take precedence over engine
|
|
807
|
+
* scope. A key declared with `scope: "scene"` that falls back to engine
|
|
808
|
+
* scope emits a one-shot dev warning — almost always signals a missed
|
|
809
|
+
* `beforeEnter` hook.
|
|
810
|
+
*/
|
|
753
811
|
use(key) {
|
|
754
812
|
this._serviceCache ??= /* @__PURE__ */ new Map();
|
|
755
|
-
|
|
756
|
-
if (
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
813
|
+
const cached = this._serviceCache.get(key.id);
|
|
814
|
+
if (cached !== void 0) return cached;
|
|
815
|
+
const scene = this.entity.tryScene;
|
|
816
|
+
const scoped = scene?._resolveScoped(key);
|
|
817
|
+
if (scoped !== void 0) {
|
|
818
|
+
this._serviceCache.set(key.id, scoped);
|
|
819
|
+
return scoped;
|
|
820
|
+
}
|
|
821
|
+
const value = this.context.resolve(key);
|
|
822
|
+
if (key.scope === "scene") {
|
|
823
|
+
this._warnScopedFallback(key);
|
|
824
|
+
return value;
|
|
825
|
+
}
|
|
826
|
+
this._serviceCache.set(key.id, value);
|
|
760
827
|
return value;
|
|
761
828
|
}
|
|
829
|
+
_warnScopedFallback(key) {
|
|
830
|
+
const logger = this.context.tryResolve(LoggerKey);
|
|
831
|
+
logger?.warn(
|
|
832
|
+
"core",
|
|
833
|
+
`Scoped key "${key.id}" fell back to engine scope \u2014 did a plugin forget to register a beforeEnter hook?`,
|
|
834
|
+
{ component: this.constructor.name }
|
|
835
|
+
);
|
|
836
|
+
}
|
|
762
837
|
/**
|
|
763
838
|
* Lazy proxy-based service resolution. Can be used at field-declaration time:
|
|
764
839
|
* ```ts
|
|
765
|
-
* readonly
|
|
840
|
+
* readonly input = this.service(InputManagerKey);
|
|
766
841
|
* ```
|
|
767
842
|
* The actual resolution is deferred until first property access.
|
|
768
843
|
*/
|
|
@@ -1019,8 +1094,27 @@ var Entity = class {
|
|
|
1019
1094
|
this.name = name ?? new.target.name ?? "Entity";
|
|
1020
1095
|
this.tags = new Set(tags);
|
|
1021
1096
|
}
|
|
1022
|
-
/**
|
|
1097
|
+
/**
|
|
1098
|
+
* The scene this entity belongs to. Throws if the entity is not attached
|
|
1099
|
+
* to a scene — which in practice only happens before `scene.spawn` /
|
|
1100
|
+
* `addChild` wires it up, or after `destroy()` tears it down. Inside
|
|
1101
|
+
* lifecycle methods (`setup`, component `onAdd`, `update`, etc.) this is
|
|
1102
|
+
* always safe to access.
|
|
1103
|
+
*
|
|
1104
|
+
* For the rare case where you genuinely need to inspect whether an
|
|
1105
|
+
* entity has a scene (e.g. defensive code in systems iterating a query
|
|
1106
|
+
* result), use `tryScene` instead.
|
|
1107
|
+
*/
|
|
1023
1108
|
get scene() {
|
|
1109
|
+
if (!this._scene) {
|
|
1110
|
+
throw new Error(
|
|
1111
|
+
`Entity "${this.name}" is not attached to a scene. Use \`tryScene\` if you need to check.`
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
return this._scene;
|
|
1115
|
+
}
|
|
1116
|
+
/** The scene this entity belongs to, or `null` if detached. */
|
|
1117
|
+
get tryScene() {
|
|
1024
1118
|
return this._scene;
|
|
1025
1119
|
}
|
|
1026
1120
|
/** True if destroy() has been called. */
|
|
@@ -1058,6 +1152,20 @@ var Entity = class {
|
|
|
1058
1152
|
this._scene._addExistingEntity(child);
|
|
1059
1153
|
}
|
|
1060
1154
|
}
|
|
1155
|
+
spawnChild(name, classOrBlueprint, params) {
|
|
1156
|
+
const scene = this.scene;
|
|
1157
|
+
if (this._children?.has(name)) {
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`Entity "${this.name}" already has a child named "${name}".`
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
const child = classOrBlueprint === void 0 ? scene.spawn(name) : scene.spawn(
|
|
1163
|
+
classOrBlueprint,
|
|
1164
|
+
params
|
|
1165
|
+
);
|
|
1166
|
+
this.addChild(name, child);
|
|
1167
|
+
return child;
|
|
1168
|
+
}
|
|
1061
1169
|
/** Remove a named child. Returns the detached entity. */
|
|
1062
1170
|
removeChild(name) {
|
|
1063
1171
|
const child = this._children?.get(name);
|
|
@@ -1550,7 +1658,7 @@ var GameLoop = class {
|
|
|
1550
1658
|
}
|
|
1551
1659
|
/** Process one frame with the given dt in milliseconds. */
|
|
1552
1660
|
tick(dtMs) {
|
|
1553
|
-
if (!this.callbacks) return;
|
|
1661
|
+
if (!this.running || !this.callbacks) return;
|
|
1554
1662
|
this._frameCount++;
|
|
1555
1663
|
this.callbacks.earlyUpdate(dtMs);
|
|
1556
1664
|
this.accumulator += dtMs;
|
|
@@ -1578,6 +1686,8 @@ var Scene = class {
|
|
|
1578
1686
|
transparentBelow = false;
|
|
1579
1687
|
/** Asset handles to load before onEnter(). Override in subclasses. */
|
|
1580
1688
|
preload;
|
|
1689
|
+
/** Default transition used when this scene is the destination of a push/pop/replace. */
|
|
1690
|
+
defaultTransition;
|
|
1581
1691
|
/** Manual pause flag. Set by game code to pause this scene regardless of stack position. */
|
|
1582
1692
|
paused = false;
|
|
1583
1693
|
/** Time scale multiplier for this scene. 1.0 = normal, 0.5 = half speed. Default: 1. */
|
|
@@ -1589,6 +1699,7 @@ var Scene = class {
|
|
|
1589
1699
|
queryCache;
|
|
1590
1700
|
bus;
|
|
1591
1701
|
_entityEventHandlers;
|
|
1702
|
+
_scopedServices;
|
|
1592
1703
|
/** Access the EngineContext. */
|
|
1593
1704
|
get context() {
|
|
1594
1705
|
return this._context;
|
|
@@ -1606,6 +1717,11 @@ var Scene = class {
|
|
|
1606
1717
|
}
|
|
1607
1718
|
return false;
|
|
1608
1719
|
}
|
|
1720
|
+
/** Whether a scene transition is currently running. */
|
|
1721
|
+
get isTransitioning() {
|
|
1722
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
1723
|
+
return sm?.isTransitioning ?? false;
|
|
1724
|
+
}
|
|
1609
1725
|
/** Convenience accessor for the AssetManager. */
|
|
1610
1726
|
get assets() {
|
|
1611
1727
|
return this._context.resolve(AssetManagerKey);
|
|
@@ -1730,6 +1846,31 @@ var Scene = class {
|
|
|
1730
1846
|
}
|
|
1731
1847
|
}
|
|
1732
1848
|
// ---- Internal methods ----
|
|
1849
|
+
/**
|
|
1850
|
+
* Register a scene-scoped service. Called from a plugin's `beforeEnter`
|
|
1851
|
+
* hook to make per-scene state (render tree, physics world) resolvable via
|
|
1852
|
+
* `Component.use(key)`.
|
|
1853
|
+
* @internal
|
|
1854
|
+
*/
|
|
1855
|
+
_registerScoped(key, value) {
|
|
1856
|
+
this._scopedServices ??= /* @__PURE__ */ new Map();
|
|
1857
|
+
this._scopedServices.set(key.id, value);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Resolve a scene-scoped service, or `undefined` if none was registered.
|
|
1861
|
+
* @internal
|
|
1862
|
+
*/
|
|
1863
|
+
_resolveScoped(key) {
|
|
1864
|
+
return this._scopedServices?.get(key.id);
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Clear all scene-scoped services. Called by the SceneManager after
|
|
1868
|
+
* `afterExit` hooks run, so plugin cleanup code still sees scoped state.
|
|
1869
|
+
* @internal
|
|
1870
|
+
*/
|
|
1871
|
+
_clearScopedServices() {
|
|
1872
|
+
this._scopedServices?.clear();
|
|
1873
|
+
}
|
|
1733
1874
|
/**
|
|
1734
1875
|
* Set the engine context. Called by SceneManager when the scene is pushed.
|
|
1735
1876
|
* @internal
|
|
@@ -1781,6 +1922,138 @@ var Scene = class {
|
|
|
1781
1922
|
}
|
|
1782
1923
|
};
|
|
1783
1924
|
|
|
1925
|
+
// src/LoadingScene.ts
|
|
1926
|
+
var LoadingScene = class extends Scene {
|
|
1927
|
+
static {
|
|
1928
|
+
__name(this, "LoadingScene");
|
|
1929
|
+
}
|
|
1930
|
+
name = "loading";
|
|
1931
|
+
/**
|
|
1932
|
+
* Minimum wall-clock ms the scene stays visible before handing off.
|
|
1933
|
+
* Prevents flicker on cached loads. Default 0.
|
|
1934
|
+
*/
|
|
1935
|
+
minDuration = 0;
|
|
1936
|
+
/** Transition used for the loading → target handoff. */
|
|
1937
|
+
transition;
|
|
1938
|
+
/**
|
|
1939
|
+
* When true (default), the handoff fires automatically after loading and
|
|
1940
|
+
* `minDuration`. Set false to gate it behind `continue()` — useful when
|
|
1941
|
+
* the loading scene also asks the player to press a key or click.
|
|
1942
|
+
*/
|
|
1943
|
+
autoContinue = true;
|
|
1944
|
+
_progress = 0;
|
|
1945
|
+
_started = false;
|
|
1946
|
+
_active = true;
|
|
1947
|
+
_continueRequested = false;
|
|
1948
|
+
_continueGate;
|
|
1949
|
+
// Bumped on every `_run` attempt. `AssetManager.loadAll` uses `Promise.all`
|
|
1950
|
+
// under the hood, so individual loaders from a failed attempt can still
|
|
1951
|
+
// resolve and fire `onProgress` after the attempt rejects. Without this
|
|
1952
|
+
// guard, a retry kicked off from `onLoadError` would see stale progress
|
|
1953
|
+
// callbacks mutate `_progress` and emit `scene:loading:progress` events
|
|
1954
|
+
// attributed to the current attempt.
|
|
1955
|
+
_attempt = 0;
|
|
1956
|
+
/** Current load progress, 0 → 1. Updated as the AssetManager reports progress. */
|
|
1957
|
+
get progress() {
|
|
1958
|
+
return this._progress;
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Kick off asset loading. While a load is in flight, subsequent calls
|
|
1962
|
+
* are no-ops. After a load failure the guard is released, so calling
|
|
1963
|
+
* `startLoading()` from `onLoadError` (or from a retry button) kicks off
|
|
1964
|
+
* a fresh load against the same target.
|
|
1965
|
+
*
|
|
1966
|
+
* Usually called once from `onEnter` after spawning the loading UI:
|
|
1967
|
+
* ```ts
|
|
1968
|
+
* override onEnter() {
|
|
1969
|
+
* this.spawn(LoadingSceneProgressBar);
|
|
1970
|
+
* this.startLoading();
|
|
1971
|
+
* }
|
|
1972
|
+
* ```
|
|
1973
|
+
*
|
|
1974
|
+
* Deferring the call lets you gate the start of the load behind a
|
|
1975
|
+
* title screen, "press any key" prompt, intro animation, etc.
|
|
1976
|
+
*/
|
|
1977
|
+
startLoading() {
|
|
1978
|
+
if (this._started) return;
|
|
1979
|
+
this._started = true;
|
|
1980
|
+
this._run().catch((err) => {
|
|
1981
|
+
if (!this._active) return;
|
|
1982
|
+
const logger = this.context.tryResolve(LoggerKey);
|
|
1983
|
+
if (logger) {
|
|
1984
|
+
logger.error("LoadingScene", "loading failed", { error: err });
|
|
1985
|
+
} else {
|
|
1986
|
+
console.error("[LoadingScene] loading failed:", err);
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Trigger the handoff to `target`. No-op if already called or if
|
|
1992
|
+
* `autoContinue` already fired it. If called before loading finishes,
|
|
1993
|
+
* the handoff runs as soon as loading + `minDuration` complete.
|
|
1994
|
+
*/
|
|
1995
|
+
continue() {
|
|
1996
|
+
if (this._continueRequested) return;
|
|
1997
|
+
this._continueRequested = true;
|
|
1998
|
+
this._continueGate?.();
|
|
1999
|
+
}
|
|
2000
|
+
onExit() {
|
|
2001
|
+
this._active = false;
|
|
2002
|
+
this._continueGate?.();
|
|
2003
|
+
}
|
|
2004
|
+
async _run() {
|
|
2005
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2006
|
+
if (!this._active) return;
|
|
2007
|
+
const attempt = ++this._attempt;
|
|
2008
|
+
const target = typeof this.target === "function" ? this.target() : this.target;
|
|
2009
|
+
const startedAt = performance.now();
|
|
2010
|
+
const bus = this.context.resolve(EventBusKey);
|
|
2011
|
+
try {
|
|
2012
|
+
await this.assets.loadAll(target.preload ?? [], (ratio) => {
|
|
2013
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2014
|
+
this._progress = ratio;
|
|
2015
|
+
bus.emit("scene:loading:progress", { scene: this, ratio });
|
|
2016
|
+
});
|
|
2017
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2018
|
+
const elapsed = performance.now() - startedAt;
|
|
2019
|
+
const remaining = this.minDuration - elapsed;
|
|
2020
|
+
if (remaining > 0) {
|
|
2021
|
+
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
2022
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2023
|
+
}
|
|
2024
|
+
} catch (err) {
|
|
2025
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2026
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
2027
|
+
this._started = false;
|
|
2028
|
+
this._attempt++;
|
|
2029
|
+
if (this.onLoadError) {
|
|
2030
|
+
await this.onLoadError(error);
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
throw error;
|
|
2034
|
+
}
|
|
2035
|
+
bus.emit("scene:loading:done", { scene: this });
|
|
2036
|
+
if (!this.autoContinue && !this._continueRequested) {
|
|
2037
|
+
await new Promise((resolve) => {
|
|
2038
|
+
this._continueGate = resolve;
|
|
2039
|
+
});
|
|
2040
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2041
|
+
}
|
|
2042
|
+
const scenes = this.context.resolve(SceneManagerKey);
|
|
2043
|
+
await scenes.replace(
|
|
2044
|
+
target,
|
|
2045
|
+
this.transition ? { transition: this.transition } : void 0
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
// src/SceneTransition.ts
|
|
2051
|
+
function resolveTransition(callSite, destination) {
|
|
2052
|
+
if (callSite) return callSite;
|
|
2053
|
+
return destination?.defaultTransition;
|
|
2054
|
+
}
|
|
2055
|
+
__name(resolveTransition, "resolveTransition");
|
|
2056
|
+
|
|
1784
2057
|
// src/SceneManager.ts
|
|
1785
2058
|
var SceneManager = class {
|
|
1786
2059
|
static {
|
|
@@ -1790,6 +2063,12 @@ var SceneManager = class {
|
|
|
1790
2063
|
_context;
|
|
1791
2064
|
bus;
|
|
1792
2065
|
assetManager;
|
|
2066
|
+
hookRegistry;
|
|
2067
|
+
logger;
|
|
2068
|
+
_currentRun;
|
|
2069
|
+
_pendingChain = Promise.resolve();
|
|
2070
|
+
_mutationDepth = 0;
|
|
2071
|
+
_destroyed = false;
|
|
1793
2072
|
/**
|
|
1794
2073
|
* Set the engine context.
|
|
1795
2074
|
* @internal
|
|
@@ -1798,6 +2077,8 @@ var SceneManager = class {
|
|
|
1798
2077
|
this._context = context;
|
|
1799
2078
|
this.bus = context.tryResolve(EventBusKey);
|
|
1800
2079
|
this.assetManager = context.tryResolve(AssetManagerKey);
|
|
2080
|
+
this.hookRegistry = context.tryResolve(SceneHookRegistryKey);
|
|
2081
|
+
this.logger = context.tryResolve(LoggerKey);
|
|
1801
2082
|
}
|
|
1802
2083
|
/** The topmost (active) scene. */
|
|
1803
2084
|
get active() {
|
|
@@ -1809,84 +2090,129 @@ var SceneManager = class {
|
|
|
1809
2090
|
}
|
|
1810
2091
|
/** All non-paused scenes in the stack, bottom to top. */
|
|
1811
2092
|
get activeScenes() {
|
|
1812
|
-
return this.stack.filter((
|
|
2093
|
+
return this.stack.filter((scene) => !scene.isPaused);
|
|
2094
|
+
}
|
|
2095
|
+
/** Whether a scene transition is currently running. */
|
|
2096
|
+
get isTransitioning() {
|
|
2097
|
+
return this._currentRun !== void 0;
|
|
1813
2098
|
}
|
|
1814
2099
|
/**
|
|
1815
2100
|
* Push a scene onto the stack. Scenes below may receive onPause().
|
|
1816
2101
|
* If the scene declares a `preload` array, assets are loaded before onEnter().
|
|
1817
|
-
* Await the returned promise when using preloaded scenes.
|
|
1818
2102
|
*/
|
|
1819
|
-
push(scene) {
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
scene.onEnter?.();
|
|
1831
|
-
this.bus?.emit("scene:pushed", { scene });
|
|
1832
|
-
return Promise.resolve();
|
|
2103
|
+
async push(scene, opts) {
|
|
2104
|
+
this._assertNotMutating("push");
|
|
2105
|
+
await this._enqueue(async () => {
|
|
2106
|
+
const fromScene = this.active;
|
|
2107
|
+
await this._pushScene(scene);
|
|
2108
|
+
const transition = resolveTransition(opts?.transition, scene);
|
|
2109
|
+
if (!transition) return;
|
|
2110
|
+
await this._runTransition("push", transition, fromScene, scene);
|
|
2111
|
+
});
|
|
1833
2112
|
}
|
|
1834
2113
|
/** Pop the top scene. Scenes below may receive onResume(). */
|
|
1835
|
-
pop() {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2114
|
+
async pop(opts) {
|
|
2115
|
+
this._assertNotMutating("pop");
|
|
2116
|
+
return this._enqueue(async () => {
|
|
2117
|
+
if (this.stack.length === 0) return void 0;
|
|
2118
|
+
const fromScene = this.active;
|
|
2119
|
+
const destination = this.stack.length > 1 ? this.stack[this.stack.length - 2] : void 0;
|
|
2120
|
+
const transition = resolveTransition(opts?.transition, destination);
|
|
2121
|
+
if (transition) {
|
|
2122
|
+
await this._runTransition("pop", transition, fromScene, destination);
|
|
2123
|
+
}
|
|
2124
|
+
return this._popScene();
|
|
2125
|
+
});
|
|
1844
2126
|
}
|
|
1845
2127
|
/**
|
|
1846
|
-
* Replace the top scene.
|
|
1847
|
-
*
|
|
2128
|
+
* Replace the top scene. Without a transition the old scene exits first,
|
|
2129
|
+
* then the new scene enters. With a transition the new scene is pushed
|
|
2130
|
+
* first, both scenes coexist for the transition duration, then the old
|
|
2131
|
+
* scene is removed at the end.
|
|
1848
2132
|
*/
|
|
1849
|
-
replace(scene) {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
2133
|
+
async replace(scene, opts) {
|
|
2134
|
+
this._assertNotMutating("replace");
|
|
2135
|
+
await this._enqueue(async () => {
|
|
2136
|
+
const transition = resolveTransition(opts?.transition, scene);
|
|
2137
|
+
if (!transition) {
|
|
2138
|
+
await this._replaceScene(scene);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
const old = this.active;
|
|
2142
|
+
await this._pushScene(scene, true);
|
|
2143
|
+
await this._runTransition("replace", transition, old, scene);
|
|
2144
|
+
if (old) {
|
|
2145
|
+
this._removeScene(old, true);
|
|
2146
|
+
}
|
|
2147
|
+
this.bus?.emit("scene:replaced", {
|
|
2148
|
+
oldScene: old ?? scene,
|
|
2149
|
+
newScene: scene
|
|
2150
|
+
});
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Pop every scene on the stack, top to bottom. Each receives onExit().
|
|
2155
|
+
* Queued like push/pop/replace — runs after any in-flight transition.
|
|
2156
|
+
* Use for "restart from menu"-style flows. Does not run transitions.
|
|
2157
|
+
*/
|
|
2158
|
+
async popAll() {
|
|
2159
|
+
this._assertNotMutating("popAll");
|
|
2160
|
+
await this._enqueue(async () => {
|
|
2161
|
+
this._withMutationSync(() => {
|
|
2162
|
+
while (this.stack.length > 0) {
|
|
2163
|
+
const scene = this.stack.pop();
|
|
2164
|
+
if (!scene) break;
|
|
2165
|
+
this._teardownScene(scene);
|
|
2166
|
+
this.bus?.emit("scene:popped", { scene });
|
|
1870
2167
|
}
|
|
1871
2168
|
});
|
|
1872
|
-
}
|
|
1873
|
-
scene.onEnter?.();
|
|
1874
|
-
if (old) {
|
|
1875
|
-
this.bus?.emit("scene:replaced", { oldScene: old, newScene: scene });
|
|
1876
|
-
} else {
|
|
1877
|
-
this.bus?.emit("scene:pushed", { scene });
|
|
1878
|
-
}
|
|
1879
|
-
return Promise.resolve();
|
|
2169
|
+
});
|
|
1880
2170
|
}
|
|
1881
|
-
/**
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
2171
|
+
/**
|
|
2172
|
+
* Run the full scene-enter lifecycle (beforeEnter hooks, preload, onEnter)
|
|
2173
|
+
* for a scene that is NOT placed on the stack. Used by infrastructure
|
|
2174
|
+
* plugins like DebugPlugin that render a scene off-stack.
|
|
2175
|
+
* @internal
|
|
2176
|
+
*/
|
|
2177
|
+
async _mountDetached(scene) {
|
|
2178
|
+
await this._withMutation(async () => {
|
|
2179
|
+
scene._setContext(this._context);
|
|
2180
|
+
await this.hookRegistry?.runBeforeEnter(scene);
|
|
2181
|
+
await this._preloadScene(scene);
|
|
2182
|
+
scene.onEnter?.();
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* Run the scene-exit lifecycle (onExit, entity destruction, afterExit
|
|
2187
|
+
* hooks, scoped-service clear) for a detached scene.
|
|
2188
|
+
* @internal
|
|
2189
|
+
*/
|
|
2190
|
+
_unmountDetached(scene) {
|
|
2191
|
+
this._withMutationSync(() => {
|
|
2192
|
+
this._teardownScene(scene);
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Mark the manager destroyed and synchronously tear down every scene.
|
|
2197
|
+
* Called by Engine.destroy(). Any queued async work short-circuits on
|
|
2198
|
+
* resume; in-flight transitions' pending promises are resolved via
|
|
2199
|
+
* _cleanupRun so they don't leak.
|
|
2200
|
+
* @internal
|
|
2201
|
+
*/
|
|
2202
|
+
_destroy() {
|
|
2203
|
+
this._destroyed = true;
|
|
2204
|
+
if (this._currentRun) {
|
|
2205
|
+
this._cleanupRun(this._currentRun);
|
|
2206
|
+
}
|
|
2207
|
+
this._pendingChain = Promise.resolve();
|
|
2208
|
+
this._withMutationSync(() => {
|
|
2209
|
+
while (this.stack.length > 0) {
|
|
2210
|
+
const scene = this.stack.pop();
|
|
2211
|
+
if (!scene) break;
|
|
2212
|
+
this._teardownScene(scene);
|
|
2213
|
+
this.bus?.emit("scene:popped", { scene });
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
1890
2216
|
}
|
|
1891
2217
|
/**
|
|
1892
2218
|
* Flush destroy queues for all active scenes.
|
|
@@ -1898,21 +2224,216 @@ var SceneManager = class {
|
|
|
1898
2224
|
scene._flushDestroyQueue();
|
|
1899
2225
|
}
|
|
1900
2226
|
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Advance the active transition by `dt` ms. Called by Engine's earlyUpdate
|
|
2229
|
+
* callback with raw (unscaled) wall-clock dt.
|
|
2230
|
+
* @internal
|
|
2231
|
+
*/
|
|
2232
|
+
_tickTransition(dt) {
|
|
2233
|
+
const run = this._currentRun;
|
|
2234
|
+
if (!run) return;
|
|
2235
|
+
const remaining = run.transition.duration - run.elapsed;
|
|
2236
|
+
const consume = Math.min(dt, remaining);
|
|
2237
|
+
run.elapsed += consume;
|
|
2238
|
+
this._safeTick(run, consume);
|
|
2239
|
+
if (run.elapsed >= run.transition.duration) {
|
|
2240
|
+
this._cleanupRun(run);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
// ---- Private helpers ----
|
|
2244
|
+
_enqueue(work) {
|
|
2245
|
+
if (this._destroyed) return Promise.resolve(void 0);
|
|
2246
|
+
const next = this._pendingChain.then(async () => {
|
|
2247
|
+
if (this._destroyed) return void 0;
|
|
2248
|
+
return work();
|
|
2249
|
+
});
|
|
2250
|
+
this._pendingChain = next.then(
|
|
2251
|
+
() => void 0,
|
|
2252
|
+
() => void 0
|
|
2253
|
+
);
|
|
2254
|
+
return next;
|
|
2255
|
+
}
|
|
2256
|
+
async _pushScene(scene, suppressEvent = false) {
|
|
2257
|
+
const wasPaused = this._snapshotPauseStates();
|
|
2258
|
+
await this._withMutation(async () => {
|
|
2259
|
+
scene._setContext(this._context);
|
|
2260
|
+
await this.hookRegistry?.runBeforeEnter(scene);
|
|
2261
|
+
await this._preloadScene(scene);
|
|
2262
|
+
this.stack.push(scene);
|
|
2263
|
+
scene.onEnter?.();
|
|
2264
|
+
this._firePauseTransitions(wasPaused);
|
|
2265
|
+
if (!suppressEvent) {
|
|
2266
|
+
this.bus?.emit("scene:pushed", { scene });
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
_popScene(suppressEvent = false) {
|
|
2271
|
+
const wasPaused = this._snapshotPauseStates();
|
|
2272
|
+
return this._withMutationSync(() => {
|
|
2273
|
+
const removed = this.stack.pop();
|
|
2274
|
+
if (!removed) return void 0;
|
|
2275
|
+
this._teardownScene(removed);
|
|
2276
|
+
this._fireResumeTransitions(wasPaused);
|
|
2277
|
+
if (!suppressEvent) {
|
|
2278
|
+
this.bus?.emit("scene:popped", { scene: removed });
|
|
2279
|
+
}
|
|
2280
|
+
return removed;
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
async _replaceScene(scene) {
|
|
2284
|
+
const wasPaused = this._snapshotPauseStates();
|
|
2285
|
+
await this._withMutation(async () => {
|
|
2286
|
+
scene._setContext(this._context);
|
|
2287
|
+
await this.hookRegistry?.runBeforeEnter(scene);
|
|
2288
|
+
await this._preloadScene(scene);
|
|
2289
|
+
const old = this.stack.pop();
|
|
2290
|
+
if (old) this._teardownScene(old);
|
|
2291
|
+
this.stack.push(scene);
|
|
2292
|
+
scene.onEnter?.();
|
|
2293
|
+
this._firePauseTransitions(wasPaused);
|
|
2294
|
+
this._fireResumeTransitions(wasPaused);
|
|
2295
|
+
this.bus?.emit("scene:replaced", {
|
|
2296
|
+
oldScene: old ?? scene,
|
|
2297
|
+
newScene: scene
|
|
2298
|
+
});
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
_removeScene(scene, suppressEvent = false) {
|
|
2302
|
+
this._withMutationSync(() => {
|
|
2303
|
+
const idx = this.stack.indexOf(scene);
|
|
2304
|
+
if (idx === -1) return;
|
|
2305
|
+
const wasPaused = this._snapshotPauseStates();
|
|
2306
|
+
this.stack.splice(idx, 1);
|
|
2307
|
+
this._teardownScene(scene);
|
|
2308
|
+
this._firePauseTransitions(wasPaused);
|
|
2309
|
+
this._fireResumeTransitions(wasPaused);
|
|
2310
|
+
if (!suppressEvent) {
|
|
2311
|
+
this.bus?.emit("scene:popped", { scene });
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
async _preloadScene(scene) {
|
|
2316
|
+
if (!scene.preload?.length || !this.assetManager) return;
|
|
2317
|
+
await this.assetManager.loadAll(
|
|
2318
|
+
scene.preload,
|
|
2319
|
+
scene.onProgress?.bind(scene)
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
_teardownScene(scene) {
|
|
2323
|
+
scene.onExit?.();
|
|
2324
|
+
scene._destroyAllEntities();
|
|
2325
|
+
this.hookRegistry?.runAfterExit(scene);
|
|
2326
|
+
scene._clearScopedServices();
|
|
2327
|
+
}
|
|
2328
|
+
async _runTransition(kind, transition, fromScene, toScene) {
|
|
2329
|
+
if (this._destroyed) return;
|
|
2330
|
+
let resolveRun;
|
|
2331
|
+
const promise = new Promise((resolve) => {
|
|
2332
|
+
resolveRun = resolve;
|
|
2333
|
+
});
|
|
2334
|
+
const run = {
|
|
2335
|
+
kind,
|
|
2336
|
+
transition,
|
|
2337
|
+
elapsed: 0,
|
|
2338
|
+
fromScene,
|
|
2339
|
+
toScene,
|
|
2340
|
+
resolve: resolveRun
|
|
2341
|
+
};
|
|
2342
|
+
this._currentRun = run;
|
|
2343
|
+
this.bus?.emit("scene:transition:started", {
|
|
2344
|
+
kind,
|
|
2345
|
+
fromScene,
|
|
2346
|
+
toScene
|
|
2347
|
+
});
|
|
2348
|
+
this._safeCall(run, "begin");
|
|
2349
|
+
if (!Number.isFinite(transition.duration) || transition.duration <= 0) {
|
|
2350
|
+
this._cleanupRun(run);
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
await promise;
|
|
2354
|
+
}
|
|
2355
|
+
_cleanupRun(run) {
|
|
2356
|
+
if (this._currentRun !== run) return;
|
|
2357
|
+
this._safeCall(run, "end");
|
|
2358
|
+
this._currentRun = void 0;
|
|
2359
|
+
this.bus?.emit("scene:transition:ended", {
|
|
2360
|
+
kind: run.kind,
|
|
2361
|
+
fromScene: run.fromScene,
|
|
2362
|
+
toScene: run.toScene
|
|
2363
|
+
});
|
|
2364
|
+
run.resolve();
|
|
2365
|
+
}
|
|
2366
|
+
_safeTick(run, dt) {
|
|
2367
|
+
try {
|
|
2368
|
+
run.transition.tick(dt, this._makeContext(run));
|
|
2369
|
+
} catch (err) {
|
|
2370
|
+
this.logger?.warn(
|
|
2371
|
+
"SceneManager",
|
|
2372
|
+
`Transition tick error: ${err instanceof Error ? err.message : String(err)}`
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
_safeCall(run, method) {
|
|
2377
|
+
try {
|
|
2378
|
+
run.transition[method]?.(this._makeContext(run));
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
this.logger?.warn(
|
|
2381
|
+
"SceneManager",
|
|
2382
|
+
`Transition ${method} error: ${err instanceof Error ? err.message : String(err)}`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
_makeContext(run) {
|
|
2387
|
+
return {
|
|
2388
|
+
elapsed: run.elapsed,
|
|
2389
|
+
kind: run.kind,
|
|
2390
|
+
engineContext: this._context,
|
|
2391
|
+
fromScene: run.fromScene,
|
|
2392
|
+
toScene: run.toScene
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
_snapshotPauseStates() {
|
|
2396
|
+
return new Map(
|
|
2397
|
+
this.stack.map((scene) => [scene, scene.isPaused])
|
|
2398
|
+
);
|
|
2399
|
+
}
|
|
2400
|
+
_assertNotMutating(method) {
|
|
2401
|
+
if (this._mutationDepth === 0) return;
|
|
2402
|
+
throw new Error(
|
|
2403
|
+
`SceneManager.${method}() called reentrantly from a scene lifecycle hook (onEnter/onExit/onPause/onResume or a beforeEnter/afterExit hook). Defer the call outside the hook, e.g. via queueMicrotask() or from a component update().`
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
async _withMutation(work) {
|
|
2407
|
+
this._mutationDepth++;
|
|
2408
|
+
try {
|
|
2409
|
+
return await work();
|
|
2410
|
+
} finally {
|
|
2411
|
+
this._mutationDepth--;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
_withMutationSync(work) {
|
|
2415
|
+
this._mutationDepth++;
|
|
2416
|
+
try {
|
|
2417
|
+
return work();
|
|
2418
|
+
} finally {
|
|
2419
|
+
this._mutationDepth--;
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
1901
2422
|
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
1902
2423
|
_firePauseTransitions(wasPaused) {
|
|
1903
|
-
for (const
|
|
1904
|
-
const was = wasPaused.get(
|
|
1905
|
-
if (
|
|
1906
|
-
|
|
2424
|
+
for (const scene of this.stack) {
|
|
2425
|
+
const was = wasPaused.get(scene) ?? false;
|
|
2426
|
+
if (scene.isPaused && !was) {
|
|
2427
|
+
scene.onPause?.();
|
|
1907
2428
|
}
|
|
1908
2429
|
}
|
|
1909
2430
|
}
|
|
1910
2431
|
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
1911
2432
|
_fireResumeTransitions(wasPaused) {
|
|
1912
|
-
for (const
|
|
1913
|
-
const was = wasPaused.get(
|
|
1914
|
-
if (!
|
|
1915
|
-
|
|
2433
|
+
for (const scene of this.stack) {
|
|
2434
|
+
const was = wasPaused.get(scene) ?? false;
|
|
2435
|
+
if (!scene.isPaused && was) {
|
|
2436
|
+
scene.onResume?.();
|
|
1916
2437
|
}
|
|
1917
2438
|
}
|
|
1918
2439
|
}
|
|
@@ -2749,6 +3270,7 @@ var Engine = class {
|
|
|
2749
3270
|
scheduler;
|
|
2750
3271
|
errorBoundary;
|
|
2751
3272
|
queryCache;
|
|
3273
|
+
sceneHooks;
|
|
2752
3274
|
/** The asset manager. */
|
|
2753
3275
|
assets;
|
|
2754
3276
|
plugins = /* @__PURE__ */ new Map();
|
|
@@ -2767,6 +3289,7 @@ var Engine = class {
|
|
|
2767
3289
|
this.scheduler = new SystemScheduler();
|
|
2768
3290
|
this.inspector = new Inspector(this);
|
|
2769
3291
|
this.assets = new AssetManager();
|
|
3292
|
+
this.sceneHooks = new SceneHookRegistry();
|
|
2770
3293
|
this.scheduler.setErrorBoundary(this.errorBoundary);
|
|
2771
3294
|
this.context.register(EngineKey, this);
|
|
2772
3295
|
this.context.register(EventBusKey, this.events);
|
|
@@ -2778,11 +3301,13 @@ var Engine = class {
|
|
|
2778
3301
|
this.context.register(InspectorKey, this.inspector);
|
|
2779
3302
|
this.context.register(SystemSchedulerKey, this.scheduler);
|
|
2780
3303
|
this.context.register(AssetManagerKey, this.assets);
|
|
3304
|
+
this.context.register(SceneHookRegistryKey, this.sceneHooks);
|
|
2781
3305
|
this.scenes._setContext(this.context);
|
|
2782
3306
|
this.registerBuiltInSystems();
|
|
2783
3307
|
this.loop.setCallbacks({
|
|
2784
3308
|
earlyUpdate: /* @__PURE__ */ __name((dt) => {
|
|
2785
3309
|
this.logger.setFrame(this.loop.frameCount);
|
|
3310
|
+
this.scenes._tickTransition(dt);
|
|
2786
3311
|
this.scheduler.run("earlyUpdate" /* EarlyUpdate */, dt);
|
|
2787
3312
|
}, "earlyUpdate"),
|
|
2788
3313
|
fixedUpdate: /* @__PURE__ */ __name((dt) => this.scheduler.run("fixedUpdate" /* FixedUpdate */, dt), "fixedUpdate"),
|
|
@@ -2795,6 +3320,14 @@ var Engine = class {
|
|
|
2795
3320
|
}, "endOfFrame")
|
|
2796
3321
|
});
|
|
2797
3322
|
}
|
|
3323
|
+
/**
|
|
3324
|
+
* Register scene lifecycle hooks. The returned function unregisters the
|
|
3325
|
+
* hooks. Infrastructure plugins (renderer, physics, debug) register hooks
|
|
3326
|
+
* in their `install` or `onStart` to set up and tear down per-scene state.
|
|
3327
|
+
*/
|
|
3328
|
+
registerSceneHooks(hooks) {
|
|
3329
|
+
return this.sceneHooks.register(hooks);
|
|
3330
|
+
}
|
|
2798
3331
|
/** Register a plugin. Must be called before start(). */
|
|
2799
3332
|
use(plugin) {
|
|
2800
3333
|
if (this.started) {
|
|
@@ -2830,7 +3363,7 @@ var Engine = class {
|
|
|
2830
3363
|
};
|
|
2831
3364
|
}
|
|
2832
3365
|
for (const plugin of sorted) {
|
|
2833
|
-
plugin.onStart?.();
|
|
3366
|
+
await plugin.onStart?.();
|
|
2834
3367
|
}
|
|
2835
3368
|
this.events.emit("engine:started", void 0);
|
|
2836
3369
|
}
|
|
@@ -2838,7 +3371,7 @@ var Engine = class {
|
|
|
2838
3371
|
destroy() {
|
|
2839
3372
|
this.events.emit("engine:stopped", void 0);
|
|
2840
3373
|
this.loop.stop();
|
|
2841
|
-
this.scenes.
|
|
3374
|
+
this.scenes._destroy();
|
|
2842
3375
|
const allSystems = this.scheduler.getAllSystems();
|
|
2843
3376
|
for (let i = allSystems.length - 1; i >= 0; i--) {
|
|
2844
3377
|
allSystems[i].onUnregister?.();
|
|
@@ -2982,6 +3515,7 @@ var VERSION = "0.0.0";
|
|
|
2982
3515
|
Inspector,
|
|
2983
3516
|
InspectorKey,
|
|
2984
3517
|
KeyframeAnimator,
|
|
3518
|
+
LoadingScene,
|
|
2985
3519
|
LogLevel,
|
|
2986
3520
|
Logger,
|
|
2987
3521
|
LoggerKey,
|
|
@@ -2997,6 +3531,8 @@ var VERSION = "0.0.0";
|
|
|
2997
3531
|
QueryResult,
|
|
2998
3532
|
SERIALIZABLE_KEY,
|
|
2999
3533
|
Scene,
|
|
3534
|
+
SceneHookRegistry,
|
|
3535
|
+
SceneHookRegistryKey,
|
|
3000
3536
|
SceneManager,
|
|
3001
3537
|
SceneManagerKey,
|
|
3002
3538
|
Sequence,
|
|
@@ -3029,6 +3565,7 @@ var VERSION = "0.0.0";
|
|
|
3029
3565
|
getSerializableType,
|
|
3030
3566
|
interpolate,
|
|
3031
3567
|
isSerializable,
|
|
3568
|
+
resolveTransition,
|
|
3032
3569
|
serializable,
|
|
3033
3570
|
trait
|
|
3034
3571
|
});
|