Move entity processing loop to systems
Store cache of interested entities in each system. Perform entity activation, deactivation and removal outside of the update loop. Instead of throwing an exception when trying to get a Component from ComponentBag that is out of range, just return null. This is to allow systems to try getting a Component it isn't registered for. Fixed ComponentType.isTypeInMask
This commit is contained in:
parent
d8b82c4fee
commit
90e5ff36e7
@ -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];
|
||||
}
|
||||
|
@ -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<? extends Component> component) {
|
||||
|
@ -7,6 +7,10 @@ import com.me.common.ecs.event.Listener;
|
||||
public class Engine {
|
||||
|
||||
private Array<Entity> entities;
|
||||
private Array<Entity> toActivate;
|
||||
private Array<Entity> toDeactivate;
|
||||
private Array<Entity> toRemove;
|
||||
|
||||
private ComponentBag[] components;
|
||||
private Array<EntitySystem> 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<Entity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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<? extends Component> clazz) {
|
||||
return ComponentType.getComponentType(clazz).isTypeInMask(componentBits);
|
||||
}
|
||||
|
||||
public <T extends Component> T getComponent(Class<T> clazz) {
|
||||
return engine.getEntityComponent(this, clazz);
|
||||
}
|
||||
|
@ -1,25 +1,36 @@
|
||||
package com.me.common.ecs;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
public abstract class EntitySystem {
|
||||
|
||||
private long typeBits;
|
||||
|
||||
protected Array<Entity> entities;
|
||||
|
||||
public EntitySystem(Class<? extends Component>... 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<Entity> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user