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
和WaitGameplayEvent
AbilityTasks
的组合。它允许动画蒙太奇通过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 引擎中。