Okay, I wanted to provide some more information on this since I went on a deep dive into the internals of Unreal Engine on trying to find the actual implementations of all of this.
So there’s not any documentation that defines the function signature that the pre-defined delegates accept. That’s mostly because the function signatures for these are only created at compile time through macros. You can still learn a bit more about how they’re created to more easily figure it out, but it requires reading through the Unreal Engine code.
Fair warning, this is gonna be a bit long. There’s no easy way to really describe how to find the underlying code for delegates.
The delegates in Unreal Engine have a few macros that allow you to create your own delegates if you want (it’s also these macros that makes this more confusing than it needs to be). Some actual documentation on creating your own delagates can be found here Delegates and Lamba Functions in Unreal Engine | Unreal Engine Documentation.
Unreal Engine provides a few of these delegates created from those macros for you on the AActor class, OnTakeAnyDamage, OnActorBeginOverlap, etc… I’m just gonna try to explain OnTakeAnyDamage, but for the other pre-defined delegates you can follow the same process that I’ll try to explain here in order to find the function signature needed.
As DanM mentioned you can go to the definition of OnTakeAnyDamage
and then go the definition of the type for FTakeAnyDamageSignature
. That brings you to the macro DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams
. Once you’re here you can figure out the function signature you need.
Long story short, ignore the first 3 arguments and look at these to get the function signature:
AActor*, DamagedActor, float, Damage, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser
And what about the function return type? According to the docs this defaults to void
. Unless a macro is used that specifies _RetVal__
on it. For those the return type will be part of the macro argument list, for example DECLARE_DELEGATE_RetVal(RetValType, DelegateName)
, RetValType
being the return type you’ll use on your function. If it’s DECLARE_DELEGATE_RetVal(float, DelegateName)
, the return value is a float
.
Now we know the return value and the argument list that’s expected by the delegate for functions added to AddDynamic()
.
That leads us to this signature:
void Function(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
So that’s probably good enough for most people. You now can find the function signature for any of the other delegates that you run into.
Now if you want to understand more about the first 3 arguments continue reading…
So the first 3 arguments just inform the macro the name of the type to create for the delegate and what to create the delegate on. If you hover over the macro you’ll get this signature:
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams( SparseDelegateClass, OwningClass, DelegateName, Param1Type, Param1Name, Param2Type, Param2Name, Param3Type, Param3Name, Param4Type, Param4Name, Param5Type, Param5Name )
- SparseDelegateClass - this is the name of the class the macro will create, the class does not exist until compile time when the macro is run
- OwningClass - this is the name of the existing class the created delegate will be added to
- DelegateName - this is the name of the variable on the OwningClass the delegate will be “assigned” to
Okay, but what is a sparse delegate? The documentation on delegates doesn’t mention anything about _SPARSE_
delegates, so what even are those?
In the case of SparseDelegateClass
we can dive a little bit more into the FTakeAnyDamageSignature
type to learn more. If you hover over that type you’ll see this:
struct
FTakeAnyDamageSignature : public TSparseDynamicDelegate<FTakeAnyDamageSignature_MCSignature, AActor, FTakeAnyDamageSignatureInfoGetter>
From that we can tell it’s a struct that inherits from TSparseDynamicDelegate
. Click into that type and we get this:
struct
TSparseDynamicDelegate<MulticastDelegate, OwningClass, DelegateInfoClass> : public FSparseDelegate
Sparse version of TBaseDynamicDelegate
SparseDelegate.h
So now we got an actual file location, SparseDelegate.h
. Why didn’t FTakeAnyDamageSignature
have a file location? Well that’s due to it not having one until the macro generates a class/struct of that name at compile time.
So let’s find where SparseDelegate.h
is located. This will require searching on the GitHub source for Unreal Engine. If you don’t have access to the GitHub repository follow this guide, it’s free to get access, just gotta link your GitHub account.
Searching for that file leads us to here . At the top of that file there’s a comment that gives us a little more information on what a sparse delegate is:
/**
* Sparse delegates can be used for infrequently bound dynamic delegates so that the object uses only
* 1 byte of storage instead of having the full overhead of the delegate invocation list.
* The cost to invoke, add, remove, etc. from the delegate is higher than using the delegate
* directly and thus the memory savings benefit should be traded off against the frequency
* with which you would expect the delegate to be bound.
*/
So that indicates a sparse delegate is just a memory optimized delegate since it’s expected to not be binded to that frequently (or ever). Since OnTakeAnyDamage
is on the AActor class and everything tends to use it as a base class in Unreal Engine that seems like a good thing.
Now at the bottom of that file we actually find the #define
for DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams
here. Sweet, oh wait… It’s a macro that calls other macros . I’m not gonna dive into what that macro is actually doing, tbh I don’t understand it, but at least you know which file to look at to learn more if you want.
Okay so that’s a little bit more information on sparse delegates.
Now the second argument OwningClass
, well in the case of OnTakeAnyDamage
that uses the AActor
class. So this is just letting the macro know what existing class to create the delegate on.
Then the third argument DelegateName
, that’s just a class variable on the OwningClass
, in this case it’s letting the macro know that on AActor
there’s a OnTakeAnyDamage
variable that the delegate will be created on.
So that takes care of the first 3 arguments, now we get to Param1Type
, then Param1Name
, and repeated for 5 parameters (as the macro indicates _FiveParams
) which indicates the name and types of the 5 parameters that will be passed to the function you add with AddDynamic()
.
That’s about it for determining what those arguments mean on the macro.
If you want to look at the location where the non-sparse delegates are created you’ll find that here. The macros and base delegate types are all defined there and in the other files under the Delegates/
directory.
So hopefully that at least gives you more information on where to find the implementations for these delegate macros. But most importantly, you can now hopefully understand where to find the function signature of delegates like OnTakeAnyDamage
.