信息
· 文章中可能会出现一些错误,希望大佬们可以在评论区指出;
07 - Ability Task
本文主要说明了UE5中Gameplay技能系统(Gameplay Ability System, GAS)中Ability Task 的相关内容。
概述
Ability Task(C++类为UAbilityTask)继承自UGameplayTask类,用于和GA配合使用。使用GAS的游戏通常会包括各种自定义的Ability Task,以实现独特的游戏逻辑功能。它在GA的执行过程中异步工作,并且可以通过调用委托(原生C++代码)或输出执行引脚(蓝图)来影响逻辑执行流程,这可以让GA在多个帧中执行,也可以让GA在同一帧中执行多个不同的功能。
Ability Task可以通过调用EndTask()函数自行终止,或者当运行它的GA结束时自动终止。
开发须知
在进行Ability Task的开发和使用前,需要了解如下基本开发须知:
- 定义一些
BlueprintAssignable的动态多播委托,它们是该蓝图节点的输出引脚,用于执行后续逻辑; - 定义一个静态工厂函数,用于实例化该Ability Task并可能设置初始参数(该函数的参数可作为该蓝图节点的输入引脚)。需要注意的是,工厂函数只需进行实例化相关操作,它不应调用任何回调委托;
- 重写
Activate()函数。该函数应实际启动/执行你的Ability Task逻辑。在此处调用回调委托是安全的; - 别忘了在逻辑最后执行
EndTask()函数,此外也要通过重写::OnDestory()注销此Task注册的回调函数;
此外,对于在Ability Task中想要生成Actor的需求,GAS的Ability Task提供了BeginSpawningActor()和FinishSpawningActor()函数,具体实例详见AbilityTask_SpawnActor()类。
简单使用
接下来以一个例子简要说明一下Ability Task的使用。在Aura项目中,我们通过Ability TaskAT_TargetDataUnderMouse获取鼠标下对象的碰撞信息,并将其通过TargetData的方式发送给服务器,最终为生成火球的GA提供发射方向信息,具体代码如下:
AT_TargetDataUnderMouse.h点击展开/折叠代码
#pragma once
#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "AT_TargetDataUnderMouse.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseTargetDataDelegate, const FGameplayAbilityTargetDataHandle&, DataHandle);
/**
*
*/
UCLASS()
class AURA_API UAT_TargetDataUnderMouse : public UAbilityTask
{
GENERATED_BODY()
public:
/* vv动态多播委托可以变成节点中的可执行引脚vv */
/// 拿到目标位置信息时
UPROPERTY(BlueprintAssignable)
FMouseTargetDataDelegate OnValidDataGet;
/* vv每个AbilityTask都得有这个static工厂函数, 该函数会作为可调用的蓝图节点显示vv */
/// 获取鼠标指向对象的目标数据
/// @param OwningAbility 拥有该AbilityTask的对象, 即GA自己
/// @return 创建好的AbilityTask实例
UFUNCTION(BlueprintCallable, Category = "Ability|Tasks", meta = (DisplayName = "Target Data Under Mouse",
HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"))
static UAT_TargetDataUnderMouse* CreateTargetDataUnderMouseProxy(UGameplayAbility* OwningAbility);
private:
/// 客户端: 获取鼠标指向对象的准确位置, 发送给服务器
void SendMouseCursorData();
/// 服务器: 监听并接收位置信息, 在服务器上进行进一步处理的回调函数
void OnAbilityTargetDataSet(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag);
virtual void Activate() override;
};
AT_TargetDataUnderMouse.cpp点击展开/折叠代码
#include "AbilitySystem/AbilityTasks/AT_TargetDataUnderMouse.h"
#include "AbilitySystemComponent.h"
UAT_TargetDataUnderMouse* UAT_TargetDataUnderMouse::CreateTargetDataUnderMouseProxy(UGameplayAbility* OwningAbility)
{
UAT_TargetDataUnderMouse* MyObj = NewAbilityTask<UAT_TargetDataUnderMouse>(OwningAbility);
// 在这里初始化一些值
return MyObj;
}
void UAT_TargetDataUnderMouse::SendMouseCursorData()
{
// 此作用域参与客户端网络预测
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
// 通过PlayerController获取鼠标指向对象的位置
APlayerController* PlayerController = Ability->GetCurrentActorInfo()->PlayerController.Get();
FHitResult CursorHit;
PlayerController->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
// 存入TargetData, 并发送到服务器
// 这里的new不必担心, 后续Handle.Add()会将其封装到共享指针中
FGameplayAbilityTargetData_SingleTargetHit* TargetData = new FGameplayAbilityTargetData_SingleTargetHit();
TargetData->HitResult = CursorHit;
FGameplayAbilityTargetDataHandle TargetDataHandle;
TargetDataHandle.Add(TargetData);
AbilitySystemComponent->ServerSetReplicatedTargetData(
GetAbilitySpecHandle(),
GetActivationPredictionKey(),
TargetDataHandle,
FGameplayTag(),
AbilitySystemComponent->ScopedPredictionKey
);
// 客户端的预测广播, 为了减少延迟
if (ShouldBroadcastAbilityTaskDelegates())
{
OnValidDataGet.Broadcast(TargetDataHandle);
}
}
void UAT_TargetDataUnderMouse::OnAbilityTargetDataSet(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag)
{
// 告知ASC数据已被服务器接受并使用, 无需继续缓存下去
AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
// 服务器的确认广播, 确保数据正确和同步
if (ShouldBroadcastAbilityTaskDelegates())
{
OnValidDataGet.Broadcast(DataHandle);
}
}
void UAT_TargetDataUnderMouse::Activate()
{
const bool bIsClient = Ability->GetCurrentActorInfo()->IsLocallyControlled();
if (bIsClient)
{
// 客户端: 发送TargetData
SendMouseCursorData();
}
else
{
// 服务器: 监听TargetData
FGameplayAbilitySpecHandle GASpecHandle = GetAbilitySpecHandle();
FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
AbilitySystemComponent->AbilityTargetDataSetDelegate(GASpecHandle, ActivationPredictionKey)
.AddUObject(this, &UAT_TargetDataUnderMouse::OnAbilityTargetDataSet);
// 如果服务器监听操作比客户端发送操作慢, 就得立刻执行监听回调
const bool bIsDelegateCalled = AbilitySystemComponent->
CallReplicatedTargetDataDelegatesIfSet(GASpecHandle, ActivationPredictionKey);
if (!bIsDelegateCalled)
{
// 监听回调没有被立即执行, 说明服务器还在等客户端的TargetData数据
SetWaitingOnRemotePlayerData();
}
}
}
上述Ability Task在蓝图内可转换为一个节点,其中定义的委托为输出执行引脚:

