1
\$\begingroup\$

I am currently trying out Ashley and overall learning how an ECS works internally. What I currently don't understand is how Ashley in particular keeps its systems up-to-date by adding or removing entities based on the specified Family. See the following code:

  def main(args: Array[String]): Unit = {
    val engine = new Engine()

    engine.addSystem(new EntitySystem() {
      var entities: ImmutableArray[Entity] = _

      override def addedToEngine(engine: Engine) = {
        entities = engine.getEntitiesFor(Family.all(classOf[MyComponent]).get())
        println("updated entities")
      }

      override def update(deltaTime: Float) = {
        println(entities.size())
      }
    })

    engine.update(0F)
    engine.addEntity(produce())
    engine.update(0F)
    engine.addEntity(produce())
    engine.update(0F)
    engine.addEntity(produce())
  }

  def produce() = {
    val entity = new Entity()
    entity.add(new MyComponent)
    entity
  }

  final class MyComponent extends Component

Which regurgitates the following output:

updated entities
0
1
2

The entities object is an immutable wrapper around LibGDX's Array collection. The wrapped mutable variant isn't exposed either to the outside. There is no way this array can be mutated. The overrided addedToEngine method is only called when the EntitySystem was added to the Engine.

\$\endgroup\$
2
\$\begingroup\$

There is no way this array can be mutated.

Wrong. If you look at the source code of ImmutableArray you can see that one of the constructors takes an Array as a parameter and stores it as a variable:

private final Array<T> array;

public ImmutableArray (Array<T> array) {
    this.array = array;
}

The Engine object simply keeps a reference to this internal array and edits it whenever it updates the systems:

// In "FamilyManager" - called when you call getEntitiesFor(Family)
private ImmutableArray<Entity> registerFamily(Family family) {
    ImmutableArray<Entity> entitiesInFamily = immutableFamilies.get(family);

    if (entitiesInFamily == null) {
        Array<Entity> familyEntities = new Array<Entity>(false, 16);
        entitiesInFamily = new ImmutableArray<Entity>(familyEntities);
        families.put(family, familyEntities); // <-- Store mutable array
        immutableFamilies.put(family, entitiesInFamily); // <-- Store immutable array

        // some other unrelated stuff
    }

    return entitiesInFamily; // <-- Returns the immutable array
}

Then whenever an entity is added it simply does something like this (pseudo):

private void entityAdded(Entity entity) {
    for(Family family : getMatchingFamilies(entity)) {
        Array<Entity> entities = families.get(family);
        entities.add(entity);
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Agreed - it's immutable only in so far as someone with a reference to only the ImmutableArray object cannot add or remove items. \$\endgroup\$ – Pikalek Apr 8 '18 at 21:28
  • \$\begingroup\$ Thank you. I didn't notice that it was storing the mutable Array. \$\endgroup\$ – ExecutorService Apr 9 '18 at 17:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.