UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

166 lines (124 loc) 4.46 kB
import { assert } from "../../core/assert.js"; import { array_remove_first } from "../../core/collection/array/array_remove_first.js"; import { logger } from "../../engine/logging/GlobalLogger.js"; import { AssetLoaderStatusView } from "../asset/AssetLoaderStatusView.js"; import TaskProgressView from "./TaskProgressView.js"; /** * @type {TaskLoadingScreen} */ let _instance; export class TaskLoadingScreen { _active_promise = Promise.resolve(); /** * * @type {number} * @private */ _release_delay_seconds = 0.5; /** * * @type {number} * @private */ _release_delay_handle = -1; _pending_cleanup = []; _state_stack = []; _clearReleaseTimeout() { if (this._release_delay_handle !== -1) { clearTimeout(this._release_delay_handle); this._release_delay_handle = -1; } } /** * * @param {Engine} engine * @param {Task|TaskGroup} task * @returns {Promise<void>} */ async load(engine, task) { assert.defined(task, 'task'); assert.notNull(task, 'task'); assert.defined(engine, 'engine'); assert.notNull(engine, 'engine'); await Promise.allSettled([this._active_promise]); this._clearReleaseTimeout(); const localization = engine.localization; const taskProgressView = new TaskProgressView({ task, localization }); taskProgressView.el.classList.add('loading-screen'); //add asset manager loading progress const loaderStatusView = new AssetLoaderStatusView({ assetManager: engine.assetManager, localization }); taskProgressView.addChild(loaderStatusView); taskProgressView.link(); const graphics = engine.graphics; const engine_state = { engine_rendering_enabled: engine.renderingEnabled, graphics_auto_draw: graphics.autoDraw }; this._state_stack.push(engine_state); const domParent = document.body; domParent.appendChild(taskProgressView.el); // suspend rendering engine.renderingEnabled = false; graphics.autoDraw = false; const attempt_cleanup = () => { if (this._active_promise !== promise) { return; } // clear any pending timeouts if they exist this._clearReleaseTimeout(); // run all remaining pending cleanup tasks for (let i = 0; i < this._pending_cleanup.length; i++) { const t = this._pending_cleanup[i]; if (t === cleanup) { // skip self continue; } try { t(); } catch (e) { logger.error(e); } // just to make sure, try to remove it again array_remove_first(this._pending_cleanup, t); // update iterator i--; } // this is the last task in the queue this._release_delay_handle = setTimeout(() => { cleanup(); }, Math.ceil(this._release_delay_seconds * 1000)); } const cleanup = () => { array_remove_first(this._pending_cleanup, cleanup); domParent.removeChild(taskProgressView.el); taskProgressView.unlink(); // restore state const state = this._state_stack.pop(); engine.renderingEnabled = state.engine_rendering_enabled; engine.graphics.autoDraw = state.graphics_auto_draw; }; this._pending_cleanup.push(cleanup); const promise = task.promise().finally(attempt_cleanup); this._active_promise = promise; return promise; } /** * * @param {Engine} engine * @param {Task|TaskGroup} task * @returns {Promise<void>} */ static load(engine, task) { return TaskLoadingScreen.instance.load(engine, task); } /** * * @returns {TaskLoadingScreen} */ static get instance() { if (_instance === undefined) { _instance = new TaskLoadingScreen(); } return _instance; } }