@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
335 lines (264 loc) • 7.22 kB
JavaScript
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;