2
\$\begingroup\$

I am currently creating an Entity System, in C++, it is almost completed (I have all the code there, I just have to add a few things and test it). The only thing is, I can't figure out how to implement some features. This Entity System is based off a bit from the Artemis framework, however it is different.

I'm not sure if I'll be able to type this out the way my head processing it. I'm going to basically ask whether I should do something over something else.

Okay, now I'll give a little detail on my Entity System itself. Here are the basic classes that my Entity System uses to actually work:

  • Entity - An Id (and some methods to add/remove/get/etc Components)
  • Component - An empty abstract class
  • ComponentManager - Manages ALL components for ALL entities within a Scene
  • EntitySystem - Processes entities with specific components
  • Aspect - The class that is used to help determine what Components an Entity must contain so a specific EntitySystem can process it
  • EntitySystemManager - Manages all EntitySystems within a Scene
  • EntityManager - Manages entities (i.e. holds all Entities, used to determine whether an Entity has been changed, enables/disables them, etc.)
  • EntityFactory - Creates (and destroys) entities and assigns an ID to them
  • Scene - Contains an EntityManager, EntityFactory, EntitySystemManager and ComponentManager. Has functions to update and initialise the scene.

Now here's the issue(s)... in order for an EntitySystem to efficiently know when to check if an Entity is valid for processing (so I can add it to a specific EntitySystem that wants the Entity), it must recieve a message from the EntityManager (after a call of activate(Entity& e)). Similarly the EntityManager must know when an Entity is destroyed from the EntityFactory and the ComponentManager must know when an Entity is created AND destroyed from/in a Scene.

I do have a Listener/Observer pattern implemented at the moment, but with this pattern I may remove a Listener (which is this case is dependent on the method being called). I mainly have this implemented for specific things related to a game, i.e. Teams, Tagging of entities, etc. So... I was thinking maybe I should call a private method (using friend classes) to send out when an Entity has been activated, deleted, etc.

i.e. taken from my EntityFactory

void EntityFactory::killEntity(Entity& e)
{
      // if the entity doesn't exsist in the entity manager within the scene
      if(!getScene()->getEntityManager().doesExsist(e))
      {
         return; // go back to the caller! (should throw an exception or something..)
      }

      // tell the ComponentManager and the EntityManager that we killed an Entity
      getScene()->getComponentManager().doOnEntityWillDie(e);
      getScene()->getEntityManager().doOnEntityWillDie(e);

      // notify the listners
      for(Mouth::iterator i = getMouth().begin(); i != getMouth().end(); ++i)
      {
              (*i)->onEntityWillDie(*this, e);
      }

      _idPool.addId(e.getId()); // add the ID to the pool
      delete &e;                // delete the entity
}

As you can see on the lines where I am telling the ComponentManager and the EntityManager that an Entity will die, I am calling a method to make sure it handles it appropriately. Now I realise I could do this without calling it explicitly, with the help of that for loop notifying all listener objects connected to the EntityFactory's Mouth (an object used to tell listeners that there's an event), however is this a good idea (good design, or what)?

I've gone over the PROS and CONS, I just can't decide what I want to do.

Calling Explicitly:

PROS

  • Faster?
  • Since these functions are explicitly called, they can't be "removed"

CONS

  • Not flexible
  • Bad design? (friend functions)

Calling through Listener objects

(i.e. ComponentManager/EntityManager inherits from a EntityFactoryListener)

PROS

  • More Flexible?
  • Better Design?

CONS

  • Slower? (virtual functions)
  • Listeners can be removed, i.e. may be removed and not get called again during the program, which could cause in a crash.

P.S. If you wish to view my current source code, I am hosting it on BitBucket.

\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

What's critical to understand is that any event dispatcher / listener type system, already has a loop running inside it's implementation (or is part of a greater loop such as your update loop), polling periodically to see whether any event dispatches have been received, and in that case direct the call to the attached listeners / handlers. The only difference between that loop and your code is that you don't generally see that loop, if it's a language feature or part of an external library. But it is there.

