File: ../src/EntitySystem.js
(function(ArtemiJS) {
'use strict';
/**
* Used to generate a unique bit for each system.
* Only used internally in EntitySystem.
*
* @module ArtemiJS
* @class SystemIndexManager
* @for EntitySystem
* @final
* @constructor
*/
var SystemIndexManager = {
/**
* @property INDEX
* @type {Number}
*/
INDEX: 0,
/**
* @property indices
* @type {Array}
*/
indices: {},
/**
* @method getIndexFor
* @param {EntitySystem} entitySystem
* @return {Number} index
*/
getIndexFor: function(entitySystem) {
var index = this.indices[entitySystem];
if(!index) {
index = this.INDEX++;
this.indices[entitySystem] = index;
}
return index;
}
};
/**
* The most raw entity system. It should not typically be used, but you can
* create your own entity system handling by extending this. It is
* recommended that you use the other provided entity system implementations
*
* @module ArtemiJS
* @class EntitySystem
* @constructor
* @param {Aspect} _aspect Creates an entity system that uses the specified
* aspect as a matcher against entities.
*/
var EntitySystem = function(_aspect) {
/**
* @property world
* @type {World}
*/
this.world = null;
/**
* @private
* @final
* @property systemIndex
* @type {Number}
*/
var systemIndex = SystemIndexManager.getIndexFor(this.getClass()),
/**
* @private
* @property actives
* @type {Utils.Bag}
*/
actives = new ArtemiJS.Utils.Bag(),
/**
* @private
* @property aspect
* @type {Aspect}
*/
aspect = _aspect,
/**
* @private
* @property allSet
* @type {Utils.BitSet}
*/
allSet = aspect.getAllSet(),
/**
* @private
* @property exclusionSet
* @type {Utils.BitSet}
*/
exclusionSet = aspect.getExclusionSet(),
/**
* @private
* @property oneSet
* @type {Utils.BitSet}
*/
oneSet = aspect.getOneSet(),
/**
* @private
* @property passive
* @type {Boolean}
*/
passive,
/**
* @private
* @property dummy
* @type {Boolean}
*/
dummy = allSet.isEmpty() && oneSet.isEmpty(),
me = this;
/**
* @private
* @method removeFromSystem
* @param {Entity} entity
*/
function removeFromSystem(entity) {
actives.remove(entity);
entity.getSystemBits().clear(systemIndex);
me.removed(entity);
}
/**
* @private
* @method insertToSystem
* @param {Entity} entity
*/
function insertToSystem(entity) {
actives.add(entity);
entity.getSystemBits().set(systemIndex);
me.inserted(entity);
}
/**
* Called before processing of entities begins
*
* @method begin
*/
this.begin = function() {};
/**
* Process the entities
*
* @method process
*/
this.process = function() {
if(this.checkProcessing()) {
this.begin();
this.processEntities(actives);
this.end();
}
};
/**
* Called after the processing of entities ends
*
* @method end
*/
this.end = function() {};
/**
* Any implementing entity system must implement this method and the
* logic to process the given entities of the system.
*
* @method processEntities
* @param {Bag} entities athe entities this system contains
*/
this.processEntities = function(entities) {};
/**
* Check the system should processing
*
* @method checkProcessing
* @return {Boolean} true if the system should be processed, false if not
*/
this.checkProcessing = function() {};
/**
* Override to implement code that gets executed when systems are
* initialized.
*
* @method initialize
*/
this.initialize = function() {};
/**
* Called if the system has received a entity it is interested in,
* e.g. created or a component was added to it.
*
* @method inserted
* @param {Entity} entity the entity that was added to this system
*/
this.inserted = function(entity) {};
/**
* Called if a entity was removed from this system, e.g. deleted
* or had one of it's components removed.
*
* @method removed
* @param {Entity} entity the entity that was removed from this system.
*/
this.removed = function(entity) {};
/**
* Will check if the entity is of interest to this system.
*
* @method check
* @param {Entity} entity the entity to check
*/
this.check = function(entity) {
if(dummy) {
return;
}
var contains = entity.getSystemBits().get(systemIndex);
var interested = true;
var componentBits = entity.getComponentBits();
if(!allSet.isEmpty()) {
for (var i = allSet.nextSetBit(0); i >= 0; i = allSet.nextSetBit(i+1)) {
if(!componentBits.get(i)) {
interested = false;
break;
}
}
}
if(!exclusionSet.isEmpty() && interested) {
interested = !exclusionSet.intersects(componentBits);
}
// Check if the entity possesses ANY of the components in the oneSet. If so, the system is interested.
if(!oneSet.isEmpty()) {
interested = oneSet.intersects(componentBits);
}
if (interested && !contains) {
insertToSystem(entity);
} else if (!interested && contains) {
removeFromSystem(entity);
}
};
/**
* @method added
* @param {Entity} entity
*/
this.added = function(entity) {
this.check(entity);
};
/**
* @method changed
* @param {Entity} entity
*/
this.changed = function(entity) {
this.check(entity);
};
/**
* @method deleted
* @param {Entity} entity
*/
this.deleted = function(entity) {
if(entity.getSystemBits().get(systemIndex)) {
removeFromSystem(entity);
}
};
/**
* @method disabled
* @param {Entity} entity
*/
this.disabled = function(entity) {
if(entity.getSystemBits().get(systemIndex)) {
removeFromSystem(entity);
}
};
/**
* @method enabled
* @param {Entity} entity
*/
this.enabled = function(entity) {
this.check(entity);
};
/**
* @method setWorld
* @param {World} world
*/
this.setWorld = function(world) {
this.world = world;
};
/**
* @method isPassive
* @return {Boolean}
*/
this.isPassive = function() {
return passive;
};
/**
* @method setPassive
* @param {Boolean} passive
*/
this.setPassive = function(passive) {
this.passive = passive;
};
/**
* @method getActives
* @return {Utils.Bag} actives
*/
this.getActives = function() {
return actives;
};
};
ArtemiJS.EntitySystem = EntitySystem;
ArtemiJS.EntitySystem.prototype = Object.create(ArtemiJS.EntityObserver.prototype);
})(window.ArtemiJS || {});