A Simple Event Manager

NOTE: The implementation uses some dependencies. It is better if you’re already familiar with these dependencies used before continuing to avoid confusion.

SmartPointer – http://codesushi.spaces.live.com/Blog/cns!4483403BEB19180F!291.entry
HashedString – http://codesushi.spaces.live.com/Blog/cns!4483403BEB19180F!290.entry

Building an engine, system or an application in general that consists of many subsystems requires some form of communication between each other. This flow of communication can get quite complicated. A naive implementation would simply call the API functions directly. This method can get really messy. Not only this is very inflexible but it also introduces dependencies.

One way of fixing this problem is using an event manager. This event manager uses two design patterns called the listener and the dispatcher. Systems that will be receiving event messages are listeners and the system that will be broadcasting the events to the listeners is called a dispatcher. So basically, the event manager keeps a database of all the listeners registered to it. So whenever an event has been triggered, the event manager automatically informs all the registered listeners of the event and handle the event appropriately. With this method, dependencies between subsystems are eliminated.

A brief explanation of the implementation then source code follows below.

If you have the "Game Coding Complete by Mr. Mike" book then you may notice that my implementation is a little bit similar because this implementation is basically a simplified version of it.

The source consists of three classes:
* Event – Event data used to send to the listeners.
* Listener – Registered systems/objects in the EventManager "listening" for one or more events.
* EventManager – Manager that handles events and listeners.

The EventManager interface is pretty straight forward and easy to understand:
* AddListener( const EventType &eventType, const EventListenerPtr &listener ) – This registers a listener to the event manager specifying what event type the listener wants to listen.
* RemoveListener( const EventType &eventType, const EventListenerPtr &listener ) – Removes the listener
* TriggerEvent( const Event &event ) – Immediately sends the event to the registered listeners.
* PostEvent( const EventPtr &event ) – Queues the event to the eventQueue to be processed later on. See next method below.
* ProcessEvents( FLOAT maxTime = (FLOAT)INFINITY ) – This method processes the queued events and dispatches the events to the registered listeners. You can optionally specify a maximum time limit to limit the time it takes to process the events. The remaining unprocessed events will simply get carried over to the event queue to be processed later on when ProcessEvents is called again.

There’s definitely a lot of functionalities missing. Adding a version of Add/Remove listener that registers to listen to all incoming events and serialization would be nice…

Here’s the source code. Enjoy!
http://codaset.com/codesushi/pulse-tec/source/master/blob/Source/Engine/Include/EventManager.h
http://codaset.com/codesushi/pulse-tec/source/master/blob/Source/Engine/Source/EventManager.cpp

There could be some bugs in the code. I haven’t tested it yet(tired and sleepy). If you find one, just let me know!

Here is a pseudocode sample implementation of the EventManager. This simulates a graphics device(renderer) that checks if the device is ok or not before rendering a scene. If the device has been lost it then checks if it can be reset. If it can be reset, it triggers an event named EventDeviceLost first then it resets the device then finally triggering another event named EventDeviceReset making sure the resources handles the appropriate actions then resets itself to make the resources working again.

  1. // This acts as a dispather
  2. class DumbGraphicsDevice
  3. {
  4.     // See implementation below
  5.     BOOL IsDeviceLost( void );
  6.  
  7.     void BeginRender( void ) { }
  8.     void EndRender( void ) { }
  9.     void Present( void ) { }
  10.  
  11.     // See bottom of code for implementation.
  12.     void AddListener( class DumbGraphicsResourceTexture *pListener );
  13.  
  14.  
  15. private:
  16.  
  17.     EventManager m_eventRegistry;
  18.  
  19. };
  20.  
  21.  
  22. // Example of a concrete event
  23. // Triggers just before resetting the graphics device
  24. class EventDeviceLost : public Event
  25. {
  26. public:
  27.  
  28.     virtual const EventType & GetEventType( void ) const { return m_eventType; }
  29.  
  30.     // We can move this to DumbGraphicsDevice instead
  31.     static const EventType m_eventType;
  32.  
  33. };
  34.  
  35. // Another example of a concrete event
  36. // Triggers after the device has been reset
  37. class EventDeviceReset : public Event
  38. {
  39. public:
  40.  
  41.     virtual const EventType & GetEventType( void ) const { return m_eventType; }
  42.  
  43.     // We can move this to DumbGraphicsDevice instead
  44.     static const EventType m_eventType;
  45. };
  46.  
  47. EventType EventDeviceLost::m_eventType("DeviceLost");
  48. EventType EventDeviceReset::m_eventType("DeviceReset");
  49.  
  50. // An example of a listener
  51. class DumbGraphicsResourceTexture : public EventListener
  52. {
  53. public:
  54.  
  55.     DumbGraphicsResourceTextures( void ) { /* Handle texture resource initialization */ }
  56.     virtual ~DumbGraphicsResourceTextures( void ) { /* handle freeing of resources. */ }
  57.  
  58.  
  59.     // For debug purposes
  60.     virtual const CHAR * GetName( void ) { return "DumbGraphicsResourceTexture"; }
  61.  
  62.     // See implementation below
  63.     virtual void HandleEvent( const Event &event );
  64. };
  65.  
  66. int main( void )
  67. {
  68.     // INitialize graphics device here
  69.     DumbGraphicsDevice gd;
  70.     DumbGraphicsResourceTexture sampleTexture1;
  71.     DumbGraphicsResourceTexture sampleTexture2;
  72.  
  73.     // Make sure we register these two texture resources to listen for events!
  74.     gd.AddListener( &sampleTexture1 );
  75.     gd.AddListener( &sampleTexture1 );
  76.  
  77.  
  78.     // simulate running program (update and rendering)
  79.     while ( true )
  80.     {
  81.         
  82.         // Update();
  83.  
  84.         // Render if device is ok
  85.         if ( !gd.IsDeviceLost() )
  86.         {
  87.             gd.BeginRender();
  88.             // Render();
  89.             gd.EndRender();
  90.             gd.Present();
  91.         }
  92.  
  93.     }
  94. }
  95.  
  96. BOOL DumbGraphicsDevice::IsDeviceLost( void )
  97. {
  98.     if ( /* is device ok? */ )
  99.         return FALSE;
  100.  
  101.     // Device is lost. Attempt to recover it.
  102.     if ( /* Device can now be reset */ )
  103.     {
  104.         // Fire on device lost event
  105.         m_eventRegistry.TriggerEvent( EventDeviceLost::m_eventType );
  106.         // ResetDevice()
  107.         // Fire on device reset event
  108.         m_eventRegistry.TriggerEvent( EventDeviceReset::m_eventType );
  109.         return FALSE;
  110.     }
  111.     else
  112.         return TRUE;
  113. }
  114.  
  115. void DumbGraphicsDevice::AddListener( class DumbGraphicsResourceTexture *pListener )
  116. {
  117.     // Register pListener to both DeviceLost and DeviceReset event
  118.     m_eventRegistry.AddListener( EventDeviceLost::m_eventType, pListener );
  119.     m_eventRegistry.AddListener( EventDeviceReset::m_eventType, pListener );
  120. }
  121.  
  122. void DumbGraphicsResourceTexture::HandleEvent( const Event &event )
  123. {
  124.     // Is this a device lost event?
  125.     if ( EventDeviceLost::m_eventType == event.GetEventType() )
  126.     {
  127.         // Handle resource on lost device
  128.     }
  129.     else if ( EventDeviceReset::m_eventType == event.GetEventType() )
  130.     {
  131.         // Hanle resource on device reset
  132.     }
  133. }

Thanks!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s