Skip to content

2. Creating and registering listeners

Univise edited this page Jul 23, 2017 · 3 revisions

Two steps are involved in the setup of a listener: setup of the handler functions itself and the registration to the event system. Fortunately, both steps are incredibly simple.

A handler function must be declared as UFunction and have the following signature: void Name(Type&) where Type is a direct or indirect child of FEventBase. Note that you are accepting a reference here, not a copy of the event, which is essential for modifying interactive events. Although it seems quite counter-intuitive, the system also expects a non-const reference for informing events - the reason being the limitations of Unreal's reflection system. Here is a model event handler for our FPreEntityTakeDamageEvent, created in the previous chapter:

UFUNCTION()
void OnPreEntityTakeDamageEvent(FPreEntityTakeDamageEvent& Event)
{
    Event.Damage = 42.f; // 42 is a nice number 
}

All that is left to do now is to register the event. Obtain the UEventManager instance using the static function UEventManager::GetInstance(), and call one of these functions on it: RegisterEvents(class UObject* Object, TEnumAsByteEListenerPriority::Type Priority) or RegisterSingleEvent(class UObject* Object, class UScriptStruct* EventClass, TEnumAsByteEListenerPriority::Type Priority = EListenerPriority::Normal). Let me explain the parameters to you and when to use which version of the function:

The object parameter will always be the pointer to the listener instance; it practically all cases you will pass the this pointer.

The event priority you see in both versions determines which handlers gets to have the last say with interactive events. As there are potentially dozens of listeners, each one might attempt to override the event's data. To combat this, handlers with higher priority will be executed after lower priority ones, such that their modifications are permanent. Note that the side effect of this is that higher priority handlers will see the changes made by previous handlers. The priority enumeration is Lowest, Low, Normal, High, Highest, and Monitor. Monitor is a special priority which is not allowed to modify events; they wish to monitor the result of an event and act accordingly to what happened.

Now when do you use which version of registration function? RegisterEvents will use reflection to check for all handler functions in the object's class and assign the same priority to them all while RegisterSingleEvent will search for the handler that handles the event type passed (the parameter "EventClass"). For the parameter "EventClass" you simply pass YourEventStruct::StaticStruct() (so for example FPreEntityTakeDamageEvent::StaticStruct()) which is auto-generated by the Unreal Header Tool. A tip: do not create two handlers that accept exactly the same type of event - while the system can handle it, it makes your code's logic more complex and thusly more complex to maintain.

When your object is destructed, make sure to call UnregisterEvents or UnregisterSingleEvent, to avoid dangling pointers inside the Event Subsystem.

You should typially register events in BeginPlay() and unregister in EndPlay(). Avoid registration in the constructor because Unreal creates a default object for eachg class at startup; also if the default object for UEventManager has not yet been allocated in that stage, UEventManager::GetInstance() will simply return NULL.

Clone this wiki locally