AbilityTask
官方头文件注释:
/**
* AbilityTasks are small, self contained operations that can be performed while executing an ability.
* They are latent/asynchronous is nature. They will generally follow the pattern of 'start something and wait until it is finished or interrupted'
*
* We have code in K2Node_LatentAbilityCall to make using these in blueprints streamlined. The best way to become familiar with AbilityTasks is to
* look at existing tasks like UAbilityTask_WaitOverlap (very simple) and UAbilityTask_WaitTargetData (much more complex).
*
* These are the basic requirements for using an ability task:
*
* 1) Define dynamic multicast, BlueprintAssignable delegates in your AbilityTask. These are the OUTPUTs of your task. When these delegates fire,
* execution resumes in the calling blueprints.
*
* 2) Your inputs are defined by a static factory function which will instantiate an instance of your task. The parameters of this function define
* the INPUTs into your task. All the factory function should do is instantiate your task and possibly set starting parameters. It should NOT invoke
* any of the callback delegates!
*
* 3) Implement a Activate() function (defined here in base class). This function should actually start/execute your task logic. It is safe to invoke
* callback delegates here.
*
*
* This is all you need for basic AbilityTasks.
*
*
* CheckList:
* -Override ::OnDestroy() and unregister any callbacks that the task registered. Call Super::EndTask too!
* -Implemented an Activate function which truly 'starts' the task. Do not 'start' the task in your static factory function!
*
*
* --------------------------------------
*
* We have additional support for AbilityTasks that want to spawn actors. Though this could be accomplished in an Activate() function, it would not be
* possible to pass in dynamic "ExposeOnSpawn" actor properties. This is a powerful feature of blueprints, in order to support this, you need to implement
* a different step 3:
*
* Instead of an Activate() function, you should implement a BeginSpawningActor() and FinishSpawningActor() function.
*
* BeginSpawningActor() must take in a TSubclassOf<YourActorClassToSpawn> parameters named 'Class'. It must also have a out reference parameters of type
* YourActorClassToSpawn*& named SpawnedActor. This function is allowed to decide whether it wants to spawn the actor or not (useful if wishing to
* predicate actor spawning on network authority).
*
* BeginSpawningActor() can instantiate an actor with SpawnActorDefferred. This is important, otherwise the UCS will run before spawn parameters are set.
* BeginSpawningActor() should also set the SpawnedActor parameter to the actor it spawned.
*
* [Next, the generated byte code will set the expose on spawn parameters to whatever the user has set]
*
* If you spawned something, FinishSpawningActor() will be called and pass in the same actor that was just spawned. You MUST call ExecuteConstruction + PostActorConstruction
* on this actor!
*
* This is a lot of steps but in general, AbilityTask_SpawnActor() gives a clear, minimal example.
*
*
*/
上面文字的大意:
AbilityTask是小而完备的行为,通常在Ability执行过程中运行。
异步是其固有特征。实现的功能一般按照“开始做些事,直到结束或被中断”的模式。
官方推荐了学习两个已有的AbilityTask:
- UAbilityTask_WaitOverlap (very simple)
- UAbilityTask_WaitTargetData ( much more complex)
为了使用AbilityTask,你必须满足三个基本要求:
- 在AbilityTask中定义dynamic multicast , BlueprintAssignable类型的delegate,这些委托就是task的输出。当这些委托触发时,我们会回到调用AbilityTask的蓝图中(通常是Ability)。
- 你需要定义一个static的工厂函数来实例化AbilityTask。该工厂函数的输入参数表,就是你对ability task的输入。在这个函数中,你应该创建你的task实例并设置你的成员变量初值。不应该在此调用任何委托。
- 实现
Activate()
函数。从这个函数开始你的task。你可以在这里安全地触发委托。
你还有一个check list要检查:
- Override
:: OnDestroy()
来注销你之前在task注册的callback。记得在合适的地方调用EndTask来结束任务。 - Implemented an Activate function which truly 'starts' the task. Do not 'start' the task in your static factory function!
还有一部分是说在task中spawn actor,这里就不说明了。引擎提供了AbilityTask_SpawnActor()
供参考。
关于AbilityTask其他介绍,也可以参考之前写的:
AbilityTask
RPGAbilityTask_PlayMontageAndWaitForEvent.h/cpp
头文件:
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ActionRPG.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "RPGAbilityTask_PlayMontageAndWaitForEvent.generated.h"
class URPGAbilitySystemComponent;
/** Delegate type used, EventTag and Payload may be empty if it came from the montage callbacks */
/** 我们先声明要使用的Delegate类型,还记得要求1)么? Dynamic multicast !!!
你可以声明各种不同参数表的delegate*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRPGPlayMontageAndWaitForEventDelegate, FGameplayTag, EventTag, FGameplayEventData, EventData);
/**
* This task combines PlayMontageAndWait and WaitForEvent into one task, so you can wait for multiple types of activations such as from a melee combo
* Much of this code is copied from one of those two ability tasks
* This is a good task to look at as an example when creating game-specific tasks
* It is expected that each game will have a set of game-specific tasks to do what they want
*/
UCLASS()
class ACTIONRPG_API URPGAbilityTask_PlayMontageAndWaitForEvent : public UAbilityTask
{
GENERATED_BODY()
public:
// Constructor and overrides
URPGAbilityTask_PlayMontageAndWaitForEvent(const FObjectInitializer& ObjectInitializer);
// 要求3),重写Activate()
virtual void Activate() override;
virtual void ExternalCancel() override;
virtual FString GetDebugString() const override;
//Check List , 重写OnDestroy来注销callback
virtual void OnDestroy(bool AbilityEnded) override;
// 要求1) BlueprintAssignable的delegate提供蓝图支持
// -------------------------------------------------------------------------
/** The montage completely finished playing */
UPROPERTY(BlueprintAssignable)
FRPGPlayMontageAndWaitForEventDelegate OnCompleted;
/** The montage started blending out */
UPROPERTY(BlueprintAssignable)
FRPGPlayMontageAndWaitForEventDelegate OnBlendOut;
/** The montage was interrupted */
UPROPERTY(BlueprintAssignable)
FRPGPlayMontageAndWaitForEventDelegate OnInterrupted;
/** The ability task was explicitly cancelled by another ability */
UPROPERTY(BlueprintAssignable)
FRPGPlayMontageAndWaitForEventDelegate OnCancelled;
/** One of the triggering gameplay events happened */
UPROPERTY(BlueprintAssignable)
FRPGPlayMontageAndWaitForEventDelegate EventReceived;
// -------------------------------------------------------------------------
/**
* Play a montage and wait for it end. If a gameplay event happens that matches EventTags (or EventTags is empty), the EventReceived delegate will fire with a tag and event data.
* If StopWhenAbilityEnds is true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled.
* On normal execution, OnBlendOut is called when the montage is blending out, and OnCompleted when it is completely done playing
* OnInterrupted is called if another montage overwrites this, and OnCancelled is called if the ability or task is cancelled
*
* @param TaskInstanceName Set to override the name of this task, for later querying
* @param MontageToPlay The montage to play on the character
* @param EventTags Any gameplay events matching this tag will activate the EventReceived callback. If empty, all events will trigger callback
* @param Rate Change to play the montage faster or slower
* @param bStopWhenAbilityEnds If true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled
* @param AnimRootMotionTranslationScale Change to modify size of root motion or set to 0 to block it entirely
要求1) 静态工厂函数 , 前两个参数必须是UGameplayAbility* , FName
*/
UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
static URPGAbilityTask_PlayMontageAndWaitForEvent* PlayMontageAndWaitForEvent(
UGameplayAbility* OwningAbility,
FName TaskInstanceName,
UAnimMontage* MontageToPlay,
FGameplayTagContainer EventTags,
float Rate = 1.f,
FName StartSection = NAME_None,
bool bStopWhenAbilityEnds = true,
float AnimRootMotionTranslationScale = 1.f);
private:
/** Montage that is playing */
UPROPERTY()
UAnimMontage* MontageToPlay;
/** List of tags to match against gameplay events */
UPROPERTY()
FGameplayTagContainer EventTags;
/** Playback rate */
UPROPERTY()
float Rate;
/** Section to start montage from */
UPROPERTY()
FName StartSection;
/** Modifies how root motion movement to apply */
UPROPERTY()
float AnimRootMotionTranslationScale;
/** Rather montage should be aborted if ability ends */
UPROPERTY()
bool bStopWhenAbilityEnds;
/** Checks if the ability is playing a montage and stops that montage, returns true if a montage was stopped, false if not. */
bool StopPlayingMontage();
/** Returns our ability system component */
URPGAbilitySystemComponent* GetTargetASC();
void OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted);
void OnAbilityCancelled();
void OnMontageEnded(UAnimMontage* Montage, bool bInterrupted);
void OnGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload);
// 用于绑定到montage的委托
FOnMontageBlendingOutStarted BlendingOutDelegate;
FOnMontageEnded MontageEndedDelegate;
// Delegate Handle用来清理对外部注册的delegate
FDelegateHandle CancelledHandle;
FDelegateHandle EventHandle;
};
源文件我们主要看这些函数:
- OnMontageEnded : 触发OnCompleted蓝图输出引脚
- PlayMontageAndWaitForEvent:静态工厂函数
- Activate:执行task
- OnDestroy: 清理注册到外部的delegate
void URPGAbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload)
{
// 在broad cast 委托之前,都要调用 ShouldBroadcastAbilityTaskDelegates检测Ability是否有效
if (ShouldBroadcastAbilityTaskDelegates())
{
FGameplayEventData TempData = *Payload;
TempData.EventTag = EventTag;
// Fire delegate
EventReceived.Broadcast(EventTag, TempData);
}
}
静态工厂函数:
URPGAbilityTask_PlayMontageAndWaitForEvent* URPGAbilityTask_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(UGameplayAbility* OwningAbility,
FName TaskInstanceName, UAnimMontage* MontageToPlay, FGameplayTagContainer EventTags, float Rate, FName StartSection, bool bStopWhenAbilityEnds, float AnimRootMotionTranslationScale)
{
// 在开发阶段使用,控制montage的播放速度
UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate);
//使用NewAbilityTask来创建ability task实例
URPGAbilityTask_PlayMontageAndWaitForEvent* MyObj = NewAbilityTask<URPGAbilityTask_PlayMontageAndWaitForEvent>(OwningAbility, TaskInstanceName);
//设置成员变量初始值
MyObj->MontageToPlay = MontageToPlay;
MyObj->EventTags = EventTags;
MyObj->Rate = Rate;
MyObj->StartSection = StartSection;
MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale;
MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds;
return MyObj;
}
任务本体Activate(),在这里绑定各种delegate:
void URPGAbilityTask_PlayMontageAndWaitForEvent::Activate()
{
if (Ability == nullptr)
{
return;
}
bool bPlayedMontage = false;
// GetTargetASC的功能只是Cast
URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();
if (RPGAbilitySystemComponent)
{
const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
//获得actor上的skeletal mesh comp的动画实例
UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
if (AnimInstance != nullptr)
{
// Bind to event callback
EventHandle = RPGAbilitySystemComponent->AddGameplayEventTagContainerDelegate(EventTags, FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent));
// Play montage
if (RPGAbilitySystemComponent->PlayMontage(Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection) > 0.f)
{
// Playing a montage could potentially fire off a callback into game code which could kill this ability! Early out if we are pending kill.
if (ShouldBroadcastAbilityTaskDelegates() == false)
{
return;
}
CancelledHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnAbilityCancelled);
// Set montage delegates
//----------------------------------------------------------------------------------------------------------
BlendingOutDelegate.BindUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut);
AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay);
MontageEndedDelegate.BindUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnMontageEnded);
AnimInstance->Montage_SetEndDelegate(MontageEndedDelegate, MontageToPlay);
//--------------------------------------------------------------------------------------------------------------
//是否应用动画影响角色的位置
ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
if (Character && (Character->Role == ROLE_Authority ||
(Character->Role == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
{
Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale);
}
bPlayedMontage = true;
}
}
else
{
ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent call to PlayMontage failed!"));
}
}
else
{
ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent called on invalid AbilitySystemComponent"));
}
//动画播放失败
if (!bPlayedMontage)
{
ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent called in Ability %s failed to play montage %s; Task Instance Name %s."), *Ability->GetName(), *GetNameSafe(MontageToPlay),*InstanceName.ToString());
if (ShouldBroadcastAbilityTaskDelegates())
{
OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
}
}
/** Called when the ability task is waiting on ACharacter type of state (movement state, etc). IF the remote player ends the ability prematurely, and a task with this set is still running, the ability is killed. */
// 我们需要明确等待什么东西:
// Task is waiting for the game to do something
// WaitingOnGame = 0x01,
// Waiting for the user to do something
// WaitingOnUser = 0x02,
// Waiting on Avatar (Character/Pawn/Actor) to do something (usually something physical in the world, like land, move, etc)
// WaitingOnAvatar = 0x04
*/
SetWaitingOnAvatar();
}
OnDestroy():
void URPGAbilityTask_PlayMontageAndWaitForEvent::OnDestroy(bool AbilityEnded)
{
// Note: Clearing montage end delegate isn't necessary since its not a multicast and will be cleared when the next montage plays.
// (If we are destroyed, it will detect this and not do anything)
// montage的delegate不是multicast的,所以会自动清除
//但是,multicast的delegate要手动清除:
// This delegate, however, should be cleared as it is a multicast
if (Ability)
{
Ability->OnGameplayAbilityCancelled.Remove(CancelledHandle);
if (AbilityEnded && bStopWhenAbilityEnds)
{
StopPlayingMontage();
}
}
URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();
if (RPGAbilitySystemComponent)
{
RPGAbilitySystemComponent->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
}
Super::OnDestroy(AbilityEnded);
}
网友评论