U3D
Open-source, cross-platform 2D and 3D game engine built in C++
Loading...
Searching...
No Matches
Events

The Urho3D event system allows for data transport and function invocation without the sender and receiver having to explicitly know of each other. Both the event sender and receiver must derive from Object. An event receiver must subscribe to each event type it wishes to receive: one can either subscribe to the event coming from any sender, or from a specific sender. The latter is useful for example when handling events from the user interface elements.

Events themselves do not need to be registered. They are identified by 32-bit hashes of their names. Event parameters (the data payload) are optional and are contained inside a VariantMap, identified by 32-bit parameter name hashes. For the inbuilt Urho3D events, event type (E_UPDATE, E_KEYDOWN, E_MOUSEMOVE etc.) and parameter hashes (P_TIMESTEP, P_DX, P_DY etc.) are defined as namespaced constants inside include files such as CoreEvents.h or InputEvents.h, using the helper macros URHO3D_EVENT & URHO3D_PARAM.

When subscribing to an event, a handler function must be specified. In C++ these must have the signature void HandleEvent(StringHash eventType, VariantMap& eventData). The URHO3D_HANDLER(className, function) macro helps in defining the required class-specific function pointers. For example:

SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(MyClass, MyEventHandler));

In script events are identified by their string names instead of name hashes (though these are internally converted to hashes.) Script event handlers can either have the same signature as in C++, or a simplified signature void HandleEvent() when event type and parameters are not required. The same event subscription would look like:

SubscribeToEvent("Update", "MyEventHandler");

In C++ events must always be handled by a member function. In script procedural event handling is also possible; in this case the ScriptFile where the event handler function is located becomes the event receiver. See Scripting for more details.

Events can also be unsubscribed from. See UnsubscribeFromEvent() for details.

To send an event, fill the event parameters (if necessary) and call SendEvent(). For example, this (in C++) is how the Engine subsystem sends the Update event on each frame. For performance reason, in C++ the same map objects are being reused in each frame by calling GetEventDataMap() instead of creating a new VariantMap object each time. Note the parameter name hashes being inside a namespace which matches the event name:

using namespace Update;
VariantMap& eventData = GetEventDataMap();
eventData[P_TIMESTEP] = timeStep_;
SendEvent(E_UPDATE, eventData);

In script event parameters, like event types, are referred to with strings, so the same code would look like:

VariantMap eventData;
eventData["TimeStep"] = timeStep;
SendEvent("Update", eventData);

Sending events through another object

Because the SendEvent() function is public, an event can be "masqueraded" as originating from any object, even when not actually sent by that object's member function code. This can be used to simplify communication, particularly between components in the scene. For example, the physics simulation signals collision events by using the participating scene nodes as senders. This means that any component can easily subscribe to its own node's collisions without having to know of the actual physics components involved. The same principle can also be used in any game-specific messaging, for example making a "damage received" event originate from the scene node, though it itself has no concept of damage or health.

C++11 event binding and sending

Events can be bound to lambda functions including capturing context:

SubscribeToEvent(E_UPDATE, [&](StringHash type, VariantMap& args) {
});

std::bind() class methods:

void MyObject::OnUpdate(StringHash type, VariantMap& args)
{
}
SubscribeToEvent(E_UPDATE, std::bind(&MyObject::OnUpdate, this, std::placeholders::_1, std::placeholders::_2)));

std::bind() discarding unneeded parameters:

void Class::OnUpdate(VariantMap& args)
{
}
using namespace std::placeholders;
SubscribeToEvent(E_UPDATE, std::bind(&Class::OnUpdate, this, _2)));

There is a convenient method to send event using C++ variadic template which reduces the amount of boilerplate code required. Using the same example above, in C++11 standard the code can be rewritten as:

using namespace Update;
SendEvent(E_UPDATE, P_TIMESTEP, timeStep_);

There is only one parameter pair in the above example, however, this overload method accepts any number of parameter pairs.