Skip to content

Commit

Permalink
Adds EntityListeners on a per family basis
Browse files Browse the repository at this point in the history
  • Loading branch information
dsaltares committed Sep 13, 2014
1 parent ddfd55d commit f90d129
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 31 deletions.
125 changes: 96 additions & 29 deletions ashley/src/com/badlogic/ashley/core/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public class Engine {
private ObjectMap<Family, ImmutableArray<Entity>> immutableFamilies;
/** A collection of entity added/removed event listeners */
private SnapshotArray<EntityListener> listeners;
/** Entity added/removed event listeners per family */
private ObjectMap<Family,SnapshotArray<EntityListener>> familyListeners;

/** A listener for the Engine that's called every time a component is added. */
private final Listener<Entity> componentAdded;
Expand All @@ -79,6 +81,7 @@ public Engine(){
families = new ObjectMap<Family, Array<Entity>>();
immutableFamilies = new ObjectMap<Family, ImmutableArray<Entity>>();
listeners = new SnapshotArray<EntityListener>(false, 16);
familyListeners = new ObjectMap<Family,SnapshotArray<EntityListener>>();

componentAdded = new Listener<Entity>(){
@Override
Expand All @@ -103,12 +106,7 @@ public void receive(Signal<Entity> signal, Entity object) {
public void addEntity(Entity entity){
entities.add(entity);

for (Entry<Family, Array<Entity>> entry : families.entries()) {
if(entry.key.matches(entity)){
entry.value.add(entity);
entity.getFamilyBits().set(entry.key.getIndex());
}
}
updateFamilyMembership(entity);

entity.componentAdded.add(componentAdded);
entity.componentRemoved.add(componentRemoved);
Expand Down Expand Up @@ -186,34 +184,45 @@ public ImmutableArray<EntitySystem> getSystems() {
* Returns immutable collection of entities for the specified {@link Family}. Will return the same instance every time.
*/
public ImmutableArray<Entity> getEntitiesFor(Family family){
Array<Entity> entities = families.get(family, null);
if(entities == null){
entities = new Array<Entity>(false, 16);
for(Entity e:this.entities){
if(family.matches(e)) {
entities.add(e);
e.getFamilyBits().set(family.getIndex());
}
}
families.put(family, entities);
immutableFamilies.put(family, new ImmutableArray<Entity>(entities));
}

registerFamily(family);
return immutableFamilies.get(family);
}

/**
* Adds an {@link EntityListener}
* Adds an {@link EntityListener}.
*
* The listener will be notified every time an entity is added/removed to/from the engine.
*/
public void addEntityListener(EntityListener listener) {
listeners.add(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.
*/
public void addEntityListener(Family family, EntityListener listener) {
registerFamily(family);
SnapshotArray<EntityListener> listeners = familyListeners.get(family);

if (listeners == null) {
listeners = new SnapshotArray<EntityListener>(false, 16);
familyListeners.put(family, listeners);
}

listeners.add(listener);
}

/**
* Removes an {@link EntityListener}
*/
public void removeEntityListener(EntityListener listener) {
listeners.removeValue(listener, true);

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

/**
Expand All @@ -234,16 +243,25 @@ public void update(float deltaTime){

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


boolean belongsToFamily = entity.getFamilyBits().get(familyIndex);
boolean matches = family.matches(entity);

if (!belongsToFamily && matches) {
entry.value.add(entity);
entity.getFamilyBits().set(entry.key.getIndex());
entities.add(entity);
entity.getFamilyBits().set(familyIndex);

notifyFamilyListenersAdd(family, entity);
}
else if (belongsToFamily && !matches) {
entry.value.removeValue(entity, true);
entity.getFamilyBits().clear(entry.key.getIndex());
entities.removeValue(entity, true);
entity.getFamilyBits().clear(familyIndex);

notifyFamilyListenersRemove(family, entity);
}
}
}
Expand All @@ -263,9 +281,13 @@ private void removeEntityInternal(Entity entity) {

if(!entity.getFamilyBits().isEmpty()){
for (Entry<Family, Array<Entity>> entry : families.entries()) {
if(entry.key.matches(entity)){
entry.value.removeValue(entity, true);
entity.getFamilyBits().clear(entry.key.getIndex());
Family family = entry.key;
Array<Entity> entities = entry.value;

if(family.matches(entity)){
entities.removeValue(entity, true);
entity.getFamilyBits().clear(family.getIndex());
notifyFamilyListenersRemove(family, entity);
}
}
}
Expand All @@ -281,6 +303,51 @@ private void removeEntityInternal(Entity entity) {
listeners.end();
}

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

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

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

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

private Array<Entity> registerFamily(Family family) {
Array<Entity> entities = families.get(family);

if (entities == null) {
entities = new Array<Entity>(false, 16);
families.put(family, entities);
immutableFamilies.put(family, new ImmutableArray<Entity>(entities));

for(Entity e : this.entities){
if(family.matches(e)) {
entities.add(e);
e.getFamilyBits().set(family.getIndex());
}
}
}

return entities;
}

private static class SystemComparator implements Comparator<EntitySystem>{
@Override
public int compare(EntitySystem a, EntitySystem b) {
Expand Down
8 changes: 6 additions & 2 deletions ashley/src/com/badlogic/ashley/core/EntityListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
*/
public interface EntityListener {
/**
* Called whenever an {@link Entity} is added to {@link Engine}
* Called whenever an {@link Entity} is added to {@link Engine} or a specific {@link Family}
*
* See {@link Engine#addEntityListener(EntityListener)} and {@link Engine#addEntityListener(Family, EntityListener)}}
*
* @param entity
*/
public void entityAdded(Entity entity);

/**
* Called whenever an {@link Entity} is removed from {@link Engine}
* Called whenever an {@link Entity} is removed from {@link Engine} or a specific {@link Family}
*
* See {@link Engine#addEntityListener(EntityListener)} and {@link Engine#addEntityListener(Family, EntityListener)}}
*
* @param entity
*/
Expand Down
67 changes: 67 additions & 0 deletions ashley/tests/com/badlogic/ashley/core/EngineTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,71 @@ public void entitySystemRemovalWhileIterating() {
assertEquals(1, entities.get(i).getComponent(CounterComponent.class).counter);
}
}

@Test
public void familyListener() {
Engine engine = new Engine();

EntityListenerMock listenerA = new EntityListenerMock();
EntityListenerMock listenerB = new EntityListenerMock();

Family familyA = Family.getFor(ComponentA.class);
Family familyB = Family.getFor(ComponentB.class);

engine.addEntityListener(familyA, listenerA);
engine.addEntityListener(familyB, listenerB);

Entity entity1 = new Entity();
engine.addEntity(entity1);

assertEquals(0, listenerA.addedCount);
assertEquals(0, listenerB.addedCount);


Entity entity2 = new Entity();
engine.addEntity(entity2);

assertEquals(0, listenerA.addedCount);
assertEquals(0, listenerB.addedCount);

entity1.add(new ComponentA());

assertEquals(1, listenerA.addedCount);
assertEquals(0, listenerB.addedCount);

entity2.add(new ComponentB());

assertEquals(1, listenerA.addedCount);
assertEquals(1, listenerB.addedCount);

entity1.remove(ComponentA.class);

assertEquals(1, listenerA.removedCount);
assertEquals(0, listenerB.removedCount);

engine.removeEntity(entity2);

assertEquals(1, listenerA.removedCount);
assertEquals(1, listenerB.removedCount);

engine.removeEntityListener(listenerB);

engine.addEntity(entity2);

assertEquals(1, listenerA.addedCount);
assertEquals(1, listenerB.addedCount);

entity1.add(new ComponentB());
entity1.add(new ComponentA());

assertEquals(2, listenerA.addedCount);
assertEquals(1, listenerB.addedCount);

engine.removeAllEntities();

assertEquals(2, listenerA.removedCount);
assertEquals(1, listenerB.removedCount);

engine.addEntityListener(listenerB);
}
}

0 comments on commit f90d129

Please sign in to comment.