NOTE: The implementation uses some dependencies. It is better if you’re already familiar with these dependencies used before continuing to avoid confusion.
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!
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.