Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
111 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Simple Event Handling


Revising the IEventHandler interface

As it stands, the interface is still fairly simple. We now need to add certain functionality to handle creating the linked list, as well as a couple extra useful methods. Here is the final full code for the IEventHandler interface:

class IEventHandler {

public:
  virtual void EventHandler(const Event &e) = 0;

  // Mutator and selector
  IEventHandler * GetNextHandler(void) {return _nextHandler;}
  void SetNextHandler(IEventHandler *next) {_nextHandler = next;}

  IEventHandler() : _nextHandler(0) {
    EventDispatcher::Get()->RegisterHandler(this);
  }

protected:
  void SendEvent(int eventType, int arg1 = 0, int arg2 = 0) {
    EventDispatcher::Get()->SendEvent(eventType, arg1, arg2);
  }

private:
  IEventHandler *_nextHandler;
};

I've added a pointer to the next handler (_nextHandler), as well as mutator and selector methods for this variable. As noted above, these two methods are used by the EventDispatcher to update, and cycle through the list of devices. Using a linked list allows for the list to vary in size without worrying about dynamically allocating memory, or knowing beforehand how many devices will listen for events. (Early versions of this system used these less elegant methods).

I've also added a constructor for this interface. A class which implements the IEventHandler interface will automatically call IEventHandler's constructor when its own constructor is invoked. In this manner, one does not have to remember to register an object as a listener as IEventHandler's constructor does so automatically. A class listening to events doesn't need to know how the event dispatching is implemented, nor does it have to rely on a third party to register it with the dispatcher.

The last method (SendEvent) is used to further abstract the EventDispatcher. Any class implementing this interface doesn't need to know anything about the dispatcher. It just calls its own SendEvent method when it needs to generate an event.

Putting It All Together

Now, I'll illustrate the usage of this system with a relatively short example. Assuming we have two classes A and B defined as follows:

#include "IEventHandler.h"

class A : public IEventHandler {

public:
  void EventHandler(const Event &e) {
    switch (e.Type) {
      case E_NEWGAMEEASY:
        cout << "Class A handling E_NEWGAMEEASY event" << endl;
        SendEvent(E_INCREMENTSCORE);
        break;
      case E_PAUSEGAME:
        cout << "Class A handling E_PAUSEGAME event" << endl;
        break;
    }
  }
};

class B : public IEventHandler {

public:
  void EventHandler(const Event &e) {
    switch (e.Type) {
      case E_INCREMENTSCORE:
        cout << "Class B handling E_INCREMENTSCORE event" << endl;
        break;
      case E_PAUSEGAME:
        cout << "Class B handling E_PAUSEGAME event" << endl;
        break;
    }
  }
};

We compile and execute the following piece of code:

#include 
using namespace std;

#include "EventDispatcher.h"

void main () {
  A a1;
  B b1;

  cout << "Main fct sending E_NEWGAMEEASY event" << endl;
  EventDispatcher::Get()->SendEvent(E_NEWGAMEEASY);
  cout << "Main fct sending E_PAUSEGAME event" << endl;
  EventDispatcher::Get()->SendEvent(E_PAUSEGAME);

  char c;
  cout << "Press any key followed by [Enter] to exit" << endl;
  cin >> c;

  exit(0);
}

The output of the program will be:

Main fct sending E_NEWGAMEEASY event
Class A handling E_NEWGAMEEASY event
Class A sending a E_INCREMENTSCORE event
Class B handling E_INCREMENTSCORE event
Main fct sending E_PAUSEGAME event
Class B handling E_PAUSEGAME event
Class A handling E_PAUSEGAME event

Conclusion

Now we have a simple Event Handling/Dispatching system. There are certain performance issues to consider when using this system. Spending too much time in the EventHandler methods basically slows down the overall event dispatching. One solution is to spawn off a thread for each event handler, but as the number of events and event handling objects increase, the overhead for creating and deleting threads will become significant. The best solution is to use the event handler to set flags or store important variables (such as mouse coordinates), and make use of that during the update/render loop.

The use for this type of system is fairly obvious. Aside from handling mouse and keyboard events (and abstracting those events), general events can be created and shared across the program. Sound events can be generated for specific situations (such as a collision, or a menu selection). Different components of a game might need to be informed when a new game is created or when the game play is paused. The biggest advantage of this system, in my opinion, is that it allows a higher level of abstraction between modules.

I've included all c++ header and source code I've mentioned in this article, and you're free to use it in any program you want. Any questions or comments, please email me.





Contents
  Introduction
  Revising the IEventHandler interface

  Source code
  Printable version
  Discuss this article