Take for instance Java and C#. In C#, there is a delegates system which handles this polling internally, under the hood. In Java, however, there are no special keywords or mechanisms to do this -- you simply set it up yourself. The Java event dispatch/listen paradigm is very useful to understand, because it shows you exactly what is going on under the hood in languages like C# and Flash/AS3/JS.

So my advice to you is: If you've no explicit event subsystems already in place, then just poll per update. That way, you have just one loop -- your game loop. Of course, you can wrap this polling per update up in an event listener system if that is more comfortable for you (see my comments in the final paragraph). Ultimately, it's much the same thing. If you already have background code managing an event dispatcher / handler subsystem, then make use of what is already there.

The reason I suggest that you avoid additional looping is that conditionals are costly operations. So the fewer loop iterations, the better.

Overall, I would always recommend that you set up and use an event listener system if possible, as architecturally this is a good choice and abstracts this concept into it's own set of classes. Event listeners will run through an extant list of functor-based handlers to call, rather than explicitly checking some constant n memory locations for nullity first before referencing the functor in question. IMO this alone is worth the trouble.

You might also consider something like the signals pattern.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for that. :) Didn't realise that I was doing things quite in the wrong manner. \$\endgroup\$ Dec 7, 2012 at 4:22
  • 1
    \$\begingroup\$ C# delegates don't imply an internal loop unless you count traversing the invocation list as part of that. It's important to differentiate between a message-pump type of dispatcher and an immediate dispatcher that's just keeping lists of function pointers. If you go with the message pump approach, you may always have to be working around the fact that on each update you're probably going to lose a lot of state. Edit Not counting Windows Forms of course, but it's implied that the form classes have internal native loops listening for win32 messages. \$\endgroup\$ Dec 7, 2012 at 17:03
3
\$\begingroup\$

I am also using Artemis as the basis for a C++ game and I had a similar consideration. Looking at your source code and knowing Artemis, EntitySystems will want to be notified about Entities being added/removed/activated/deactived. Rather than propogate this information through events you could use either signal/slots (boost has an implementation and you appear to have that as a dependency already) or simple function pointers.

For instance, your EntityFactory could have "lifecycle" signal:

    enum SignalType {
        ENTITY_ADDED = 0,
        ENTITY_CHANGED = 1,
        ENTITY_DELETED = 2,
        ENTITY_ENABLED = 3,
        ENTITY_DISABLED = 4
    };

typedef boost::signals2::signal<void (SignalType, EntityPtr)> EntityLifecycleSignal;

Any class interested in lifecycle updates would need to implement a slot method:

 void EntityStatusChange(SignalType signalType, EntityPtr entityPtr);

Then your EntityFactory can raise the signal as appropriate:

// tell the ComponentManager and the EntityManager that we killed an Entity
  getScene()->getComponentManager().doOnEntityWillDie(e);
  getScene()->getEntityManager().doOnEntityWillDie(e);
entityLifecycleSignal(ENTITY_DELETED, e);

Another thing to consider is changing it such that each EntitySystem maintains an intrusive linked list of entities that it cares about (determined via Aspects) and a single entity is capable of being added to multiple EntitySystems. When an entity is destroyed it can simply be unliked in place without notifying the systems. So your code example might look something like this:

  // tell the ComponentManager and the EntityManager that we killed an Entity
  getScene()->getComponentManager().doOnEntityWillDie(e);
  getScene()->getEntityManager().doOnEntityWillDie(e);

  //Unlink from all EntitySystems
  e->entitySystem1Link.Unlink();
  e->entitySystem2Link.Unlink();
  .....

  _idPool.addId(e.getId()); // add the ID to the pool
  delete &e;                // delete the entity

