patternjavascriptMinor
Data structure for entities
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
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:
Each time you want to register a service you call the register method:
And when you wanted to get it from the container:
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:
An example usage:
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
Here's a demo of the above code: http://jsbin.com/nezowapo/1/
I hope this helps.
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 applicationsI 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 applicationsContext
StackExchange Code Review Q#51000, answer score: 5
Revisions (0)
No revisions yet.