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:
PlayMontageAndWaitForEvent是默认的PlayMontageAndWait和WaitGameplayEventAbilityTasks的组合。它允许动画蒙太奇通过AnimNotifies从动画通知中发送游戏事件回到启动它们的GameplayAbility。使用这个可以在动画蒙太奇中的特定时间触发动作。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()。

要手动取消一个 AbilityTask,只需在蓝图(称为 Async Task Proxy)或 C++ 中调用 EndTask()。
4.7.4 Root Motion Source Ability Tasks
GAS 提供了 AbilityTasks,用于通过 Root Motion Sources 在 CharacterMovementComponent 中移动角色,适用于击退、复杂跳跃、拉动和冲刺等操作。
注意: 预测 RootMotionSource AbilityTasks 适用于 4.19 和 4.25+ 版本的引擎。对于 4.20-4.24 版本的引擎,预测存在 bug;然而,AbilityTasks 在多人游戏中仍能正常执行,尽管会有轻微的网络修正,在单人游戏中表现完美。可以从 4.25 版本中挑选 预测修复 到自定义的 4.20-4.24 引擎中。