UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

335 lines (264 loc) • 7.22 kB
import List from "../../core/collection/list/List.js"; import { EntityManager } from "../ecs/EntityManager.js"; import Scene from "./Scene.js"; class SceneManager { /** * * @type {Scene|null} */ #current_scene = null /** * * @return {Scene|null} */ get current_scene() { return this.#current_scene; } /** * * @type {EntityManager|null} */ #entity_manager = null; /** * * @return {EntityManager|null} */ get entity_manager(){ return this.#entity_manager; } /** * * @param {EntityManager} entityManager * @param {Clock} clock * @constructor */ constructor(entityManager, clock) { /** * @type {List<Scene>} */ this.scenes = new List(); /** * * @type {EntityManager} */ this.#entity_manager = entityManager; /** * * @type {Clock} */ this.clock = clock; /** * Used to track scene transitions * @readonly * @private * @type {string[]} */ this.stack = []; } /** * * @param {string} name * @returns {Scene} */ create(name) { const scene = new Scene(name); this.add(scene); return scene; } /** * * @param {Scene} scene */ add(scene) { if (this.exists(scene.name)) { throw new Error(`Scene named '${scene.name}' already exists`); } this.scenes.add(scene); } /** * * @param {string} name * @returns {number} */ indexByName(name) { const length = this.scenes.length; for (let i = 0; i < length; i++) { const scene = this.scenes.get(i); if (scene.name === name) { return i; } } return -1; } /** * @template T * @param {string} name * @returns {Scene|T|undefined} */ getByName(name) { return this.scenes.find(function (scene) { return scene.name === name; }); } /** * * @param {string} name * @returns {boolean} */ remove(name) { const sceneIndex = this.indexByName(name); if (sceneIndex === -1) { //doesn't exist, no need to delete return false; } const scene = this.scenes.get(sceneIndex); if (this.#current_scene === scene) { this.deactivateCurrentScene(); this.#current_scene = null; } this.scenes.remove(sceneIndex); return true; } /** * * @returns {SceneManager} */ clear() { if (this.#current_scene !== null) { this.#current_scene.active.set(false); this.#entity_manager.detachDataset(); this.#current_scene = null; } return this; } /** * * @param {string} name * @returns {boolean} */ exists(name) { return this.getByName(name) !== undefined; } /** * @private * @param {Scene} scene */ deactivateScene(scene) { if (!scene.active.getValue()) { //not active, nothing to do return; } try { scene.handlePreDeactivation(); } catch (e) { console.error(`Exception in pre-deactivation routine of scene '${scene.name}'`, e); } scene.active.set(false); if (this.#entity_manager.dataset === scene.dataset) { this.#entity_manager.detachDataset(); } //remove speed modifiers scene.speedModifiers.forEach(m => this.clock.speed.removeModifier(m)); //unwatch modifiers scene.speedModifiers.on.added.remove(this.__handleSpeedModifierAdded, this); scene.speedModifiers.on.removed.remove(this.__handleSpeedModifierRemoved, this); try { scene.handlePostDeactivation(); } catch (e) { console.error(`Exception in post-deactivation routine of scene '${scene.id}'`, e); } } /** * @private * @param {Scene} scene */ activateScene(scene) { try { scene.handlePreActivation(); } catch (e) { console.error(`Exception in pre-activation routine of scene '${scene.id}'`, e); } const em = this.#entity_manager; em.attachDataset(scene.dataset); scene.active.set(true); scene.speedModifiers.forEach(m => this.clock.speed.addModifier(m)); //watch speed modifiers scene.speedModifiers.on.added.add(this.__handleSpeedModifierAdded, this); scene.speedModifiers.on.removed.add(this.__handleSpeedModifierRemoved, this); try { scene.handlePostActivation(); } catch (e) { console.error(`Exception in post-activation routine of scene '${scene.id}'`, e); } this.#current_scene = scene; } /** * @private */ deactivateCurrentScene() { const currentScene = this.#current_scene; if (currentScene !== null) { this.deactivateScene(currentScene); } } /** * @private * @param {LinearModifier} mod */ __handleSpeedModifierAdded(mod) { this.clock.speed.addModifier(mod); } /** * @private * @param {LinearModifier} mod */ __handleSpeedModifierRemoved(mod) { this.clock.speed.removeModifier(mod); } /** * * @param {string} name */ set(name) { const scene = this.getByName(name); if (scene === undefined) { throw new Error(`Scene named '${name}' doesn't exist, valid options are: [${this.scenes.map(s => s.name).join(', ')}]`); } if (this.#current_scene === scene) { //already at that scene return; } const em = this.#entity_manager; // Ensure that all system components are registered scene.dataset.registerManyComponentTypes(em.getComponentTypeMap()); this.deactivateCurrentScene(); this.activateScene(scene); } /** * * @param {string} id */ stackPush(id) { //take current scene and put it onto the stack this.stack.push(this.#current_scene.name); this.set(id); } /** * * @returns {string} ID of the popped scene */ stackPop() { const id = this.stack.pop(); this.set(id); return id; } /** * Clear out current stack of scenes */ stackDrop() { this.stack.splice(0, this.stack.length); } update(timeDelta) { } } export default SceneManager;