Migrating Loader bar to SuperLoad and fixing Resize issue on loader

This commit is contained in:
David Négrier
2022-03-18 15:47:25 +01:00
parent c529658ef4
commit 9f79e595a5
6 changed files with 157 additions and 83 deletions
+62 -38
View File
@@ -1,9 +1,12 @@
import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig; import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig;
import { DirtyScene } from "../Game/DirtyScene"; import { DirtyScene } from "../Game/DirtyScene";
import { gameManager } from "../Game/GameManager"; import { gameManager } from "../Game/GameManager";
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
import CancelablePromise from "cancelable-promise";
import Image = Phaser.GameObjects.Image;
import Texture = Phaser.Textures.Texture;
const TextName: string = "Loading..."; const TextName: string = "Loading...";
const LogoFrame: ImageFrameConfig = { frameWidth: 310, frameHeight: 60 };
const loadingBarHeight: number = 16; const loadingBarHeight: number = 16;
const padding: number = 5; const padding: number = 5;
@@ -13,10 +16,15 @@ export class Loader {
private progress!: Phaser.GameObjects.Graphics; private progress!: Phaser.GameObjects.Graphics;
private progressAmount: number = 0; private progressAmount: number = 0;
private logo: Phaser.GameObjects.Image | undefined; private logo: Phaser.GameObjects.Image | undefined;
private logoPoweredBy: Phaser.GameObjects.Image | undefined;
private poweredByLogo: Phaser.GameObjects.Image | undefined;
private loadingText: Phaser.GameObjects.Text | null = null; private loadingText: Phaser.GameObjects.Text | null = null;
private logoNameIndex!: string; private logoNameIndex!: string;
private superLoad: SuperLoaderPlugin;
public constructor(private scene: Phaser.Scene) {} public constructor(private scene: Phaser.Scene) {
this.superLoad = new SuperLoaderPlugin(scene);
}
public addLoader(): void { public addLoader(): void {
// If there is nothing to load, do not display the loader. // If there is nothing to load, do not display the loader.
@@ -29,41 +37,42 @@ export class Loader {
const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3); const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3);
const promiseLoadLogoTexture = new Promise<Phaser.GameObjects.Image>((res) => { //add loading if logo image until logo image is ready
if (this.scene.load.textureManager.exists(this.logoNameIndex)) { this.loadingText = this.scene.add.text(
return res( this.scene.game.renderer.width / 2,
(this.logo = this.scene.add.image( this.scene.game.renderer.height / 2 - 50,
this.scene.game.renderer.width / 2, TextName
this.scene.game.renderer.height / 2 - 150, );
this.logoNameIndex
)) const logoPromise = this.superLoad.image(this.logoNameIndex, logoResource);
); logoPromise.then((texture) => {
} else { this.logo = this.scene.add.image(
//add loading if logo image is not ready this.scene.game.renderer.width / 2,
this.loadingText = this.scene.add.text( this.scene.game.renderer.height / 2 - 150,
this.scene.game.renderer.width / 2, texture
this.scene.game.renderer.height / 2 - 50, );
TextName
); this.loadingText?.destroy();
}
this.scene.load.spritesheet(this.logoNameIndex, logoResource, LogoFrame);
this.scene.load.once(`filecomplete-spritesheet-${this.logoNameIndex}`, () => {
if (this.loadingText) {
this.loadingText.destroy();
}
return res(
(this.logo = this.scene.add.image(
this.scene.game.renderer.width / 2,
this.scene.game.renderer.height / 2 - 150,
this.logoNameIndex
))
);
});
}); });
let poweredByLogoPromise: CancelablePromise<Texture> | undefined;
if (gameManager.currentStartedRoom.loadingLogo) {
poweredByLogoPromise = this.superLoad.image(
"poweredByLogo",
"static/images/Powered_By_WorkAdventure_Small.png"
);
poweredByLogoPromise.then((texture) => {
this.poweredByLogo = this.scene.add.image(
this.scene.game.renderer.width / 2,
this.scene.game.renderer.height - 50,
texture
);
});
}
this.progressContainer = this.scene.add.graphics(); this.progressContainer = this.scene.add.graphics();
this.progress = this.scene.add.graphics();
this.progressContainer.fillStyle(0x444444, 0.8); this.progressContainer.fillStyle(0x444444, 0.8);
this.progress = this.scene.add.graphics();
this.resize(); this.resize();
@@ -71,20 +80,26 @@ export class Loader {
this.progressAmount = value; this.progressAmount = value;
this.drawProgress(); this.drawProgress();
}); });
const resizeFunction = this.resize.bind(this);
this.scene.scale.on(Phaser.Scale.Events.RESIZE, resizeFunction);
this.scene.load.on("complete", () => { this.scene.load.on("complete", () => {
if (this.loadingText) { if (this.loadingText) {
this.loadingText.destroy(); this.loadingText.destroy();
} }
promiseLoadLogoTexture logoPromise.cancel();
.then((resLoadingImage: Phaser.GameObjects.Image) => { poweredByLogoPromise?.cancel();
resLoadingImage.destroy();
}) this.logo?.destroy();
.catch((e) => console.error(e)); this.poweredByLogo?.destroy();
this.progress.destroy(); this.progress.destroy();
this.progressContainer.destroy(); this.progressContainer.destroy();
if (this.scene instanceof DirtyScene) { if (this.scene instanceof DirtyScene) {
this.scene.markDirty(); this.scene.markDirty();
} }
this.scene.scale.off(Phaser.Scale.Events.RESIZE, resizeFunction);
}); });
} }
@@ -92,9 +107,13 @@ export class Loader {
if (this.scene.load.textureManager.exists(this.logoNameIndex)) { if (this.scene.load.textureManager.exists(this.logoNameIndex)) {
this.scene.load.textureManager.remove(this.logoNameIndex); this.scene.load.textureManager.remove(this.logoNameIndex);
} }
if (this.scene.load.textureManager.exists("poweredByLogo")) {
this.scene.load.textureManager.remove("poweredByLogo");
}
} }
public resize(): void { public resize(): void {
console.log("RESIZE TRIGGERED");
const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3); const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3);
this.progressContainer.clear(); this.progressContainer.clear();
@@ -117,6 +136,11 @@ export class Loader {
this.logo.x = this.scene.game.renderer.width / 2; this.logo.x = this.scene.game.renderer.width / 2;
this.logo.y = this.scene.game.renderer.height / 2 - 150; this.logo.y = this.scene.game.renderer.height / 2 - 150;
} }
if (this.poweredByLogo) {
this.poweredByLogo.x = this.scene.game.renderer.width / 2;
this.poweredByLogo.y = this.scene.game.renderer.height - 40;
}
} }
private drawProgress() { private drawProgress() {
@@ -2,7 +2,7 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type { CharacterTexture } from "../../Connexion/LocalUser"; import type { CharacterTexture } from "../../Connexion/LocalUser";
import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures"; import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures";
import CancelablePromise from "cancelable-promise"; import CancelablePromise from "cancelable-promise";
import {SuperLoaderPlugin} from "../Services/SuperLoaderPlugin"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
import Texture = Phaser.Textures.Texture; import Texture = Phaser.Textures.Texture;
export interface FrameConfig { export interface FrameConfig {
@@ -52,11 +52,13 @@ export const lazyLoadPlayerCharacterTextures = (
): CancelablePromise<string[]> => { ): CancelablePromise<string[]> => {
const promisesList: CancelablePromise<Texture>[] = []; const promisesList: CancelablePromise<Texture>[] = [];
for (const texture of textures) { for (const texture of textures) {
promisesList.push(superLoaderPlugin.spritesheet(texture.id, texture.img, { promisesList.push(
frameWidth: 32, superLoaderPlugin.spritesheet(texture.id, texture.img, {
frameHeight: 32, frameWidth: 32,
})); frameHeight: 32,
}; })
);
}
const returnPromise: CancelablePromise<Texture[]> = CancelablePromise.all(promisesList); const returnPromise: CancelablePromise<Texture[]> = CancelablePromise.all(promisesList);
return returnPromise.then(() => return returnPromise.then(() =>
+1 -1
View File
@@ -19,7 +19,7 @@ export class GameManager {
private companion: string | null; private companion: string | null;
private startRoom!: Room; private startRoom!: Room;
private cameraSetup?: { video: unknown; audio: unknown }; private cameraSetup?: { video: unknown; audio: unknown };
currentGameSceneName: string | null = null; private currentGameSceneName: string | null = null;
// Note: this scenePlugin is the scenePlugin of the EntryScene. We should always provide a key in methods called on this scenePlugin. // Note: this scenePlugin is the scenePlugin of the EntryScene. We should always provide a key in methods called on this scenePlugin.
private scenePlugin!: Phaser.Scenes.ScenePlugin; private scenePlugin!: Phaser.Scenes.ScenePlugin;
+2 -4
View File
@@ -100,7 +100,7 @@ import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite";
import type { VideoPeer } from "../../WebRtc/VideoPeer"; import type { VideoPeer } from "../../WebRtc/VideoPeer";
import CancelablePromise from "cancelable-promise"; import CancelablePromise from "cancelable-promise";
import { Deferred } from "ts-deferred"; import { Deferred } from "ts-deferred";
import {SuperLoaderPlugin} from "../Services/SuperLoaderPlugin"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null; initPosition: PointInterface | null;
reconnecting: boolean; reconnecting: boolean;
@@ -1917,7 +1917,7 @@ ${escapedMessage}
return; return;
} }
const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, addPlayerData.characterLayers); const texturesPromise = lazyLoadPlayerCharacterTextures(this.superLoad, addPlayerData.characterLayers);
const player = new RemotePlayer( const player = new RemotePlayer(
addPlayerData.userId, addPlayerData.userId,
addPlayerData.userUuid, addPlayerData.userUuid,
@@ -2088,8 +2088,6 @@ ${escapedMessage}
right: camera.scrollX + camera.width, right: camera.scrollX + camera.width,
bottom: camera.scrollY + camera.height, bottom: camera.scrollY + camera.height,
}); });
this.loader.resize();
} }
private getObjectLayerData(objectName: string): ITiledMapObject | undefined { private getObjectLayerData(objectName: string): ITiledMapObject | undefined {
@@ -4,7 +4,7 @@ import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager";
import type CancelablePromise from "cancelable-promise"; import type CancelablePromise from "cancelable-promise";
import { PlayerTextures } from "../Entity/PlayerTextures"; import { PlayerTextures } from "../Entity/PlayerTextures";
import Texture = Phaser.Textures.Texture; import Texture = Phaser.Textures.Texture;
import {SuperLoaderPlugin} from "../Services/SuperLoaderPlugin"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
export abstract class AbstractCharacterScene extends ResizableScene { export abstract class AbstractCharacterScene extends ResizableScene {
protected playerTextures: PlayerTextures; protected playerTextures: PlayerTextures;
+83 -33
View File
@@ -1,9 +1,6 @@
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import CancelablePromise from "cancelable-promise"; import CancelablePromise from "cancelable-promise";
import {FrameConfig} from "../Entity/PlayerTexturesLoadingManager"; import { Scene } from "phaser";
import {Scene} from "phaser"; import Texture = Phaser.Textures.Texture;
/** /**
* A wrapper around Phaser LoaderPlugin. Each method returns a (cancelable) Promise that resolves as soon as * A wrapper around Phaser LoaderPlugin. Each method returns a (cancelable) Promise that resolves as soon as
@@ -13,72 +10,125 @@ import {Scene} from "phaser";
* So there is no risk of trying to add a resource on a closed scene. * So there is no risk of trying to add a resource on a closed scene.
*/ */
export class SuperLoaderPlugin { export class SuperLoaderPlugin {
constructor(private scene: Scene) { constructor(private scene: Scene) {}
public spritesheet(
key: string,
url: string,
frameConfig?: Phaser.Types.Loader.FileTypes.ImageFrameConfig,
xhrSettings?: Phaser.Types.Loader.XHRSettingsObject
) {
return this.loadResource<Texture>(
() => {
this.scene.load.spritesheet(key, url, frameConfig, xhrSettings);
},
key,
url,
() => {
if (this.scene.load.textureManager.exists(key)) {
return this.scene.load.textureManager.get(key);
}
return undefined;
},
"spritesheet"
);
} }
public spritesheet(key: string, url: string, frameConfig?: Phaser.Types.Loader.FileTypes.ImageFrameConfig, xhrSettings?: Phaser.Types.Loader.XHRSettingsObject) public image(key: string, url: string, xhrSettings?: Phaser.Types.Loader.XHRSettingsObject) {
{ return this.loadResource<Texture>(
return new CancelablePromise<Phaser.Textures.Texture>((res, rej, cancel) => { () => {
this.scene.load.image(key, url, xhrSettings);
},
key,
url,
() => {
if (this.scene.load.textureManager.exists(key)) {
return this.scene.load.textureManager.get(key);
}
return undefined;
},
"image"
);
}
/**
* @param callback The function that calls the loader to load a resource
* @param key The key of the resource to be loaded
* @param fromCache A function that checks in the cache if the resource is already available
* @param type The type of resource loaded
* @private
*/
private loadResource<T>(
callback: () => void,
key: string,
url: string,
fromCache: () => T | undefined,
type: string
): CancelablePromise<T> {
// If for some reason, the "url" is empty, let's reject the promise.
if (!url) {
console.error("Tried to load an empty " + type + ". URL is missing.");
return CancelablePromise.reject(Error("Failed loading " + type + ": URL is empty"));
}
return new CancelablePromise<T>((res, rej, cancel) => {
if (this.scene.scene.settings.status === Phaser.Scenes.DESTROYED) { if (this.scene.scene.settings.status === Phaser.Scenes.DESTROYED) {
rej(new Error('Trying to load a spritesheet in a Scene that is already destroyed.')); rej(new Error("Trying to load a " + type + " in a Scene that is already destroyed."));
} }
if (this.scene.load.textureManager.exists(key)) { const resource = fromCache();
return res(this.scene.load.textureManager.get(key)); if (resource !== undefined) {
} return res(resource);
// If for some reason, the "url" is empty, let's reject the promise.
if (!url) {
console.error("Tried to load an empty texture. URL is missing.");
rej(new Error('Failed loading spritesheet: URL is empty'));
return;
} }
let destroySceneEventRegistered = false; let destroySceneEventRegistered = false;
const unloadCallbacks = () => { const unloadCallbacks = () => {
this.scene.load.off("filecomplete-spritesheet-" + key, successCallback); this.scene.load.off("filecomplete-" + type + "-" + key, successCallback);
this.scene.load.off("loaderror", errorCallback); this.scene.load.off("loaderror", errorCallback);
if (destroySceneEventRegistered) { if (destroySceneEventRegistered) {
this.scene.events.off(Phaser.Scenes.Events.DESTROY, unloadCallbacks); this.scene.events.off(Phaser.Scenes.Events.DESTROY, unloadCallbacks);
} }
} };
const errorCallback = (file: { src: string }) => { const errorCallback = (file: { src: string }) => {
if (file.src !== url) return; if (file.src !== url) return;
console.error("Failed loading spritesheet: ", url); console.error("Failed loading " + type + ": ", url);
rej(new Error('Failed loading spritesheet: "' + url + '"')); rej(new Error('Failed loading "+type+": "' + url + '"'));
unloadCallbacks(); unloadCallbacks();
}; };
const successCallback = () => { const successCallback = () => {
this.scene.load.off("loaderror", errorCallback); this.scene.load.off("loaderror", errorCallback);
this.scene.events.off(Phaser.Scenes.Events.DESTROY, unloadCallbacks); this.scene.events.off(Phaser.Scenes.Events.DESTROY, unloadCallbacks);
res(this.scene.load.textureManager.get(key)); const resource = fromCache();
if (!resource) {
return rej(new Error("Newly loaded resource not available in cache"));
}
res(resource);
}; };
cancel(() => { cancel(() => {
unloadCallbacks(); unloadCallbacks();
}); });
this.scene.load.spritesheet(key, url, frameConfig, xhrSettings); callback();
this.scene.load.once("filecomplete-spritesheet-" + key, successCallback); this.scene.load.once("filecomplete-" + type + "-" + key, successCallback);
this.scene.load.on("loaderror", errorCallback); this.scene.load.on("loaderror", errorCallback);
// When the scene is destroyed, let's remove our callbacks. if (this.scene.scene.settings.status > Phaser.Scenes.LOADING) {
// We only need to register this destroy event is the scene is not in loading state (otherwise, Phaser // When the scene is destroyed, let's remove our callbacks.
// will take care of that for us). // We only need to register this destroy event is the scene is not in loading state (otherwise, Phaser
if (this.scene.scene.settings.status === Phaser.Scenes.LOADING) { // will take care of that for us).
destroySceneEventRegistered = true; destroySceneEventRegistered = true;
this.scene.events.once(Phaser.Scenes.Events.DESTROY, unloadCallbacks); this.scene.events.once(Phaser.Scenes.Events.DESTROY, unloadCallbacks);
}
if (this.scene.scene.settings.status !== Phaser.Scenes.LOADING) { // Let's start the loader if we are no more in the scene loading state
this.scene.load.start(); this.scene.load.start();
// Due to a bug, if the loader is already started, additional items are not added.... unless we // Due to a bug, if the loader is already started, additional items are not added.... unless we
// explicitly call the "update" method. // explicitly call the "update" method.
this.scene.load.update(); this.scene.load.update();
} }
}); });
}; }
} }