I am working on an Entity-System-Component engine for games, which basically means an entity is an object that points to components attached to it, and systems retrieve components to modify their data.
I have a class, which is the main data holder, containing the entities, components and system, and methods to dispatch things. The current implementation is straightforward (each entity/component has an idea and there are hashtables containing their references). But it means lots of for loop to retrieve data, and I am wondering if there aren't any more clever way to manage all that.
Here are the most important parts of the code. First, the scene and the data it holds:
var Scene = function (description) {
this.description = description; // Just debug texts
this.name = description.name; // Reference for the list of scenes
this.entitiesCounter = 0; // This is used to assign ids to entities
this.entities = {}; // Holds every entity, assigned by their id
this.componentData = {}; // For each type of component, there is a nested json hashtable working like the entities object
this.componentCounters = {}; // Counters for components IDs (one per component type)
this.entityComponents = {}; // A hashtable containing the list of components id associated to an entity ID, used for retrieving an entity's data
this.functionsUsed = {}; // Keeps a list of the functions that systems have, and which systems use them. For example we can have "update" which would have a reference to every system's update function if they are implemented
};
Scene.prototype.init = function (callback) {
for (var i in componentTypes) {
this.componentData[i] = {}; // Initializes the component data holders
this.componentCounters[i] = 0; // Initializes the counters
}
// Fills the system functions array
for (var s in systems) {
for (var f in systems[s]) {
if (typeof systems[s][f] === "function") {
if (typeof this.functionsUsed[f] === "object") {
this.functionsUsed[f].push(s);
} else {
this.functionsUsed[f] = [s];
}
}
}
}
callback();
};
See the entire file on Github
So then, all this is used like this for example:
- The
update
function is called - The scene searches in its
functionsUsed
for the list ofupdate
systems functions and calls everyone of them - In the system code, I want to modify a certain component type's data for this entity (say
renderer
) - My system will take the current entity
id
, and use it to search in theentityComponents
table for therenderer
componentid
if it exists, and use its ID - Then if it exists, it will go in the
componentData
and fetch the component using the component's typerenderer
and itsid
- Then the system can modify data
In some scenarios, this implies lots of loops for retrieving the data, for example here. This function takes a list of components type and retrieve any entity that has all of them:
ES.prototype.getEntitiesWithComponents = function (components) {
// Empty associative array for holding every correct entity
var res = {};
// Loop through all the entity/components association
for (var i in this.entityComponents) {
var valid = true;
// For each one, loop through the list of accepted components, to search for a match
for (var c = 0; c < components.length; c++) {
// If the entity doesn't have one of the component, let it go
if (!this.entityComponents[i].hasOwnProperty(components[c])) {
valid = false;
break;
}
}
// If all the components were found, add it to the answer holder
if (valid) {
res[i] = {};
res[i].entity = this.entities[i];
for (var comp = 0; comp < components.length; comp++) {
var compType = components[comp];
res[i][compType] = this.getComponentForEntity(compType, this.entities[i]._id);
}
}
}
return res;
};
Entire file with other functions like this one on Github
As you can see, lots of for-loops are needed for most of the data manipulation. And especially since they are not standard array but objects, looping through them is slower (I think, not sure?)
So I'm wondering, is the way I implemented all that efficient, and could it be better?