You might also add said logic to the destructor. Initially I rolled my own intrusive linked list implementation but then, wisely I think, switched to the one discussed here: http://www.codeofhonor.com/blog/avoiding-game-crashes-related-to-linked-lists

With this strategy an entity can easily be added and removed from one or more EntitySystems without the need for event notifications or even a multitarget callback. Looking at your source though, it appears you intend to give EntitySystems a hook to do custom code when an entity is removed:

            void EntitySystem::remove(Entity& e)
            {
                    _entitiesToProcess[e.getId()] = NULL;

                    // call the virtual fn
                    onRemovedEntity(e);
            }

If you want to maintain this hook you are back to multitarget callback.

\$\endgroup\$
1
  • \$\begingroup\$ Isn't boost::signal/boost::signals2::signal slower than virtual functions/function pointers? \$\endgroup\$ Dec 8, 2012 at 4:52
0
\$\begingroup\$

Don't Change Anything

You missed one serious PRO of calling your entity manager explicitly: easy predictability. Well, you did mention "can't be removed," but it's so much more than that.

Given that in order for this entire entity system to work, you require that EntityFactory and EntityManager collaborate to manage the lifecycle of Entity objects, and other objects being able to subscribe to Entity::killEntity events is merely a way for other code to hook into this event, your initial approach is what I would have done.

What if you unintentionally add a listener in the wrong order? What if you unintentionally remove the EntityManager listener? Yeah, the answer to those questinos are "Don't do that!" but separating a call that is absolutely required from calls that are purely optional is a good, and pretty basic, defensive programming technique. Don't cause problems for yourself in order to adhere to some sort of pure architecture.

Maybe Change Something

You might want to actually consider putting the entityManager->doOnEntityWillDie(e) calls after you've iterated through the listeners. This would be treating the entityManager->doOnEntityWillDie(e) as part of the "dying" process by convention.

You could even provide willDie and hasDied signal slots/virtual calls.

\$\endgroup\$
6
  • \$\begingroup\$ hmm, I'm not entirely sure I should do this. This would cause more loops in my program, which as stated by Nick Wiggil, is a bad thing to do. As it is expensive. I'm thinking of throwing the Listener objects out and going for an approach that Artemis used, with an EntityObserver abstract base class (interface in Java) for notifications on an Entity's state. e.g. onEntityAdded(Entity& e), onEntityWillDie(Entity& e), onEntityEnabed(Entity& e)... etc. \$\endgroup\$ Dec 8, 2012 at 5:02
  • \$\begingroup\$ @miguel.martin I'm not really sure how what you're proposing is different. Which classes are implementing these pure virtual functions? And the idea that loops in a program are bad is a very shallow way to think about writing efficient code. How many times are you looping over each object? Are you using the same regions of memory in batches? Are you running a lot of the same code all at once to make optimal use of your instruction cache? \$\endgroup\$ Dec 8, 2012 at 6:26
  • \$\begingroup\$ I'm sorry, I just got really confused as I got told to do a particular thing and then I get told the complete opposite. Basically an EntityObserver may be added to a Scene object, the Scene object will maintain all entities (but in actual fact the EntityManager will be doing this). So if you call activate(Entity&) on the Scene, it will add the Entity to an array, and before it updates the EntitySystemManager that is attached to the Scene, it will loop through all the activated entities, refreshed, etc. and call these abstract methods. I'm sorry if this doesn't make much sense. \$\endgroup\$ Dec 8, 2012 at 10:06
  • \$\begingroup\$ I may however, try implementing both of these, or any alternates ways I can think of and profile it to evidently prove that Listener objects are slowing everything down (or whatever else I think up of). \$\endgroup\$ Dec 8, 2012 at 10:08
  • \$\begingroup\$ It seems as the C# port for Artemis actually does something similar to what I was originally planning to do... interesting. github.com/thelinuxlich/artemis_CSharp \$\endgroup\$ Dec 8, 2012 at 11:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .