Delegates
Delegates¶
Related Docs
Delegates are a unique feature of Unreal Engine that allows you to bind functions to events. You can think of them as a type-safe function pointer. A delegate can be bound to a member function of an arbitrary object, calling the function on the object at a future time. Currently, delegate signatures support any combination of the following:
- Functions returning a value.
- Functions declared as
const
. - Up to eight function parameters.
- Up to four payload variables, i.e., parameters of the bound function that are set when the delegate is bound.
Delegates in Unreal Engine can be categorized in two ways:
- Delegates are either Single or Multicast, depending on whether they invoke a single function or broadcast to multiple.
- Delegates are either Static or Dynamic, depending on whether they are bound at compile time or at runtime.
In order to use a delegate, you must first declare it in the header file, using DECLARE_*_DELEGATE_*
macros:
// Single-cast static delegate with no parameters.
DECLARE_DELEGATE(DelegateName)
// Single-cast static delegate with one parameter.
DECLARE_DELEGATE_ONEPARAM (DelegateName, Param1Type, Param1)
// Single-cast static delegate with up to eight parameters.
DECLARE_DELEGATE_<NUM>PARAMS(DelegateName, Param1Type, Param1, ParamType2, Param2, ...)
// Single-cast static delegate with a return value. Multicast delegates cannot have return values.
DECLARE_DELEGATE_RetVal (RetValType, DelegateName)
// Multicast static delegate.
DECLARE_MULTICAST_DELEGATE...()
// Single-cast dynamic delegate.
DECLARE_DYNAMIC_DELEGATE...()
// Multicast dynamic delegate.
DECLARE_DYNAMIC_MULTICAST_DELEGATE ...()
For example, in our header file, we declare a single-cast static delegate with one parameter (before the class declaration):
DECLARE_DELEGATE_OneParam(FStringDelegate, FString); // Parameter names are optional.
We then need to instantiate the delegate in the class definition:
class FMyClass {
FStringDelegate WriteToLogDelegate;
};
Note that only dynamic delegates can be exposed to Blueprints via UPROPERTY()
, in which case they need to have the BlueprintCallable
specifier.
Next, we need to bind a function (or functions) to the delegate instance. If you are working with static delegates, in the case of single-cast ones, there are several Bind*
methods available, the most common of which are as follows:
// BindUObject requires that the target be a UObject
MySingleStaticDelegate.BindUObject(Instance, &ClassName::FunctionName, Payload1, Payload2, ...);
// BindRaw is for if the target is not a UObject
MySingleStaticDelegate.BindRaw(Instance, &ClassName::FunctionName, Payload1, Payload2, ...);
// BindLambda is useful for simpler anonymous functions
MySingleStaticDelegate.BindLambda([](int32 NewScore) {
// Do something with score
});
You can use this
and ThisClass
as Instance
and ClassName
, respectively.
As for static multicast delegates, you can use the Add*
methods to add functions to the delegate. You can also use Remove
and RemoveAll
to remove functions from the delegate.
// AddUObject requires that the target be a UObject
MyMulticastStaticDelegate.AddUObject(this, &ThisClass::FunctionName, Payload1, Payload2, ...);
// AddRaw is for if the target is not a UObject
MyMulticastStaticDelegate.AddRaw(Instance, &ClassName::FunctionName, Payload1, Payload2, ...);
// AddLambda is useful for simpler anonymous functions
MyMulticastStaticDelegate.AddLambda([](int32 NewScore) {
// Do something with score
});
Finally, if you are working with dynamic delegates:
// For dynamic single-cast delegates:
MyDynamicSingleDelegate.BindDynamic(this, &ThisClass::FunctionName);
// For dynamic multicast delegates:
MyDynamicMulticastDelegate.AddUniqueDynamic(this, &ThisClass::FunctionName);
// AddUniqueDynamic ensures that the function is not added (and therefore invoked) multiple times.
Note that dynamic delegates do not support payloads.
Finally, throughout the lifetime of the delegate you can invoke its subscriber(s) by calling Execute
:
// It is good practice to check if the delegate is bound, if it is dynamic.
if (WriteToLogDelegate.IsBound()) {
WriteToLogDelegate.Execute(TEXT("Hello, World!"));
}
// Alternatively, you can combine the two steps:
WriteToLogDelegate.ExecuteIfBound(TEXT("Hello, World!"));