Simply entity refreshing.

Consolidate toRemove, toEnable toDisable into toRefresh, move logic of
whether an entity should be added or removed to an EntitySystem into
EntitySystem.

Entities now have a systemBits member which represents which
systems they're currently a part of and is used by the refresh() logic
in EntitySystem.

Adding or removing components from an entity after it has been created
and activated will now add or remote the entity from relevant systems.
This commit is contained in:
Matt Low 2020-01-27 03:10:42 +04:00
parent 83223e5dc2
commit f366c4b808
4 changed files with 95 additions and 104 deletions

View File

@ -66,24 +66,14 @@ public class Engine {
return entityManager.create(); return entityManager.create();
} }
protected void activateEntity(Entity entity) {
entityManager.activate(entity);
}
protected void deactivateEntity(Entity entity) {
entityManager.deactivate(entity);
}
protected void removeEntity(Entity entity) {
entityManager.remove(entity);
}
public void callEvent(Event event) { public void callEvent(Event event) {
listenerRegistry.callEvent(event); listenerRegistry.callEvent(event);
} }
protected void refreshEntity(Entity entity) {
entityManager.queueRefresh(entity);
}
protected void removeAllEntityComponents(int entityId) { protected void removeAllEntityComponents(int entityId) {
for (int i = 0; i < components.length; i++) { for (int i = 0; i < components.length; i++) {
components[i].insert(entityId, null); components[i].insert(entityId, null);

View File

@ -6,16 +6,19 @@ public final class Entity {
private static long nextUniqueId = 0; private static long nextUniqueId = 0;
private Engine engine; private Engine engine;
protected final int id;
protected int id;
protected long componentBits; protected long componentBits;
protected long systemEnabledBits;
protected boolean active;
protected boolean removed;
protected boolean pendingRefresh;
private long uniqueId; private long uniqueId;
private boolean active;
protected Entity(Engine engine) { protected Entity(Engine engine) {
this.engine = engine; this.engine = engine;
this.active = false;
this.id = nextId++; this.id = nextId++;
} }
@ -33,16 +36,17 @@ public final class Entity {
public void activate() { public void activate() {
active = true; active = true;
engine.activateEntity(this); refresh();
} }
public void deactivate() { public void deactivate() {
active = false; active = false;
engine.deactivateEntity(this); refresh();
} }
public void remove() { public void remove() {
engine.removeEntity(this); removed = true;
refresh();
} }
public Engine getEngine() { public Engine getEngine() {
@ -59,10 +63,12 @@ public final class Entity {
public void addComponent(Component component) { public void addComponent(Component component) {
engine.addEntityComponent(this, component); engine.addEntityComponent(this, component);
refresh();
} }
public void removeComponent(Component component) { public void removeComponent(Component component) {
engine.removeEntityComponent(this, component); engine.removeEntityComponent(this, component);
refresh();
} }
protected void addComponentType(ComponentType type) { protected void addComponentType(ComponentType type) {
@ -73,9 +79,27 @@ public final class Entity {
componentBits &= ~type.bits; componentBits &= ~type.bits;
} }
protected void addSystemEnabledBit(long bit) {
systemEnabledBits |= bit;
}
protected void removeSystemEnabledBit(long bit) {
systemEnabledBits &= ~bit;
}
public void refresh() {
if (!pendingRefresh) {
engine.refreshEntity(this);
pendingRefresh = true;
}
}
protected void reset() { protected void reset() {
componentBits = 0; componentBits = 0;
systemEnabledBits = 0;
active = false; active = false;
removed = false;
pendingRefresh = false;
} }
protected void updateUniqueId() { protected void updateUniqueId() {

View File

@ -7,114 +7,63 @@ final class EntityManager {
private Engine engine; private Engine engine;
protected Array<Entity> entities; protected Array<Entity> entities;
protected Array<Entity> toActivate;
protected Array<Entity> toDeactivate;
protected Array<Entity> toRemove;
protected Array<Entity> removedEntities; protected Array<Entity> removedEntities;
protected Array<Entity> toRefresh;
public EntityManager(Engine engine) { EntityManager(Engine engine) {
this.engine = engine; this.engine = engine;
this.entities = new Array<>(); this.entities = new Array<>(false, 16);
this.toActivate = new Array<>();
this.toDeactivate = new Array<>();
this.toRemove = new Array<>();
this.removedEntities = new Array<>(false, 16); this.removedEntities = new Array<>(false, 16);
} this.toRefresh = new Array<>(false, 16);
public void update() {
activatePending();
deactivatePending();
removePending();
} }
public Entity create() { public Entity create() {
Entity entity; Entity entity;
if (!removedEntities.isEmpty()) { if (!removedEntities.isEmpty()) {
entity = removedEntities.removeIndex(0); entity = removedEntities.removeIndex(0);
entity.reset();
} else { } else {
entity = new Entity(engine); entity = new Entity(engine);
} }
entity.reset();
entity.updateUniqueId(); entity.updateUniqueId();
entities.add(entity); entities.add(entity);
return entity; return entity;
} }
public void remove(Entity entity) { public void update() {
toRemove.add(entity); if (toRefresh.isEmpty()) {
}
public void activate(Entity entity) {
toActivate.add(entity);
}
public void deactivate(Entity entity) {
toDeactivate.add(entity);
}
private void removePending() {
if (toRemove.isEmpty()) {
return; return;
} }
for (Entity entity : toRemove) { for (Entity entity : toRefresh) {
refreshEntity(entity);
entity.pendingRefresh = false;
}
toRefresh.clear();
}
public void queueRefresh(Entity entity) {
toRefresh.add(entity);
}
private void refreshEntity(Entity entity) {
for (BaseSystem system : engine.systems) {
if (!(system instanceof EntitySystem)) {
continue;
}
((EntitySystem) system).refresh(entity);
}
if (entity.removed) {
engine.removeAllEntityComponents(entity.id); engine.removeAllEntityComponents(entity.id);
entities.removeValue(entity, true); entities.removeValue(entity, true);
removeEntityFromSystems(entity);
removedEntities.add(entity); removedEntities.add(entity);
} }
toRemove.clear();
} }
private void activatePending() {
if (toActivate.isEmpty()) {
return;
}
for (Entity entity : toActivate) {
addEntityToSystems(entity);
}
toActivate.clear();
}
private void deactivatePending() {
if (toDeactivate.isEmpty()) {
return;
}
for (Entity entity : toDeactivate) {
removeEntityFromSystems(entity);
}
toDeactivate.clear();
}
private void removeEntityFromSystems(Entity entity) {
for (BaseSystem system : engine.systems) {
if (!(system instanceof EntitySystem)) {
continue;
}
EntitySystem entitySystem = (EntitySystem) system;
if (entitySystem.interestedIn(entity)) {
entitySystem.removeEntity(entity);
}
}
}
private void addEntityToSystems(Entity entity) {
for (BaseSystem system : engine.systems) {
if (!(system instanceof EntitySystem)) {
continue;
}
EntitySystem entitySystem = (EntitySystem) system;
if (entitySystem.interestedIn(entity)) {
entitySystem.addEntity(entity);
}
}
}
} }

View File

@ -2,14 +2,22 @@ package com.me.common.ecs;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import java.util.HashMap;
import java.util.Map;
public abstract class EntitySystem extends BaseSystem { public abstract class EntitySystem extends BaseSystem {
private static final Map<Class<? extends EntitySystem>, Long> systemBits = new HashMap<>();
private static long nextBit = 1l;
private long systemBit;
private long typeBits; private long typeBits;
protected Array<Entity> entities; protected Array<Entity> entities;
public EntitySystem(Engine engine, Class<? extends Component>... components) { public EntitySystem(Engine engine, Class<? extends Component>... components) {
super(engine); super(engine);
this.systemBit = getBitFor(getClass());
this.typeBits = ComponentType.getMaskBits(components); this.typeBits = ComponentType.getMaskBits(components);
this.entities = new Array<>(true, 16, Entity.class); this.entities = new Array<>(true, 16, Entity.class);
} }
@ -26,16 +34,36 @@ public abstract class EntitySystem extends BaseSystem {
public abstract void processEntity(Entity entity, float dt); public abstract void processEntity(Entity entity, float dt);
public boolean interestedIn(Entity entity) { protected void refresh(Entity entity) {
return (entity.componentBits & typeBits) == typeBits; boolean enabled = (entity.systemEnabledBits & systemBit) == systemBit;
boolean interested = (entity.componentBits & typeBits) == typeBits;
if (interested && !enabled && entity.active && !entity.removed) {
add(entity);
} else if (enabled && (!interested || !entity.active || entity.removed)) {
remove(entity);
}
} }
protected void addEntity(Entity entity) { private void add(Entity entity) {
entity.addSystemEnabledBit(systemBit);
entities.add(entity); entities.add(entity);
} }
protected void removeEntity(Entity entity) { private void remove(Entity entity) {
entity.removeSystemEnabledBit(systemBit);
entities.removeValue(entity, true); entities.removeValue(entity, true);
} }
static long getBitFor(Class<? extends EntitySystem> es) {
Long bits = systemBits.get(es);
if (bits == null) {
bits = nextBit;
nextBit <<= 1;
systemBits.put(es, bits);
}
return bits;
}
} }