fireblade-engine / ecs

A dependency free, lightweight, fast Entity-Component System (ECS) implementation in Swift
MIT License
110 stars 11 forks source link

Iterate all entities in a `Nexus`? #32

Closed howlingblast closed 4 years ago

howlingblast commented 4 years ago

Hi there,

is there a way to iterate over all entities in a Nexus without the need for a family? There is no way to create a FamilyTraitSet for "all" components without explicitly giving all possible components types(!).

What I try to achieve: I have a component instance(!) and try to find out which entity holds that component. Something along these lines:

  func componentChanged(_ component: Component) { // this can be any component
       guard let entity = nexus.getEntity(ofComponent: component.identifier) else { return }
       ...
  }

with

  extension Nexus {

       func getEntity(ofComponent: ComponentIdenfier) -> Entity? {
           ...
       }
  }

Unfortunately Nexus.componentIdsByEntity is not available outside of the module and there is no EntityIterator provided by Nexus to iterate over all entities.

Thanks, Kaweh

ctreffs commented 4 years ago

Hi @reversepanda,

thanks for asking, you are right, there is currently now way of iterating over all entities in the Nexus without using a family. This is a design limitation, which I will lift, because there is actually no reason not to have it.

I will provide a patch version that enables this feature, very soon.

If you're interested in conversing about the design or implementation please feel free to join the Fireblade Discord server via this link https://discord.gg/AFdree (link will expire in 12 hours).

I would love to hear more about your usage of FirebladeECS and your input on it 🙂

Cheers, Chris

ctreffs commented 4 years ago

I've released a new version including an EntitiesIterator - see https://github.com/fireblade-engine/ecs/releases/tag/0.17.1

Please give some feedback if this helps solve your issue :-)

ctreffs commented 4 years ago

Hi @reversepanda,

I thought a little more about your problem and found a solution, that would allow you to find the entity of an arbitrary component instance without iterating over all entities.

func getEntity<C>(for component: C, in nexus: Nexus) -> Entity? where C: Component {
    let family = nexus.family(requires: C.self)

    if let (entity, _) = family.entityAndComponents.first(where: { (_, comp) in comp === component }) {
        return entity
    }

    return nil
}

This is actually possible even before the 0.17.1 release that added the EntitiesIterator and is much more performant, since it already provides knowledge about the component type via a family.

Have a try yourself, maybe this would even be a benefit to the library itself.

Love to hear your thoughts on that.

Cheers, Chris

howlingblast commented 4 years ago

Hi @ctreffs, thanks a lot for the fast response and solution. I'll give it a try today.

Your second solution works only if one knows the component's type at compile type. That's not the case for my use case though:

Because a component is a class I can pass it around as a reference of type Component. I'm doing this for my use case. This is what happens:


class MyComponent: Component {

      var myProp: Int { 
          didSet {
              notify(.componentUpdated(self))
          }
      }
}

enum Notification {

      // this is type erasing to be useable from any `Component`
      case componentUpdated(Component)
}

func notify(_ notification: Notification) {
       // will send to some kind of notificationcenter/publisher
}

I agree that it's not the best performance-wise and your second solution is definitely better if the situation can be handled at compile time, which is not the case for me. As I'm "just" developing a prototype right now, performance is not a concern for me, also I don't have a lot of entities (and the number won't increase much also).

I agree it's still better that FirebladeECS is pushing towards compile time checks to avoid performance issues down the road and you should probably keep that philosophy. Nevertheless for my particular use case I prefer the runtime/dynamic approach because I just trade development time vs. performance.

At least because "Component" is a class and not a value type I have the choice. 👍

Thanks again for the fast help, Kaweh

ctreffs commented 4 years ago

Hi @reversepanda,

glad to hear, that I could help you 👍

Nevertheless you brought my attention to a limitation of the family system, that I had not thought about. There is actually no reason why one would not be able to create a family from a dynamic component during runtime. The implementation currently relies on the ComponentIdentifier, which is derived from the static type of the component but will be available during runtime as well. There is just no public API yet that provides the ability to create such a family 🤔

But I will change that 👍
Thanks for pointing that out.

If you are interested I would love to see what you are doing with the ECS, so reach out to me on Twitter (https://twitter.com/ChrisDailyGrind) or via Discord (https://discord.gg/AFdree).

For now I will close the issue, since the question could be answered 🙂