UE5.8 Gameplay Ability System 专题系列

UE5.8 GAS 专题(九):AbilityTask、TargetData 与目标选择

详解 AbilityTask 的异步模型、TargetData 的复制与确认、目标选择、服务器校验、自定义 TargetData 和常见射击/范围技能流程。

总览

Ability 经常需要等待:等待输入释放、等待动画事件、等待目标选择、等待服务器确认。把这些等待逻辑直接写在 Ability 里,会让流程又长又难复制。AbilityTask 的作用就是把异步步骤封装成可复用节点,而 TargetData 则把“我命中了谁”变成可复制、可确认、可校验的数据。

本篇讲 AbilityTask、TargetData 的执行路径,以及射击、点选、范围技能如何落地。

UE5.8 GAS 专题(九):AbilityTask、TargetData 与目标选择 配图
目标选择要变成可复制、可确认、可校验的 TargetData,而不是 Ability 里临时扫世界。

源码依据

关键路径:

  • AbilitySystemComponent.h
  • Abilities/Tasks
  • Abilities/GameplayAbilityTargetTypes.h
  • GameplayAbility.h

ASC 提供 TargetData 相关 RPC 和确认函数:

  • ServerSetReplicatedTargetData
  • ServerSetReplicatedTargetDataCancelled
  • ConfirmAbilityTargetData
  • CancelAbilityTargetData
  • CallReplicatedTargetDataDelegatesIfSet

这说明 TargetData 不是临时参数,而是 GAS 网络模型的一等公民。

AbilityTask 解决什么

AbilityTask 适合封装这些等待:

Task 用途
WaitInputPress 等下一次输入按下
WaitInputRelease 等输入释放,蓄力技能常用
WaitGameplayEvent 等外部 GameplayEvent
WaitTargetData 等目标选择
PlayMontageAndWait 播 Montage 并等结束、打断
WaitDelay Ability 内延迟

Task 的好处是:Ability 保持流程清晰,异步节点能统一处理预测、取消和结束。

TargetData 里放什么

常见 TargetData:

类型 内容 场景
Actor Data 目标 Actor 列表 点选、锁定技能
HitResult Data 命中点、法线、骨骼、物理材质 射击、近战
Location Data 位置、半径、方向 范围技能
自定义 Data 项目自定义结构 复杂弹道、格子、弱点

目标选择的关键不是客户端找到了谁,而是客户端提交了一份服务器能验证的数据。

射击技能流程

推荐流程:

  1. 客户端输入激活 Ability。
  2. Ability 播本地开火表现。
  3. 客户端 Trace 生成 HitResult TargetData。
  4. 通过 ASC 复制 TargetData 到服务器。
  5. 服务器验证距离、视线、武器状态、射击时间。
  6. 服务器构建 Damage GE。
  7. 服务器应用 GE,复制属性和 Cue。
void UGA_RifleFire::OnTargetDataReady(const FGameplayAbilityTargetDataHandle& Data)
{
    UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
    if (!ASC)
    {
        EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true);
        return;
    }

    FScopedPredictionWindow PredictionWindow(ASC, IsPredictingClient());

    if (HasAuthority(&CurrentActivationInfo))
    {
        ApplyDamageToTargetData(Data);
    }
    else
    {
        SendTargetDataToServer(Data);
    }
}

这里的示例强调结构:本地可以预测,但最终应用伤害必须走服务器校验。

服务器校验

服务器收到 TargetData 后不能直接相信。至少校验:

校验 说明
距离 命中点不能超过武器射程
视线 是否有遮挡
时间 是否过期,是否和开火时间匹配
阵营 是否可以攻击
状态 攻击者是否死亡、眩晕、无武器
频率 是否超过开火速率
目标有效性 Actor 是否仍存在且可受击

服务器校验失败时,应取消或拒绝本次 Ability 副作用,并让客户端预测回滚。

自定义 TargetData

当 HitResult 和 Actor 列表不够时,可以定义自定义 TargetData。比如格子战棋技能需要格子坐标,弹幕技能需要种子和时间戳。

自定义 TargetData 要注意:

  • 实现 NetSerialize。
  • 数据足够服务器验证。
  • 不放不可信的大对象指针。
  • 不放纯客户端才能理解的 UI 状态。
  • 版本变化要兼容旧客户端或做协议保护。

AbilityTask 与 EndAbility

Task 必须考虑 Ability 结束:

  • Ability 被取消时 Task 要清理委托。
  • Montage 被打断时要走失败路径。
  • TargetData 取消时要 EndAbility 或回到等待状态。
  • 输入释放晚于服务器拒绝时不能继续应用 GE。

一个常见实践是:Ability 内所有 Task 绑定都收束到少量处理函数,每个函数第一行检查 Ability 是否仍 active、ActorInfo 是否有效、ASC 是否有效。

项目落地模板

推荐把目标选择抽象成项目级 Task:

Task 说明
UAT_WaitWeaponHitData 武器 Trace、散布、HitResult TargetData
UAT_WaitGroundTargetData 地面点选、范围半径
UAT_WaitLockOnTargetData 锁定目标
UAT_PlayMontageAndWaitForEvent 动画和 GameplayEvent 合并

Ability 使用这些 Task,而不是每个 Ability 自己写 Trace 和输入。

常见坑

  • Ability 里临时找目标。 服务器无法验证客户端意图。
  • TargetData 不校验。 客户端可以提交超远距离或穿墙命中。
  • 异步回调不检查 Ability 状态。 Ability 已结束还继续应用 GE。
  • 自定义 TargetData 不做 NetSerialize。 多人下直接失效。
  • Task 里强引用 Actor 不清理。 取消或死亡后容易悬挂。
  • 预测窗口丢失。 输入释放、目标确认等后续事件需要新的预测窗口。

本篇结论:AbilityTask 让异步流程可复用,TargetData 让目标选择可复制和可校验。多人 GAS 项目里,“命中了谁”必须是一条清晰的数据流,而不是客户端一句口头声明。

源码路径索引

  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AbilitySystemComponent.h
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent.cpp
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/GameplayAbility.h
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.h
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AttributeSet.h
  • Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayPrediction.h