• Advertisement
Sign in to follow this  

Building an event system

This topic is 4476 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Does anyone have some thoughts to share when designing a relatively lightweight event system for use in a game? I could go into detail with how my current system works and why I'm becoming unhappy with it, but I think a snippet will illustrate both reasonably well.
class Input :
  public EventDispatcher<RotateEvent>,
  public EventDispatcher<FireEvent>,
  public EventDispatcher<ChangeWeaponEvent>,
  public EventDispatcher<AccelerateEvent>,
  // snip 20+ EventDispatcher<...>
{
  void Poll() {
    // giant switch statement!
    // publish events to listeners
  }  
};

class Player :
  public EventListener<RotateEvent>,
  public EventListener<FireEvent>,
  public EventListener<ChangeWeaponEvent>,
  public EventListener<AccelerateEvent>,
  // snip 10+ EventListern<...>
{
  virtual void OnEvent(const FireEvent& e) {
    // bang bang
  }

  // snip 10+ OnEvent(...)
};


I was actually willing to deal with the inheritance, but I really need some way to register/unregister events at runtime which is where it falls apart, at least I can't seem to do it without RTTI. I would just fall back on passing around event "id's" but Im pretty reliant on the ability to send along a few arguments with the various events. So before I dump any more time into this thing, was wondering if you guys could give me some tips on changing what I have or completely different systems that have worked well for you and can match the two most important things to me here (an interface along the lines of OnEvent(EventType) for all events in handlers, and the ability to pass arguments of any type/quantity with events) in addition to the potential to plugin/remove/change events at runtime.

Share this post


Link to post
Share on other sites
Advertisement
You could go the baseclass/virtual function route.
Build a Event class, have you event types inherit from it.
When you "fire" an event, you know what type of event you are firing, so you can have a class specific "setparamers" member for that class.
Once the event is set up, you pass it to the event handler as a pointer to the baseclass type, and call some "exec_event" sort of member.

ie.

class Event
{
public:
virtual void Exec()=0;
};

class FireEvent : public Event
{
protected:
Sound_Object *weapon_sound;
World_Object *target;
float damage.
public:
FireEvent ( Sound_Object, World_Object, float ) { ... }
virtual void Exec() { PlaySound(weapon_sound); DoDamage(target, damage); }
};

class EventListener
{
private:
vector<Event *> eventlist;
public:
AddEvent ( Event *e ) { eventlist.push_back(e); }
CheckEvents ( ) { for ( ... ) {eventlist->Exec();} eventlist.clear() }
};

//somewhere you need to use an event....
Event *fe = new FireEvent ( ak47_sound, terrorist, 100000.0f );
Player->eventlistener.AddEvent ( fe );

//and later when the player starts updating, it can poll for events...
Player::CheckEvents() { eventlistener.CleckEvents(); }



as far as event dispatch, you are still probably going to end up with a giant switch statement of some sort. You could make a std::map to keep a valid collection of event allocators, but there is still the deal with whatever parameters you need to pass in. Sorry I can't help too much there. Only thing I
can think of is using something of the form:
SetParam ( stringstream );
and that way you can pipe information into the stringstream, and the setparams function can check that the right amount / type of data is there. That way you have a unified parameter setting function you can add to the base class.
That way you don't have to worry about differing ways to setup each event type.

Also note, there may be a cleaner system for this, there was another thread recently about some way of organising this base_class->inherited_class in such
a way as to remove the virtual function part and replace it with templated code.
Don't know if that would work better in this case or not.

Share this post


Link to post
Share on other sites
I'll try to explain the event system I'm working with and am quite happy about.
I have an Event base class, of which all types of events inherit. The parameters of an event, become parameters of the subclasses' constructor.

All subclasses contain a static instance of EventType. The constructor of this class receives a string that is hashes and used later on. This prevents having a large enumeration of event types, which triggers a recopmlie of your whole game every time you add an event. (And it's useful during debugging).

On initialization, the event listeners register with the EventManager by giving it a function pointer and an event type. The EventManager will call this function every time the event was thrown, reducing the need to inherit from a EventListener class.

The code that generates events, enqueues this with a call that looks roughly like this:
EventManager::instance().queueEvent( new FireEvent("terrorist", 255.0f) );


The EventManager keeps track of the listeners by using a std::map< unsigned int, EventListenerList >. When handling events, it does
event.getType().getHash()

and uses that hash to retrieve the registered EventListeners (i.e. function pointer wrapped in a class) from the map. It then calls
listener.->handleEvent(event)
for every one of them.

I hope the above makes sense to you. If it doesn't I could try to weed out some project-specific things out of my code and post some if you're interested.

Share this post


Link to post
Share on other sites
Aaah.. boost.signals... in general i 'd second your suggestion, snk_kid (loving boost myself). but recent discussions in the boost dev list have revealed quite a performance problem with boost.signals (compared to more traditional approaches). for a performant message system, i'd refrain from using it and build the message passing system myself. if you plan on making everything multi threaded then you would have to make some changes/additions to boost.signals anyway... so rolling your own seems not so much of additional cost.

cheers,
simon

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement