4、GAS相关概念4.7 Ability Tasks

4.7 Ability Tasks

4.7.1 Ability Tasks定义

GameplayAbilities 只在一帧内执行,这本身没有太多灵活性。为了执行那些需要持续时间或响应未来某个时刻触发的委托的动作,我们使用称为 AbilityTasks 的延迟动作。

GAS 提供了许多内置的 AbilityTasks

  • RootMotionSource 移动角色的任务
  • 播放动画蒙太奇的任务
  • 响应 Attribute 变化的任务
  • 响应 GameplayEffect 变化的任务
  • 响应玩家输入的任务
  • 等等

UAbilityTask 构造函数强制执行一个硬编码的游戏全局最大值:1000 个并发的 AbilityTasks 同时运行。在为像 RTS 游戏那样可以同时拥有数百个角色的游戏设计 GameplayAbilities 时,需要记住这一点。

4.7.2 自定义Ability Tasks

通常你会创建自己的自定义 AbilityTasks(在 C++ 中)。示例项目包含两个自定义 AbilityTasks

  1. PlayMontageAndWaitForEvent 是默认的 PlayMontageAndWaitWaitGameplayEvent AbilityTasks 的组合。它允许动画蒙太奇通过 AnimNotifies 从动画通知中发送游戏事件回到启动它们的 GameplayAbility。使用这个可以在动画蒙太奇中的特定时间触发动作。
  2. WaitReceiveDamage 监听 OwnerActor 是否受到伤害。被动护甲堆叠的 GameplayAbility 在英雄受到伤害时移除一层护甲。

AbilityTasks 由以下部分组成:

  • 创建新的 AbilityTask 实例的静态函数
  • AbilityTask 完成其目的时广播的委托
  • 启动其主要任务、绑定外部委托等的 Activate() 函数
  • 清理工作的 OnDestroy() 函数,包括它绑定的外部委托
  • 绑定到的任何外部委托的回调函数
  • 成员变量和任何内部辅助函数

注意: AbilityTasks 只能声明一种类型的输出委托。所有输出委托必须是这种类型,无论它们是否使用参数。如果不使用委托参数,请传递默认值。

AbilityTasks 只在运行拥有 GameplayAbility 的客户端或服务器上运行;但是,AbilityTasks 可以通过在 AbilityTask 构造函数中设置 bSimulatedTask = true;,覆盖 virtual void InitSimulatedTask(UGameplayTasksComponent& InGameplayTasksComponent);,并将任何成员变量设置为复制来设置为在模拟客户端上运行。这在像移动 AbilityTasks 这样的少数特殊情况中很有用,其中你不想复制每个移动变化,而是模拟整个移动 AbilityTask。所有 RootMotionSource AbilityTasks 都这么做。参考 AbilityTask_MoveToLocation.h/.cpp 作为示例。

如果在 AbilityTask 构造函数中设置 bTickingTask = true; 并覆盖 virtual void TickTask(float DeltaTime);AbilityTasks 可以进行每帧的更新。这在你需要平滑地跨帧插值值时非常有用。参考 AbilityTask_MoveToLocation.h/.cpp 作为示例。

4.7.3 使用Ability Tasks

在 C++ 中创建和激活一个 AbilityTask(来自 GDGA_FireGun.cpp):

UGDAT_PlayMontageAndWaitForEvent* Task = UGDAT_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(this, NAME_None, MontageToPlay, FGameplayTagContainer(), 1.0f, NAME_None, false, 1.0f);
Task->OnBlendOut.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnCompleted.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnInterrupted.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->OnCancelled.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->EventReceived.AddDynamic(this, &UGDGA_FireGun::EventReceived);
Task->ReadyForActivation();

在蓝图中,我们只需使用为 AbilityTask 创建的蓝图节点。我们不必调用 ReadyForActivation(),因为它会被 Engine/Source/Editor/GameplayTasksEditor/Private/K2Node_LatentGameplayTaskCall.cpp 自动调用。K2Node_LatentGameplayTaskCall 还会自动调用 BeginSpawningActor()FinishSpawningActor()(如果它们在你的 AbilityTask 类中存在)(参见 AbilityTask_WaitTargetData)。重申一下,K2Node_LatentGameplayTaskCall 只是对蓝图做了自动魔法处理。在 C++ 中,我们必须手动调用 ReadyForActivation()BeginSpawningActor()FinishSpawningActor()

蓝图 WaitTargetData 能力任务

要手动取消一个 AbilityTask,只需在蓝图(称为 Async Task Proxy)或 C++ 中调用 EndTask()

4.7.4 Root Motion Source Ability Tasks

GAS 提供了 AbilityTasks,用于通过 Root Motion SourcesCharacterMovementComponent 中移动角色,适用于击退、复杂跳跃、拉动和冲刺等操作。

注意: 预测 RootMotionSource AbilityTasks 适用于 4.19 和 4.25+ 版本的引擎。对于 4.20-4.24 版本的引擎,预测存在 bug;然而,AbilityTasks 在多人游戏中仍能正常执行,尽管会有轻微的网络修正,在单人游戏中表现完美。可以从 4.25 版本中挑选 预测修复 到自定义的 4.20-4.24 引擎中。