Game Coding Complete Message Handler Upgrade

Published July 21, 2005
Advertisement
For those that read my last entry, here is the upgrade to the message handler I was talking about.

It *should* be compatible with the code supplied with the book, I'm not 100% sure because I made a few mods to my working copy ;)

First we need the functors. These allow you to make the callbacks to your class member functions. The functor has the same prototype as the IEventListener
bool function_name( Event const &a_event );


class IEventHandlerFunctor{public:    explicit IEventHandlerFunctor() { }    virtual bool execute( Event const &a_event ) = 0;    virtual EventListenerPtr getListener() = 0;};typedef boost::shared_ptr IEventHandlerFunctorPtr;template <class T>class EventHandlerFunctor : public IEventHandlerFunctor{public:    typedef bool (T::*handlerProc)( Event const &a_event );    explicit EventHandlerFunctor( T *a_inst, handlerProc a_proc ) : m_inst(a_inst), m_proc(a_proc) { }        bool execute( Event const &a_event )    {        return (*m_inst.*m_proc)( a_event );    }    EventListenerPtr getListener()    {        return reinterpret_cast( m_inst );    }private:    T *m_inst;    handlerProc m_proc;};


You will notice that I typedef IEventHandlerFunctorPtr as a shared_ptr, which should allow us to throw the functors around and have them cleaned up after us.

From here, we have an implementation of the IEventListener interface, called EventListener. This class is what we'll be deriving all of our classes which are supposed to be handling messages.

class EventListener : public IEventListener{public:    virtual ~EventListener();    // Interface for IEventListener    bool handleEvent( Event const & a_event );        IEventHandlerFunctorPtr registerEventHandler( EventType const &a_type, IEventHandlerFunctorPtr a_handler );    bool removeEventHandler( EventType const &a_type );    bool isEventRegistered( EventType const &a_type );private:   	typedef std::map< EventType, IEventHandlerFunctorPtr > EventMap;	typedef EventMap::iterator    EventMapIterator;	typedef std::pair< EventType, IEventHandlerFunctorPtr > EventMapPair; 		EventMap  m_eventHandlers;};


You'll notice that it implements the IEventListener interface as it should, but it is no longer virtual, meaning the messages stop here ;) Whenever a class needs to handle a message it must now register the message type and the functor with itself. This, in turn, will register the class with the global EventManager and will forward the messages on to the functor specified at registration. The rest of the implementation follows:

EventListener::~EventListener(){    EventMapIterator it = m_eventHandlers.begin();    while ( it != m_eventHandlers.end() )    {        IEventHandlerFunctorPtr h( it->second );        removeGlobalEventListener( h->getListener(), it->first );        ++it;    }}bool EventListener::handleEvent( Event const & a_event ){    // Find the handler based on type    IEventHandlerFunctorPtr h;    EventMapIterator it = m_eventHandlers.find( a_event.getType() );    if ( it != m_eventHandlers.end() )    {        h = it->second;        return h->execute( a_event );    }    return false;}IEventHandlerFunctorPtr EventListener::registerEventHandler( EventType const &a_type, IEventHandlerFunctorPtr a_handler ){    IEventHandlerFunctorPtr h;    EventMapIterator it = m_eventHandlers.find( a_type );    if ( it != m_eventHandlers.end() )    {        h = it->second;    }    m_eventHandlers.insert( EventMapPair(a_type, a_handler) );    safeAddListener( a_handler->getListener(), a_type );    return h;}bool EventListener::isEventRegistered( EventType const &a_type ){    EventMapIterator it = m_eventHandlers.find( a_type );    if ( it != m_eventHandlers.end() )    {        return true;    }    return false;}bool EventListener::removeEventHandler( EventType const &a_type ){    EventMapIterator it = m_eventHandlers.find( a_type );    if ( it != m_eventHandlers.end() )    {        IEventHandlerFunctorPtr h = it->second;        safeDelListener( h->getListener(), a_type );        m_eventHandlers.erase( a_type );        return true;    }    return false;}




USAGE

Usage is pretty simple and can be used with the existing Game Coding Complete system. You create your own events with their datapackets first. I created a simple event BeeSeven_Event which I want my class to handle. The class itself is very simple:-

class TestListener : public EventListener{public:    TestListener()    {        registerEventHandler( BeeSeven_Event::gkName,                IEventHandlerFunctorPtr( new EventHandlerFunctor ( this, &TestListener::handle_b7 ) )            );    }	virtual ~TestListener() { }    bool handle_b7( Event const & a_event )    {        std::cout << "bee seven";        return true;    }};


You will notice that we *must* register the handler functions for each class instance. For this I do it in the ctor, but you may want an Init() method or something.

And that's it... Your TestListener class will register itself to listen to 'b7' events (plus any others you want) and will unregister the handlers when it destructs.

TestListener t;BeeSeven_Event b7;safeTrigger( b7 );


And there you have it. Any improvements are welcome.
Previous Entry Game Coding Complete
Next Entry Code
0 likes 4 comments

Comments

Washu
You should type it as code and leave it as code, IE: don't manually add < and >
July 21, 2005 09:06 PM
Rebooted
boost::signals already provides a good signal-slot system you could use here instead of your own implementation.
July 22, 2005 05:33 AM
evolutional
I guess I *could* hook this up to boost::signals (or sigslot), but the idea of this was to augment the existing code that came with the Game Coding Complete book and point out that the author presented a useful solution, but fell slightly short of the mark in terms of how he handled the game Events.
July 22, 2005 08:11 AM
Rob Loach


I LOVE IT!!!
July 22, 2005 12:13 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement