Entities and Components

Posted 2020-01-29


The ECS implementation in JS is chugging along, although I'm improvising it from what little I bother reading about it. I needed to think of a way to quickly access Entities and their Components in a way that would let me deal with them as groups: from what I read, it's best to access components at the same time if they're all the same type. For instance, a gravity system might affect the velocity component of a bunch of different entities at once.

It may be naive, but from where I'm standing it seems serviceable. Here's entities:

class EntityManager {
    constructor() {
        this.entities = {};
        this.entitiesByType = {};
        this.index = 0;
    }

    addEntityByTypeMap(entityType) {
        this.entitiesByType[entityType] = [];
    }

    removeEntityByTypeMap(entityType) {
        delete this.entitiesByType[entityType];
    }

    addEntity(entity) {
        entity.id = this.index;
        this.entities[this.index] = entity;
        cm.addComponentByEntityMap(this.index);
        if (!(entity.type in this.entitiesByType)) {
            this.addEntityByTypeMap(entity.type);
        }
        this.entitiesByType[].push(this.index);
        this.index++;
    }

    removeEntity(entityId) {
        var type = this.entities[entityId].type; 
        var byTypeIndex = this.entitiesByType[type].indexOf(entityId);
        this.entitiesByType[type].splice(byTypeIndex, 1);

        cm.removeComponentByEntityMap(entityId);
        delete this.entities[entityId];
    }
}

class Entity {
    constructor() {
        this.id = 0;
        this.type = "";
    }
}

and here's components:

class ComponentManager {
    constructor() {
        this.components = {}
        this.componentsByEntity = {};
        this.componentsByType = {};
    }

    addComponentByEntityMap(entityId) {
        this.componentsByEntity[entityId] = [];
    }

    removeComponentByEntityMap(entityId) {
        delete this.componentsByEntity[entityId];
    }

    addComponentByTypeMap(componentType) {
        this.componentsByType[componentType] = [];
    }

    removeComponentByTypeMap(componentType) {
        delete this.componentsByType[componentType];
    }

    addComponent(entityId, component) {
        component.id = this.index;
        component.entityId = entityId; // probably set when instantiated tho
        this.components[this.index] = component;

        if (!(component.type in this.componentsByType)) {
            this.addComponentByTypeMap(component.type, this.index);
        }
        this.componentsByType[component.type].push(this.index);

        if (!(entityId in this.componentsByEntity)) {
            this.addComponentByEntityMap(entityId);
        }
        this.componentsByEntity[entityId].push(this.index);

        this.index++;
    }

    removeComponent(componentId) {
        var entityId = this.components[componentId].entityId;
        var byEntityIndex = this.componentsByEntity[entityId].indexOf(componentId);
        this.componentsByEntity[entityId].splice(byEntityIndex, 1);

        var type = this.components[componentId].type;
        var byTypeIndex = this.componentsByType[type].indexOf(componentId);
        this.componentsByType[type].splice(byTypeIndex, 1);

        delete this.components[componentId];
    }
}

class Component {
    constructor() {
        this.id = 0;
        this.entityId = 0;
        // Type is set by children of this class.
        // Used instead of obj.instance.name because that breaks when minified.
        // TODO: what about inheritance classes of those children?
        this.type = "";
    }
}

I'll try to work out how these things play along with systems tomorrow, but it'll be a busy night, so it may take longer. On top of that, I'm not sure how systems will work within Phaser, which has a lot of existing boilerplate for creating game objects and drawing their sprites. I hope I'm not getting in over my head!


Tagged: ecs daily