Skip to content

Commit

Permalink
Implement entity listener priority
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton Gustafsson committed Jun 9, 2015
1 parent af13dc9 commit f9152e6
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 105 deletions.
224 changes: 119 additions & 105 deletions ashley/src/com/badlogic/ashley/core/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import com.badlogic.ashley.signals.Signal;
import com.badlogic.ashley.utils.ImmutableArray;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Bits;
import com.badlogic.gdx.utils.LongMap;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectMap.Entry;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.SnapshotArray;

Expand All @@ -45,7 +45,8 @@
* @author Stefan Bachmann
*/
public class Engine {
private static SystemComparator comparator = new SystemComparator();
private static SystemComparator systemComparator = new SystemComparator();
private static Family empty = Family.all().get();

private Array<Entity> entities;
private ImmutableArray<Entity> immutableEntities;
Expand All @@ -61,8 +62,9 @@ public class Engine {
private ObjectMap<Family, Array<Entity>> families;
private ObjectMap<Family, ImmutableArray<Entity>> immutableFamilies;

private SnapshotArray<EntityListener> entityListeners;
private ObjectMap<Family,SnapshotArray<EntityListener>> familyListeners;
private SnapshotArray<EntityListenerData> entityListeners;
private ObjectMap<Family, Bits> entityListenerMasks;


private final Listener<Entity> componentAdded;
private final Listener<Entity> componentRemoved;
Expand All @@ -88,8 +90,8 @@ public Engine(){
systemsByClass = new ObjectMap<Class<?>, EntitySystem>();
families = new ObjectMap<Family, Array<Entity>>();
immutableFamilies = new ObjectMap<Family, ImmutableArray<Entity>>();
entityListeners = new SnapshotArray<EntityListener>(false, 16);
familyListeners = new ObjectMap<Family,SnapshotArray<EntityListener>>();
entityListeners = new SnapshotArray<EntityListenerData>(true, 16);
entityListenerMasks = new ObjectMap<Family, Bits>();

componentAdded = new ComponentListener(this);
componentRemoved = new ComponentListener(this);
Expand Down Expand Up @@ -183,7 +185,7 @@ public void addSystem(EntitySystem system){
systemsByClass.put(systemType, system);
system.addedToEngineInternal(this);

systems.sort(comparator);
systems.sort(systemComparator);
}
}

Expand Down Expand Up @@ -224,8 +226,17 @@ public ImmutableArray<Entity> getEntitiesFor(Family family){
*
* The listener will be notified every time an entity is added/removed to/from the engine.
*/
public void addEntityListener(EntityListener listener) {
entityListeners.add(listener);
public void addEntityListener (EntityListener listener) {
addEntityListener(empty, 0, listener);
}

/**
* Adds an {@link EntityListener}. The listener will be notified every time an entity is added/removed
* to/from the engine. The priority determines in which order the entity listeners will be called. Lower
* value means it will get executed first.
*/
public void addEntityListener (int priority, EntityListener listener) {
addEntityListener(empty, priority, listener);
}

/**
Expand All @@ -234,25 +245,66 @@ public void addEntityListener(EntityListener listener) {
* The listener will be notified every time an entity is added/removed to/from the given family.
*/
public void addEntityListener(Family family, EntityListener listener) {
addEntityListener(family, 0, listener);
}

/**
* Adds an {@link EntityListener} for a specific {@link Family}. The listener will be notified every time an entity is
* added/removed to/from the given family. The priority determines in which order the entity listeners will be called. Lower
* value means it will get executed first.
*/
public void addEntityListener (Family family, int priority, EntityListener listener) {
registerFamily(family);
SnapshotArray<EntityListener> listeners = familyListeners.get(family);

if (listeners == null) {
listeners = new SnapshotArray<EntityListener>(false, 16);
familyListeners.put(family, listeners);
int insertionIndex = 0;
while (insertionIndex < entityListeners.size) {
if (entityListeners.get(insertionIndex).priority <= priority) {
insertionIndex++;
} else {
break;
}
}

// Shift up bitmasks by one step
for (Bits mask : entityListenerMasks.values()) {
for (int k = mask.length(); k > insertionIndex; k--) {
if (mask.get(k - 1)) {
mask.set(k);
} else {
mask.clear(k);
}
}
mask.clear(insertionIndex);
}

listeners.add(listener);
entityListenerMasks.get(family).set(insertionIndex);

EntityListenerData entityListenerData = new EntityListenerData();
entityListenerData.listener = listener;
entityListenerData.priority = priority;
entityListeners.insert(insertionIndex, entityListenerData);
}

/**
* Removes an {@link EntityListener}
*/
public void removeEntityListener(EntityListener listener) {
entityListeners.removeValue(listener, true);
public void removeEntityListener (EntityListener listener) {
for (int i = 0; i < entityListeners.size; i++) {
EntityListenerData entityListenerData = entityListeners.get(i);
if (entityListenerData.listener == listener) {
// Shift down bitmasks by one step
for (Bits mask : entityListenerMasks.values()) {
for (int k = i, n = mask.length(); k < n; k++) {
if (mask.get(k + 1)) {
mask.set(k);
} else {
mask.clear(k);
}
}
}

for (SnapshotArray<EntityListener> familyListenerArray : familyListeners.values()) {
familyListenerArray.removeValue(listener, true);
entityListeners.removeIndex(i--);
}
}
}

Expand All @@ -279,29 +331,48 @@ public void update(float deltaTime){
updating = false;
}

private void updateFamilyMembership(Entity entity){
for (Entry<Family, Array<Entity>> entry : families.entries()) {
Family family = entry.key;
Array<Entity> familyEntities = entry.value;
int familyIndex = family.getIndex();


boolean belongsToFamily = entity.getFamilyBits().get(familyIndex);
boolean matches = family.matches(entity);
private void updateFamilyMembership (Entity entity, boolean removing) {
// Find families that the entity was added to/removed from, and fill
// the bitmasks with corresponding listener bits.
Bits addListenerBits = new Bits();
Bits removeListenerBits = new Bits();

for (Family family : entityListenerMasks.keys()) {
final int familyIndex = family.getIndex();
final Bits entityFamilyBits = entity.getFamilyBits();

boolean belongsToFamily = entityFamilyBits.get(familyIndex);
boolean matches = family.matches(entity) && !removing;

if (belongsToFamily != matches) {
final Bits listenersMask = entityListenerMasks.get(family);
final Array<Entity> familyEntities = families.get(family);
if (matches) {
addListenerBits.or(listenersMask);
familyEntities.add(entity);
entityFamilyBits.set(familyIndex);
} else {
removeListenerBits.or(listenersMask);
familyEntities.removeValue(entity, true);
entityFamilyBits.clear(familyIndex);
}
}
}

if (!belongsToFamily && matches) {
familyEntities.add(entity);
entity.getFamilyBits().set(familyIndex);
// Notify listeners; set bits match indices of listeners
notifying = true;
Object[] items = entityListeners.begin();

notifyFamilyListenersAdd(family, entity);
}
else if (belongsToFamily && !matches) {
familyEntities.removeValue(entity, true);
entity.getFamilyBits().clear(familyIndex);
for (int i = removeListenerBits.nextSetBit(0); i >= 0; i = removeListenerBits.nextSetBit(i + 1)) {
((EntityListenerData)items[i]).listener.entityRemoved(entity);
}

notifyFamilyListenersRemove(family, entity);
}
for (int i = addListenerBits.nextSetBit(0); i >= 0; i = addListenerBits.nextSetBit(i + 1)) {
((EntityListenerData)items[i]).listener.entityAdded(entity);
}

entityListeners.end();
notifying = false;
}

protected void removeEntityInternal(Entity entity) {
Expand All @@ -314,83 +385,23 @@ protected void removeEntityInternal(Entity entity) {
removed = true;
}

if(!entity.getFamilyBits().isEmpty()){
for (Entry<Family, Array<Entity>> entry : families.entries()) {
Family family = entry.key;
Array<Entity> familyEntities = entry.value;

if(family.matches(entity)){
familyEntities.removeValue(entity, true);
entity.getFamilyBits().clear(family.getIndex());
notifyFamilyListenersRemove(family, entity);
}
}
}
updateFamilyMembership(entity, true);

entity.componentAdded.remove(componentAdded);
entity.componentRemoved.remove(componentRemoved);
entity.componentOperationHandler = null;

notifying = true;
Object[] items = entityListeners.begin();
for (int i = 0, n = entityListeners.size; i < n; i++) {
EntityListener listener = (EntityListener)items[i];
listener.entityRemoved(entity);
}
entityListeners.end();
notifying = false;

entity.uuid = 0L;
}

protected void addEntityInternal(Entity entity) {
entities.add(entity);
entitiesById.put(entity.getId(), entity);

updateFamilyMembership(entity);
updateFamilyMembership(entity, false);

entity.componentAdded.add(componentAdded);
entity.componentRemoved.add(componentRemoved);
entity.componentOperationHandler = componentOperationHandler;

notifying = true;
Object[] items = entityListeners.begin();
for (int i = 0, n = entityListeners.size; i < n; i++) {
EntityListener listener = (EntityListener)items[i];
listener.entityAdded(entity);
}
entityListeners.end();
notifying = false;
}

private void notifyFamilyListenersAdd(Family family, Entity entity) {
SnapshotArray<EntityListener> listeners = familyListeners.get(family);

if (listeners != null) {
notifying = true;
Object[] items = listeners.begin();
for (int i = 0, n = listeners.size; i < n; i++) {
EntityListener listener = (EntityListener)items[i];
listener.entityAdded(entity);
}
listeners.end();
notifying = false;
}
}

private void notifyFamilyListenersRemove(Family family, Entity entity) {
SnapshotArray<EntityListener> listeners = familyListeners.get(family);

if (listeners != null) {
notifying = true;
Object[] items = listeners.begin();
for (int i = 0, n = listeners.size; i < n; i++) {
EntityListener listener = (EntityListener)items[i];
listener.entityRemoved(entity);
}
listeners.end();
notifying = false;
}
}

private ImmutableArray<Entity> registerFamily(Family family) {
Expand All @@ -401,12 +412,10 @@ private ImmutableArray<Entity> registerFamily(Family family) {
immutableEntitiesInFamily = new ImmutableArray<Entity>(familyEntities);
families.put(family, familyEntities);
immutableFamilies.put(family, immutableEntitiesInFamily);
entityListenerMasks.put(family, new Bits());

for(Entity e : this.entities){
if(family.matches(e)) {
familyEntities.add(e);
e.getFamilyBits().set(family.getIndex());
}
for (Entity entity : this.entities){
updateFamilyMembership(entity, false);
}
}

Expand Down Expand Up @@ -460,7 +469,7 @@ public ComponentListener(Engine engine) {

@Override
public void receive(Signal<Entity> signal, Entity object) {
engine.updateFamilyMembership(object);
engine.updateFamilyMembership(object, false);
}
}

Expand Down Expand Up @@ -533,6 +542,11 @@ protected ComponentOperation newObject() {
}
}

private static class EntityListenerData {
public EntityListener listener;
public int priority;
}

private static class SystemComparator implements Comparator<EntitySystem>{
@Override
public int compare(EntitySystem a, EntitySystem b) {
Expand Down
Loading

0 comments on commit f9152e6

Please sign in to comment.