From 7772a0cdb4e9cbae4c50c5515d9449a6e7148cc9 Mon Sep 17 00:00:00 2001 From: Torben Schweren Date: Mon, 19 Jun 2023 02:28:50 +0200 Subject: [PATCH] Merge branch bugfix/infinite-asset-loading-loop into main (#102) * Fix infinite asset loading - Implement events for asset loading in AssetLoader - Adapt main game class to new AssetLoader events. --- src/ts/engine/assets/assetloader.ts | 107 +++++++++++++++++++++++----- src/ts/engine/game.ts | 41 ++++++----- src/ts/game/main.ts | 24 ++++++- src/ts/game/scenes/leveloverview.ts | 2 +- src/ts/game/scenes/mainmenu.ts | 3 +- 5 files changed, 137 insertions(+), 40 deletions(-) diff --git a/src/ts/engine/assets/assetloader.ts b/src/ts/engine/assets/assetloader.ts index af9e9ba..a7f1478 100644 --- a/src/ts/engine/assets/assetloader.ts +++ b/src/ts/engine/assets/assetloader.ts @@ -1,19 +1,63 @@ +import EngineEvent from "../event/engineevent.js"; +import EngineEventHandler from "../event/engineventhandler.js"; import AssetVerifier from "./assetverifier.js"; class AssetLoader { private _images: Map; private _audio: Map; - constructor() { + private _imagesLoadedEvent: EngineEventHandler>; + private _audiosLoadedEvent: EngineEventHandler>; + private _assetsLoadedEvent: EngineEventHandler>; + + private _imageLoadTrackerTask: number | undefined; + private _audioLoadTrackerTask: number | undefined; + private _assetLoadTrackerTask: number | undefined; + + public get imagesLoadedEvent(): EngineEventHandler< + boolean, + EngineEvent + > { + return this._imagesLoadedEvent; + } + + public get audiosLoadedEvent(): EngineEventHandler< + boolean, + EngineEvent + > { + return this._audiosLoadedEvent; + } + + public get assetsLoadedEvent(): EngineEventHandler< + boolean, + EngineEvent + > { + return this._assetsLoadedEvent; + } + + public constructor() { this._images = new Map(); this._audio = new Map(); - } - hasImage(key: string): boolean { + this._imagesLoadedEvent = new EngineEventHandler< + boolean, + EngineEvent + >(); + this._audiosLoadedEvent = new EngineEventHandler< + boolean, + EngineEvent + >(); + this._assetsLoadedEvent = new EngineEventHandler< + boolean, + EngineEvent + >(); + } + + public hasImage(key: string): boolean { return this._images.has(key); } - getImage(key: string): HTMLImageElement { + public getImage(key: string): HTMLImageElement { if (!this.hasImage(key)) { throw new Error(`Image with key ${key} does not exist!`); } @@ -21,58 +65,58 @@ class AssetLoader { return this._images.get(key) as HTMLImageElement; } - getImagesCount(): number { + public getImagesCount(): number { return this._images.size; } - getImagesReadyCount(): number { + public getImagesReadyCount(): number { return Array.from(this._images.values()) .map((image) => AssetVerifier.verifyImage(image)) .filter((status) => status === true).length; } - areImagesReady(): boolean { + public areImagesReady(): boolean { return this.getImagesCount() === this.getImagesReadyCount(); } - hasAudio(key: string): boolean { + public hasAudio(key: string): boolean { return this._audio.has(key); } - getAudio(key: string): HTMLAudioElement { + public getAudio(key: string): HTMLAudioElement { if (!this.hasAudio(key)) { throw new Error(`Audio with key ${key} does not exist!`); } return this._audio.get(key) as HTMLAudioElement; } - getAudiosCount(): number { + public getAudiosCount(): number { return this._audio.size; } - getAudiosReadyCount(): number { + public getAudiosReadyCount(): number { return Array.from(this._audio.values()) .map((audio) => AssetVerifier.verifyAudio(audio)) .filter((status) => status === true).length; } - areAudiosReady(): boolean { + public areAudiosReady(): boolean { return this.getAudiosCount() === this.getAudiosReadyCount(); } - getAssetsCount(): number { + public getAssetsCount(): number { return this.getImagesCount() + this.getAudiosCount(); } - getAssetsReadyCount(): number { + public getAssetsReadyCount(): number { return this.getImagesReadyCount() + this.getAudiosReadyCount(); } - areAssetsReady(): boolean { + public areAssetsReady(): boolean { return this.getAssetsCount() === this.getAssetsReadyCount(); } - registerImages(className: string) { + public registerImages(className: string) { let images = document.getElementsByClassName(className); if (images.length == 0) { console.warn(`No images found for class ${className}`); @@ -92,7 +136,7 @@ class AssetLoader { } } - registerAudios(className: string) { + public registerAudios(className: string) { let audio = document.getElementsByClassName(className); if (audio.length == 0) { console.warn(`No audios found for class ${className}`); @@ -111,6 +155,35 @@ class AssetLoader { this._audio.set(audioElement.id, audioElement); } } + + public startTracking() { + this._imageLoadTrackerTask = setInterval(() => { + let imagesReady = this.areImagesReady(); + this._imagesLoadedEvent.dispatch(new EngineEvent(imagesReady)); + + if (imagesReady) { + clearInterval(this._imageLoadTrackerTask); + } + }, 500); + + this._audioLoadTrackerTask = setInterval(() => { + let audiosReady = this.areAudiosReady(); + this._audiosLoadedEvent.dispatch(new EngineEvent(audiosReady)); + + if (audiosReady) { + clearInterval(this._audioLoadTrackerTask); + } + }, 500); + + this._assetLoadTrackerTask = setInterval(() => { + let assetsReady = this.areAssetsReady(); + this._assetsLoadedEvent.dispatch(new EngineEvent(assetsReady)); + + if (assetsReady) { + clearInterval(this._assetLoadTrackerTask); + } + }, 500); + } } export default AssetLoader; diff --git a/src/ts/engine/game.ts b/src/ts/engine/game.ts index 8cc833e..11e53fb 100644 --- a/src/ts/engine/game.ts +++ b/src/ts/engine/game.ts @@ -5,6 +5,7 @@ import CookieManager from "./cookies/cookiemanager.js"; import Services from "./dependencyinjection/services.js"; import EngineSetup from "./enginesetup.js"; import EntityManager from "./entitiy/entitymanager.js"; +import EngineEvent from "./event/engineevent.js"; import InputHandler from "./input/inputhandler.js"; import LevelManager from "./level/levelmanager.js"; import Compositor from "./renderer/compositor.js"; @@ -93,25 +94,31 @@ class Game { this._levelManager ); - while (!this._assetLoader.areAssetsReady()) { - console.log("Loading assets..."); - } - - engineSetup.registerTextures( - this._assetLoader, - this._assetManager, - this._entityManager, - this._levelManager - ); - - engineSetup.registerLevels( - this._assetLoader, - this._assetManager, - this._entityManager, - this._levelManager + this._assetLoader.assetsLoadedEvent.subscribe( + (engineEvent: EngineEvent) => { + if (!engineEvent.eventData) { + console.log("Loading assets..."); + } else { + engineSetup.registerTextures( + this._assetLoader, + this._assetManager, + this._entityManager, + this._levelManager + ); + + engineSetup.registerLevels( + this._assetLoader, + this._assetManager, + this._entityManager, + this._levelManager + ); + + engineSetup.registerScenes(this._sceneManager); + } + } ); - engineSetup.registerScenes(this._sceneManager); + this._assetLoader.startTracking(); } public startUpdateLoop(): void { diff --git a/src/ts/game/main.ts b/src/ts/game/main.ts index 86a4387..107d16d 100644 --- a/src/ts/game/main.ts +++ b/src/ts/game/main.ts @@ -6,6 +6,7 @@ import SpriteSheet from "../engine/assets/texture/spritesheet.js"; import Cookie from "../engine/cookies/cookie.js"; import CookieManager from "../engine/cookies/cookiemanager.js"; import Services from "../engine/dependencyinjection/services.js"; +import EngineEvent from "../engine/event/engineevent.js"; import Game from "../engine/game.js"; import InputHandler from "../engine/input/inputhandler.js"; import LevelManager from "../engine/level/levelmanager.js"; @@ -28,17 +29,34 @@ function getGameCanvas(): HTMLCanvasElement { return canvas; } -function start(): void { +function init(): void { let htmlCanvasElement: HTMLCanvasElement = getGameCanvas(); let game: Game = new Game(htmlCanvasElement, new GameSetup()); + window.addEventListener("beforeunload", game.stopGame.bind(game)); let inputHandler: InputHandler = Services.resolve("InputHandler"); inputHandler.addWhiteListedKeys(["F5", "F11", "F12", "Alt"]); - game.startGame(); + let assetLoader = Services.resolve("AssetLoader"); + + if (!assetLoader.areAssetsReady()) { + assetLoader.assetsLoadedEvent.subscribe( + (engineEvent: EngineEvent) => { + if (engineEvent.eventData) { + start(game); + } + } + ); + } else { + start(game); + } +} + +function start(game: Game): void { Services.resolve("SceneManager").switch("mainmenu"); + game.startGame(); } -window.addEventListener("DOMContentLoaded", start); +window.addEventListener("DOMContentLoaded", init); diff --git a/src/ts/game/scenes/leveloverview.ts b/src/ts/game/scenes/leveloverview.ts index 76e3cc6..3e555b0 100644 --- a/src/ts/game/scenes/leveloverview.ts +++ b/src/ts/game/scenes/leveloverview.ts @@ -25,7 +25,7 @@ class LevelOverview extends Base { private _backToMainMenu: Button; private _reset: Button; - _audioPlayer: AudioPlayer; + private _audioPlayer: AudioPlayer; public constructor() { super( diff --git a/src/ts/game/scenes/mainmenu.ts b/src/ts/game/scenes/mainmenu.ts index b6b1ef7..19911f5 100644 --- a/src/ts/game/scenes/mainmenu.ts +++ b/src/ts/game/scenes/mainmenu.ts @@ -98,9 +98,8 @@ class MainMenu extends Base { public open(): void { super.open(); - console.log(this._audioPlayer.currentMusic); if ( - this._audioPlayer.currentMusic === null || + !this._audioPlayer.currentMusic || this._audioPlayer.currentMusic.id !== "track4" ) { this._audioPlayer.play(