diff --git a/core/src/com/me/common/ecs/ComponentBag.java b/core/src/com/me/common/ecs/ComponentBag.java index 88407e4..39b3c0f 100644 --- a/core/src/com/me/common/ecs/ComponentBag.java +++ b/core/src/com/me/common/ecs/ComponentBag.java @@ -11,8 +11,8 @@ public class ComponentBag { } public Component get(int index) { - if (index > size) { - throw new IndexOutOfBoundsException("index > size"); + if (index >= size) { + return null; } return items[index]; } diff --git a/core/src/com/me/common/ecs/ComponentType.java b/core/src/com/me/common/ecs/ComponentType.java index 6436fba..0fcabbd 100644 --- a/core/src/com/me/common/ecs/ComponentType.java +++ b/core/src/com/me/common/ecs/ComponentType.java @@ -50,7 +50,7 @@ public class ComponentType { } protected boolean isTypeInMask(long mask) { - return (bits & mask) == mask; + return (bits & mask) == bits; } protected static void registerComponentType(Class component) { diff --git a/core/src/com/me/common/ecs/Engine.java b/core/src/com/me/common/ecs/Engine.java index cce1c9f..d24bc4f 100644 --- a/core/src/com/me/common/ecs/Engine.java +++ b/core/src/com/me/common/ecs/Engine.java @@ -7,6 +7,10 @@ import com.me.common.ecs.event.Listener; public class Engine { private Array entities; + private Array toActivate; + private Array toDeactivate; + private Array toRemove; + private ComponentBag[] components; private Array systems; @@ -14,6 +18,10 @@ public class Engine { public Engine() { this.entities = new Array<>(); + this.toActivate = new Array<>(); + this.toDeactivate = new Array<>(); + this.toRemove = new Array<>(); + this.systems = new Array<>(); this.listenerRegistry = new ListenerRegistry(); } @@ -26,6 +34,10 @@ public class Engine { this.systems.add(system); } + public void registerListener(Listener listener) { + listenerRegistry.registerListener(listener); + } + public void ready() { this.components = new ComponentBag[ComponentType.getRegisteredComponentTypeCount()]; for (int i = 0; i < components.length; i++) { @@ -33,10 +45,22 @@ public class Engine { } } + public Array getEntities() { + return entities; + } + + public int getEntityCount() { + return entities.size; + } + public void update(float dt) { + activatePending(); + deactivatePending(); + removePending(); + for (EntitySystem system : systems) { system.preProcessing(); - updateSystem(system, dt); + system.processEntities(dt); system.postProcessing(); } } @@ -47,13 +71,59 @@ public class Engine { return entity; } - public void removeEntity(Entity entity) { - removeAllEntityComponents(entity.getId()); - entities.removeValue(entity, true); + protected void removeEntity(Entity entity) { + entity.deactivate(); + toRemove.add(entity); } - public void registerListener(Listener listener) { - listenerRegistry.registerListener(listener); + private void removePending() { + if (toRemove.isEmpty()) { + return; + } + + for (Entity entity : toRemove) { + removeAllEntityComponents(entity.getId()); + entities.removeValue(entity, true); + } + + toRemove.clear(); + } + + protected void activateEntity(Entity entity) { + toActivate.add(entity); + } + + private void activatePending() { + if (toActivate.isEmpty()) { + return; + } + + for (Entity entity : toActivate) { + for (EntitySystem system : systems) { + if (system.interestedIn(entity)) { + system.entities.add(entity); + } + } + } + toActivate.clear(); + } + + protected void deactivateEntity(Entity entity) { + toDeactivate.add(entity); + } + + private void deactivatePending() { + if (toDeactivate.isEmpty()) { + return; + } + + for (Entity entity : toDeactivate) { + for (EntitySystem system : systems) { + system.entities.removeValue(entity, true); + } + } + + toDeactivate.clear(); } public void callEvent(Event event) { @@ -72,6 +142,7 @@ public class Engine { entity.addComponentType(type); } + protected void removeEntityComponent(Entity entity, Component component) { ComponentType type = ComponentType.getComponentType(component.getClass()); components[type.getId()].remove(entity.getId()); @@ -83,21 +154,4 @@ public class Engine { return clazz.cast(components[type.getId()].get(entity.getId())); } - protected void updateSystem(EntitySystem system, float dt) { - for (Entity entity : entities) { - if (!entity.isActive()) { - continue; - } - - // Check if this system is interested in this entity - if ((entity.getComponentBits() & system.getTypeMask()) != system.getTypeMask()) { - continue; - } - - // If so, process the entity - system.processEntity(entity, dt); - } - } - - } diff --git a/core/src/com/me/common/ecs/Entity.java b/core/src/com/me/common/ecs/Entity.java index 444014c..3645169 100644 --- a/core/src/com/me/common/ecs/Entity.java +++ b/core/src/com/me/common/ecs/Entity.java @@ -26,17 +26,27 @@ public final class Entity { } public void activate() { - this.active = true; + active = true; + engine.activateEntity(this); } public void deactivate() { - this.active = false; + active = false; + engine.deactivateEntity(this); + } + + public void remove() { + engine.removeEntity(this); } public Engine getEngine() { return this.engine; } + public boolean hasComponent(Class clazz) { + return ComponentType.getComponentType(clazz).isTypeInMask(componentBits); + } + public T getComponent(Class clazz) { return engine.getEntityComponent(this, clazz); } diff --git a/core/src/com/me/common/ecs/EntitySystem.java b/core/src/com/me/common/ecs/EntitySystem.java index 8a81d08..51f8ecc 100644 --- a/core/src/com/me/common/ecs/EntitySystem.java +++ b/core/src/com/me/common/ecs/EntitySystem.java @@ -1,25 +1,36 @@ package com.me.common.ecs; +import com.badlogic.gdx.utils.Array; + public abstract class EntitySystem { private long typeBits; + protected Array entities; + public EntitySystem(Class... components) { typeBits = ComponentType.getMaskBits(components); - } - - /** - * @return the type mask for this system. Only entities containing all component types specified - * by the mask will be processed by it. - */ - public long getTypeMask() { - return typeBits; + entities = new Array<>(); } public void preProcessing() {} + public Array getEntities() { + return entities; + } + + public void processEntities(float dt) { + for (int i = 0, n = getEntities().size; i < n; i++) { + processEntity(entities.get(i), dt); + } + } + public abstract void processEntity(Entity entity, float dt); public void postProcessing() {} + public boolean interestedIn(Entity entity) { + return (entity.getComponentBits() & typeBits) == typeBits; + } + }