UNPKG

ecsy

Version:
321 lines (240 loc) 9.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityManager = void 0; var _ObjectPool = require("./ObjectPool.js"); var _QueryManager = _interopRequireDefault(require("./QueryManager.js")); var _EventDispatcher = _interopRequireDefault(require("./EventDispatcher.js")); var _SystemStateComponent = require("./SystemStateComponent.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class EntityPool extends _ObjectPool.ObjectPool { constructor(entityManager, entityClass, initialSize) { super(entityClass, undefined); this.entityManager = entityManager; if (typeof initialSize !== "undefined") { this.expand(initialSize); } } expand(count) { for (var n = 0; n < count; n++) { var clone = new this.T(this.entityManager); clone._pool = this; this.freeList.push(clone); } this.count += count; } } /** * @private * @class EntityManager */ class EntityManager { constructor(world) { this.world = world; this.componentsManager = world.componentsManager; // All the entities in this instance this._entities = []; this._nextEntityId = 0; this._entitiesByNames = {}; this._queryManager = new _QueryManager.default(this); this.eventDispatcher = new _EventDispatcher.default(); this._entityPool = new EntityPool(this, this.world.options.entityClass, this.world.options.entityPoolSize); // Deferred deletion this.entitiesWithComponentsToRemove = []; this.entitiesToRemove = []; this.deferredRemovalEnabled = true; } getEntityByName(name) { return this._entitiesByNames[name]; } /** * Create a new entity */ createEntity(name) { var entity = this._entityPool.acquire(); entity.alive = true; entity.name = name || ""; if (name) { if (this._entitiesByNames[name]) { console.warn(`Entity name '${name}' already exist`); } else { this._entitiesByNames[name] = entity; } } this._entities.push(entity); this.eventDispatcher.dispatchEvent(ENTITY_CREATED, entity); return entity; } // COMPONENTS /** * Add a component to an entity * @param {Entity} entity Entity where the component will be added * @param {Component} Component Component to be added to the entity * @param {Object} values Optional values to replace the default attributes */ entityAddComponent(entity, Component, values) { // @todo Probably define Component._typeId with a default value and avoid using typeof if (typeof Component._typeId === "undefined" && !this.world.componentsManager._ComponentsMap[Component._typeId]) { throw new Error(`Attempted to add unregistered component "${Component.getName()}"`); } if (~entity._ComponentTypes.indexOf(Component)) { if (process.env.NODE_ENV !== "production") { console.warn("Component type already exists on entity.", entity, Component.getName()); } return; } entity._ComponentTypes.push(Component); if (Component.__proto__ === _SystemStateComponent.SystemStateComponent) { entity.numStateComponents++; } var componentPool = this.world.componentsManager.getComponentsPool(Component); var component = componentPool ? componentPool.acquire() : new Component(values); if (componentPool && values) { component.copy(values); } entity._components[Component._typeId] = component; this._queryManager.onEntityComponentAdded(entity, Component); this.world.componentsManager.componentAddedToEntity(Component); this.eventDispatcher.dispatchEvent(COMPONENT_ADDED, entity, Component); } /** * Remove a component from an entity * @param {Entity} entity Entity which will get removed the component * @param {*} Component Component to remove from the entity * @param {Bool} immediately If you want to remove the component immediately instead of deferred (Default is false) */ entityRemoveComponent(entity, Component, immediately) { var index = entity._ComponentTypes.indexOf(Component); if (!~index) return; this.eventDispatcher.dispatchEvent(COMPONENT_REMOVE, entity, Component); if (immediately) { this._entityRemoveComponentSync(entity, Component, index); } else { if (entity._ComponentTypesToRemove.length === 0) this.entitiesWithComponentsToRemove.push(entity); entity._ComponentTypes.splice(index, 1); entity._ComponentTypesToRemove.push(Component); entity._componentsToRemove[Component._typeId] = entity._components[Component._typeId]; delete entity._components[Component._typeId]; } // Check each indexed query to see if we need to remove it this._queryManager.onEntityComponentRemoved(entity, Component); if (Component.__proto__ === _SystemStateComponent.SystemStateComponent) { entity.numStateComponents--; // Check if the entity was a ghost waiting for the last system state component to be removed if (entity.numStateComponents === 0 && !entity.alive) { entity.remove(); } } } _entityRemoveComponentSync(entity, Component, index) { // Remove T listing on entity and property ref, then free the component. entity._ComponentTypes.splice(index, 1); var component = entity._components[Component._typeId]; delete entity._components[Component._typeId]; component.dispose(); this.world.componentsManager.componentRemovedFromEntity(Component); } /** * Remove all the components from an entity * @param {Entity} entity Entity from which the components will be removed */ entityRemoveAllComponents(entity, immediately) { let Components = entity._ComponentTypes; for (let j = Components.length - 1; j >= 0; j--) { if (Components[j].__proto__ !== _SystemStateComponent.SystemStateComponent) this.entityRemoveComponent(entity, Components[j], immediately); } } /** * Remove the entity from this manager. It will clear also its components * @param {Entity} entity Entity to remove from the manager * @param {Bool} immediately If you want to remove the component immediately instead of deferred (Default is false) */ removeEntity(entity, immediately) { var index = this._entities.indexOf(entity); if (!~index) throw new Error("Tried to remove entity not in list"); entity.alive = false; this.entityRemoveAllComponents(entity, immediately); if (entity.numStateComponents === 0) { // Remove from entity list this.eventDispatcher.dispatchEvent(ENTITY_REMOVED, entity); this._queryManager.onEntityRemoved(entity); if (immediately === true) { this._releaseEntity(entity, index); } else { this.entitiesToRemove.push(entity); } } } _releaseEntity(entity, index) { this._entities.splice(index, 1); if (this._entitiesByNames[entity.name]) { delete this._entitiesByNames[entity.name]; } entity._pool.release(entity); } /** * Remove all entities from this manager */ removeAllEntities() { for (var i = this._entities.length - 1; i >= 0; i--) { this.removeEntity(this._entities[i]); } } processDeferredRemoval() { if (!this.deferredRemovalEnabled) { return; } for (let i = 0; i < this.entitiesToRemove.length; i++) { let entity = this.entitiesToRemove[i]; let index = this._entities.indexOf(entity); this._releaseEntity(entity, index); } this.entitiesToRemove.length = 0; for (let i = 0; i < this.entitiesWithComponentsToRemove.length; i++) { let entity = this.entitiesWithComponentsToRemove[i]; while (entity._ComponentTypesToRemove.length > 0) { let Component = entity._ComponentTypesToRemove.pop(); var component = entity._componentsToRemove[Component._typeId]; delete entity._componentsToRemove[Component._typeId]; component.dispose(); this.world.componentsManager.componentRemovedFromEntity(Component); //this._entityRemoveComponentSync(entity, Component, index); } } this.entitiesWithComponentsToRemove.length = 0; } /** * Get a query based on a list of components * @param {Array(Component)} Components List of components that will form the query */ queryComponents(Components) { return this._queryManager.getQuery(Components); } // EXTRAS /** * Return number of entities */ count() { return this._entities.length; } /** * Return some stats */ stats() { var stats = { numEntities: this._entities.length, numQueries: Object.keys(this._queryManager._queries).length, queries: this._queryManager.stats(), numComponentPool: Object.keys(this.componentsManager._componentPool).length, componentPool: {}, eventDispatcher: this.eventDispatcher.stats }; for (var ecsyComponentId in this.componentsManager._componentPool) { var pool = this.componentsManager._componentPool[ecsyComponentId]; stats.componentPool[pool.T.getName()] = { used: pool.totalUsed(), size: pool.count }; } return stats; } } exports.EntityManager = EntityManager; const ENTITY_CREATED = "EntityManager#ENTITY_CREATE"; const ENTITY_REMOVED = "EntityManager#ENTITY_REMOVED"; const COMPONENT_ADDED = "EntityManager#COMPONENT_ADDED"; const COMPONENT_REMOVE = "EntityManager#COMPONENT_REMOVE";