HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Data structure for entities

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
entitiesdataforstructure

Problem

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

Solution

I wrote a small method for doing dependency registering/injection a while ago which you may find useful; it's similar to what you're doing here but the implementation is very lightweight. Basically each entity in the system which I would want to use multiple times is registered in a container entity, like so:

var Container = function() {
    this._services = {};
};


Each time you want to register a service you call the register method:

Container.prototype.register = function(service, constructor, options) {
    this._services[service] = {
        constructor: constructor,
        options: options || {}
    };
};


And when you wanted to get it from the container:

Container.prototype.get = function(service) {
    var s = this._services[service].constructor;
    return (typeof s === 'function') ? s(this) : s;
};


The filtering is what I find the most useful though; you can register each dependency with an array of tags to use later on, and then get all of the entities registered with that tag:

Container.prototype.getByTag = function(tag) {
    return Object.keys(this._services).reduce(function(list, service) {
        var tags = this._services[service].options.tags;
        tags && tags.indexOf(tag) > -1 && list.push(this.get(service));
        return list;
    }.bind(this), []); 
};


An example usage:

var Application = function() {};

Application.prototype.helloWorld = function() {
    return 'Hello world!';
};

var c = new Container();

c.register('app', new Application(), { tags: ['app'] });
c.register('app2', new Application(), { tags: ['app'] });
c.register('app3', new Application());
c.register('app4', new Application(), { tags: ['other'] });

console.log(c.getByTag('app'));
// => Array of two applications


I think it's easier to register your entities and return arrays back when you want to use them, it saves you from having to write all of those for loops.

Here's a demo of the above code: http://jsbin.com/nezowapo/1/

I hope this helps.

Code Snippets

var Container = function() {
    this._services = {};
};
Container.prototype.register = function(service, constructor, options) {
    this._services[service] = {
        constructor: constructor,
        options: options || {}
    };
};
Container.prototype.get = function(service) {
    var s = this._services[service].constructor;
    return (typeof s === 'function') ? s(this) : s;
};
Container.prototype.getByTag = function(tag) {
    return Object.keys(this._services).reduce(function(list, service) {
        var tags = this._services[service].options.tags;
        tags && tags.indexOf(tag) > -1 && list.push(this.get(service));
        return list;
    }.bind(this), []); 
};
var Application = function() {};

Application.prototype.helloWorld = function() {
    return 'Hello world!';
};

var c = new Container();

c.register('app', new Application(), { tags: ['app'] });
c.register('app2', new Application(), { tags: ['app'] });
c.register('app3', new Application());
c.register('app4', new Application(), { tags: ['other'] });

console.log(c.getByTag('app'));
// => Array of two applications

Context

StackExchange Code Review Q#51000, answer score: 5

Revisions (0)

No revisions yet.