Polycode provides a simple event system that can be used to send and receive notifications between classes. It is used internally in Polycode for all asynchronous callbacks, such as input events and physics scene collision callbacks. The event system is extended into the Lua bindings, so you can listen to events generated in C++ from Lua.

Contents

Events overview.

The Polycode event system is based around EventDispatcher and EventListener classes. If you've ever done any ActionScript programming, this system will be familiar to you. To send events, a class must subclass the EventDispatcher class. To listen to that class's events, other classes must subclass the EventHandler class and add themselves as event listeners to the event dispatcher for a particular event type and implement the handleEvent method. When an event is dispatched from the event dispatcher, the handleEvent method will be called on all of the event handlers that are registered as listeners for that event type, passing the dispatched event to them.

Many common superclasses in Polycode, such as Entity, already subclass the EventDispatcher class.

Sending events (C++).

To be able to send Polycode events, the class sending the events must subclass the EventDispatcher class. To send an event, you call the dispatchEvent method, passing it an event type and a pointer to an Event class, which will be passed to all the listeners that are listening for that event type.

dispatchEvent(new Event(), Event::COMPLETE_EVENT);

The Event class will be automatically deleted by the EventDispatcher after being sent to all of the listeners, so the listeners must not depend on it being around after the event callback and the EventDispatcher sublcass must not try to delete the event manually!

If your event is just a notification and doesn't need to send any data, you can just send an instance of the stock Event class. The stock Event class provides a few predefined commonly used event types, but you can of course use any integer you want. If you do need to send data with the event, you can subclass the Event class and add whatever data you need to it, but the event handler will have to cast the Event class to the appropriate type. You must set a string parameter in your Event subclass called eventType, so that event handlers that handle multiple types of events know how to cast the Event subclass pointer. Most of the time you don't have to do the string comparison check as single event dispatchers generally dispatch only one kind of event.

Receiving events (C++).

To receive events in C++, a class must subclass the EventHandler class and impement the handleEvent method. An EventHandler subclass can then add itself as a listener for specific events to an event dispatcher class using addEventListener. When the dispatcher dispatches an event that the handler is listening to, its handleEvent method will be called and the event will be passed to it by the event dispatcher.

Here is an example of listening to trigger events from the Polycode timer class. First we subclass EventHandler in our class and create a new Timer that fires every 500ms somewhere in the class and add ourselves as a listener.

timer = new Timer(true, 500.0); 
timer->addEventListener(this, Timer::EVENT_TRIGGER);

Then in our handleEvent method, we check if the dispatcher of the event is our timer. Since we only registered for one event type (and the Timer only broadcasts one type of event), we don't even need to check for the event type.

void ExampleApp::handleEvent(Event *event) {
    if(event->getDispatcher() == timer) {
        cout << "TRIGGER!\n";
    }
}

If we are listening to multiple events from multiple dispatchers, we need to check the dispatcher and the event type. If we know that a certain event dispatcher is sending a certain type of event, we can cast the Event pointer to this event type to get access to the extra event data. Here's an example of handleEvent in a class that is listening for multiple input events from Polycode.

void PolycodeTemplateApp::handleEvent(Event *event) {
    if(event->getDispatcher() == core->getInput()) {
        InputEvent *inputEvent = (InputEvent*) event;
        switch(event->getEventCode()) {
            case InputEvent::EVENT_KEYDOWN:
                cout << "PRESSED KEY:" << inputEvent->keyCode() << endl;
            break;
            case InputEvent::EVENT_MOUSEDOWN:
                cout << "CLICKED BUTTON:" << inputEvent->mouseButton << endl;
            break;
        }
    }
}

Events in Lua.

The event system in Lua is designed to be compatible with the events in C++ and it functions almost exactly the same, except that it allows us to add specific functions as event handlers. Here is an example of listening to trigger events from a Polycode Timer class from Lua.

function onTimer(t, event)
	print("TRIGGER!")
end

timer = Timer(true, 500)
timer:addEventListener(nil, onTimer, Timer.EVENT_TRIGGER)

The syntax is almost exactly the same as in C++, except we are passing it a specific function as a callback. The first parameter to addEventListener can be nil or it can be used to pass other data as the first argument to the callback function. Because of the way that classes work in Lua, this system allows us to pass a class instance function as a callback function by passing the class instance as the first argument (class functions in Lua pass the class instance invisibly as the first argument).

class "TimerApp"

function TimerApp:TimerApp()
	self.timer = Timer(true, 500)
	self.timer:addEventListener(self, self.onTimer, Timer.EVENT_TRIGGER)
end

function TimerApp:onTimer(event)
	print("TRIGGER!")
end

timerApp = TimerApp()

Dispatching events in Lua works the same way as it does in C++. We subclass an EventDispatcher class and use dispatchEvent to send our events. In the example below, we modify our Timer app to dispatch its own event when it handles the Timer's trigger event.


class "TimerApp" (EventDispatcher)

function TimerApp:TimerApp()
	self.timer = Timer(true, 500)
	self.timer:addEventListener(self, self.onTimer, Timer.EVENT_TRIGGER)
end

function TimerApp:onTimer(event)
	self:dispatchEvent(Event(), Event.CHANGE_EVENT)
end

timerApp = TimerApp()

function onTimerAppEvent(t, event)
	print("TRIGGER!")
end

timerApp:addEventListener(nil, onTimerAppEvent, Event.CHANGE_EVENT)