In GameplayerAbility.h
, we can see there’re two functions:
UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "ActivateAbility", meta=(ScriptName = "ActivateAbility"))
void K2_ActivateAbility();
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData);
Why not just use one BlueprintNativeEvent to replace two functions?
BlueprintNativeEvents can’t be virtual and it reads clearly from within the code that this is a BP event being called however it seems that the K2_ naming convention has been abandoned. There probably is more reasons I haven’t thought of.
That (probably… im guessing) because BlueprintNativeEvent generate definition of function on original function name and any C++ virtual override code need to have _Implementation suffix definition which may be confusing if programmer relay only on InteliSense or API reference which don’t tell you to use that instead. Thats probably why this is avoided. Note that even BeginPlay and Tick is implemented exact same way to with ReceiveBeginPlay and ReceiveTick, probably for same reason as this is most common overrided function and it would kind of ruined code aesthetics, it more natural to just name functions Tick and BeginPlay.
So it all depends on want you want to bucher your overridable function in C++ with ugly _Implementation suffix or not
The function it self can not, but generated _Implementation is virtual, in fact some AGameMode events use it
This is a good point. Thank you for answering!
After reading code in UGameAbility::ActivateAbility()
and AActor::BeginPlay()
, I think it’s because a function with “K2_” prefix is just a part of the function without “K2_”. In other words, a non-“K2_” function includes one or multiple “K2_” functions.
For example, in UGameAbility::ActivateAbility()
, K2_ActivateAbility()
or K2_ActivateAbilityFromEvent
will get called depending on conditions.
void UGameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
if (bHasBlueprintActivate)
{
// A Blueprinted ActivateAbility function must call CommitAbility somewhere in its execution chain.
K2_ActivateAbility();
}
else if (bHasBlueprintActivateFromEvent)
{
if (TriggerEventData)
{
// A Blueprinted ActivateAbility function must call CommitAbility somewhere in its execution chain.
K2_ActivateAbilityFromEvent(*TriggerEventData);
}
else
{
UE_LOG(LogAbilitySystem, Warning, TEXT("Ability %s expects event data but none is being supplied. Use Activate Ability instead of Activate Ability From Event."), *GetName());
bool bReplicateEndAbility = false;
bool bWasCancelled = true;
EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
}
else
{
// Native child classes may want to override ActivateAbility and do something like this:
// Do stuff...
if (CommitAbility(Handle, ActorInfo, ActivationInfo)) // ..then commit the ability...
{
// Then do more stuff...
}
}
}
BlueprintNativeEvent can’t get the same effect as above.