diff --git a/CHANGELOG.md b/CHANGELOG.md index d829d138..90b4a460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Fixed issue where the initial scene onPreLoad was not being run - Fixed unecessary coupling with `ex.ColliderComponent`/`ex.BodyComponent` that prevented collider tracking on entities that have `ex.TransformComponent`/`ex.ColliderComponent`, this influenced users doing Entity level ECS with pointer events. - Fixed issue where passing 0 to `ex.Sound.play(0)` would not set the volume to 0, instead the previous volume would play. - Fixed issue where the Actor.color did not respect being set diff --git a/sandbox/tests/onpreload/index.html b/sandbox/tests/onpreload/index.html new file mode 100644 index 00000000..6ad9a3b0 --- /dev/null +++ b/sandbox/tests/onpreload/index.html @@ -0,0 +1,16 @@ + + + + + + Scene onPreload + + + Console should read +

Pre loading empty scene

+

Initializing empty scene

+

Pre loading empty scene

+

Initializing empty scene

+ + + diff --git a/sandbox/tests/onpreload/index.ts b/sandbox/tests/onpreload/index.ts new file mode 100644 index 00000000..b9541d99 --- /dev/null +++ b/sandbox/tests/onpreload/index.ts @@ -0,0 +1,40 @@ +export function delay(milliseconds: number, clock?: ex.Clock): Promise { + const future = new ex.Future(); + const schedule = clock?.schedule.bind(clock) ?? setTimeout; + schedule(() => { + future.resolve(); + }, milliseconds); + return future.promise; +} + +class EmptyScene extends ex.Scene { + constructor(public name: string) { + super(); + } + override onInitialize(engine: ex.Engine): void { + console.log(`Initializing ${this.name} scene`); + + setTimeout(() => { + engine.goToScene('level2'); + }, 5000); + } + + override onPreLoad(loader: ex.DefaultLoader): void { + console.log(`Pre loading ${this.name} scene`); + const image = new ex.ImageSource('https://cdn.rawgit.com/excaliburjs/Excalibur/7dd48128/assets/sword.png'); + loader.addResource(image); + } +} + +const game = new ex.Engine({ + width: 800, + height: 600, + scenes: { + start: EmptyScene, + level2: EmptyScene + //start: new EmptyScene('start'), + //level2: new EmptyScene('level2'), + } +}); + +game.start('start'); diff --git a/src/engine/Director/Director.ts b/src/engine/Director/Director.ts index b52befd4..6f9b6e5b 100644 --- a/src/engine/Director/Director.ts +++ b/src/engine/Director/Director.ts @@ -188,6 +188,10 @@ export class Director { if (deferredSceneInstance && deferredTransition) { deferredTransition._addToTargetScene(this._engine, deferredSceneInstance); } + + const inTransition = this._getInTransition(deferredScene); + const hideLoader = inTransition?.hideLoader; + this.maybeLoadScene(deferredScene, hideLoader); await this.swapScene(deferredScene); if (deferredSceneInstance && deferredTransition) { await this.playTransition(deferredTransition, deferredSceneInstance); @@ -228,13 +232,16 @@ export class Director { this.startScene = startScene; + const maybeHideLoader = options?.inTransition?.hideLoader ?? false; + this.maybeLoadScene(startScene, maybeHideLoader); + // Fire and forget promise for the initial scene if (maybeStartTransition) { - const startSceneInstance = this.getSceneInstance(this.startScene); + const startSceneInstance = this.getSceneInstance(startScene); if (startSceneInstance) { // eslint-disable-next-line @typescript-eslint/no-floating-promises maybeStartTransition._addToTargetScene(this._engine, startSceneInstance); - this.swapScene(this.startScene).then(() => { + this.swapScene(startScene).then(() => { startSceneInstance.onTransition('in'); // eslint-disable-next-line @typescript-eslint/no-floating-promises return this.playTransition(maybeStartTransition, startSceneInstance); @@ -242,7 +249,7 @@ export class Director { } } else { // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.swapScene(this.startScene); + this.swapScene(startScene); } this.currentSceneName = this.startScene; @@ -558,6 +565,8 @@ export class Director { /** * Swaps the current and destination scene after performing required lifecycle events + * + * Note: swap scene will wait for any pending loader on the destination scene * @param destinationScene * @param data */ diff --git a/src/engine/Scene.ts b/src/engine/Scene.ts index e1d87754..cfea6f00 100644 --- a/src/engine/Scene.ts +++ b/src/engine/Scene.ts @@ -88,6 +88,17 @@ export function isSceneConstructor(x: any): x is SceneConstructor { * actors in a scene. Only actors in scenes will be updated and drawn. * * Typical usages of a scene include: levels, menus, loading screens, etc. + * + * Scenes go through the following lifecycle + * 1. onPreLoad - called once + * 2. onInitialize - called once + * 3. onActivate - called the first frame the scene is current + * 4. onPreUpdate - called every update + * 5. onPostUpdate - called every update + * 6. onPreDraw - called every draw + * 7. onPostDraw - called every draw + * 8. onDeactivate - called teh first frame thescene is no longer current + * */ export class Scene implements CanInitialize, CanActivate, CanDeactivate, CanUpdate, CanDraw { private _logger: Logger = Logger.getInstance();