09 TargetData 目标与瞄准
目标:掌握 GAS 中"技能要打谁"的标准数据通道——
FGameplayAbilityTargetData与 Handle 的完整结构、自定义 TargetData 子类化(网络序列化)、TargetActor 的工作原理与四种确认类型、目标过滤器、World Reticle 准星系统、服务器端目标生成、GE Container 便捷封装,以及四种经典目标系统场景。
9.1 为什么需要 TargetData
技能的"目标"远不止"一个敌人"那么简单:可能是指向性(鼠标点谁打谁)、范围(AOE 圈)、方向(扇形)、甚至无目标(自身 Buff)。GAS 用统一的 FGameplayAbilityTargetData 结构承载目标信息,让技能逻辑与"目标怎么选"解耦。
关键:
FGameplayAbilityTargetData是基类,不应该直接实例化——必须用它的内置子类或自定义子类。
9.2 TargetData 的结构体系
9.2.1 内置子类
| 类型 | 说明 |
|---|---|
FGameplayAbilityTargetData_SingleTargetHit |
单个命中(射线/HitResult),含撞击点/法线 |
FGameplayAbilityTargetData_ActorArray |
一组 Actor(AOE 命中角色) |
FGameplayAbilityTargetData_LocationInfo |
一个位置(地面 AOE 中心、指向点) |
FGameplayAbilityTargetData_SingleActor |
单个 Actor |
9.2.2 FGameplayAbilityTargetDataHandle(包装器)
直接传 FGameplayAbilityTargetData 指针有内存管理风险(基类多态、生命周期),GAS 用 Handle 包装 TArray< TSharedPtr<FGameplayAbilityTargetData> >:
// 创建 Handle 并添加目标
FGameplayAbilityTargetDataHandle Handle;
// 霰弹枪多弹丸命中
for (int32 i = 0; i < HitResults.Num(); i++)
{
FGameplayAbilityTargetData_SingleTargetHit* HitData =
new FGameplayAbilityTargetData_SingleTargetHit();
HitData->HitResult = HitResults[i];
Handle.Add(HitData);
}
// 读取
if (Handle.Num() > 0)
{
const FGameplayAbilityTargetData* Data = Handle.Get(0);
const FHitResult* Hit = Data->GetHitResult();
AActor* Target = Hit->GetActor();
}
9.2.3 自定义 TargetData 子类(携带任意数据)
USTRUCT(BlueprintType)
struct MYGAME_API FGameplayAbilityTargetData_CustomData : public FGameplayAbilityTargetData
{
GENERATED_BODY()
public:
UPROPERTY() FName CustomName = NAME_None;
UPROPERTY() int32 HitBoneIndex = -1;
// 必须重写 1:返回自身 ScriptStruct(用于复制类型识别)
virtual UScriptStruct* GetScriptStruct() const override
{
return FGameplayAbilityTargetData_CustomData::StaticStruct();
}
// 必须重写 2:网络序列化
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
CustomName.NetSerialize(Ar, Map, bOutSuccess);
Ar.SerializeIntPacked(HitBoneIndex);
bOutSuccess = true;
return true;
}
};
// 必须 3:启用网络序列化类型特性
template<>
struct TStructOpsTypeTraits<FGameplayAbilityTargetData_CustomData> :
public TStructOpsTypeTraitsBase2<FGameplayAbilityTargetData_CustomData>
{
enum { WithNetSerializer = true }; // 必须为 true,否则 Handle 无法跨网络传输
};
常见陷阱:忘记
WithNetSerializer = true→ Handle 的网络序列化失效,客户端与服务器之间无法传递 TargetData。
9.2.4 TargetData 生命周期
1. 产生(Production)
├─ TargetActor 的射线检测/碰撞重叠
├─ 手动创建(GA 内构造)
└─ GE Container 的即时目标选择
2. 传递(Transfer)
├─ 存入 EffectContext(AddHitResult)
├─ 通过 RPC 从客户端发到服务器
└─ 通过 Handle 在蓝图/C++ 传递
3. 消费(Consumption)
├─ AbilityTask 接收并处理
├─ GE 应用时使用
├─ ExecCalc / MMC 中读取
└─ GameplayCue 中访问
// 存入 EffectContext,随 GE 传递
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
Ctx.AddHitResult(HitResult);
FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(DamageGE, Level);
Spec.Data.Get()->SetContext(Ctx);
ASC->ApplyGameplayEffectSpecToTarget(*Spec.Data.Get(), TargetASC);
9.3 TargetActor:谁来"选目标"
AGameplayAbilityTargetActor 由 WaitTargetData AbilityTask 生成,负责可视化 + 捕获目标信息。常见子类:
| TargetActor | 功能 | 适用 |
|---|---|---|
AGameplayAbilityTargetActor_SingleLineTrace |
单条射线检测 | 射击武器、指向性技能 |
AGameplayAbilityTargetActor_Radius |
半径范围(可拖拽) | AOE 放置 |
AGameplayAbilityTargetActor_ActorPlacement |
放置预览 Actor | 建筑/陷阱放置 |
AGameplayAbilityTargetActor_GroundTrace |
地面追踪(鼠标指地) | 地面 AOE |
工作流程
GameplayAbility 激活
↓
WaitTargetData AbilityTask 启动
↓
生成 TargetActor(可选:可视化组件:贴花/静态网格/准星)
↓
TargetActor 在 Tick 中执行射线检测 / 碰撞重叠
↓
玩家确认目标(或自动确认)
↓
TargetData 就绪,广播给 GameplayAbility
↓
GA 使用 TargetData 应用 GE
↓
TargetActor 销毁
可视化组件
TargetActor 是 AActor,可挂任意可视化组件:
AMyTargetActor::AMyTargetActor()
{
// AOE 地面贴花指示器
DecalComponent = CreateDefaultSubobject<UDecalComponent>(TEXT("AOEDecal"));
DecalComponent->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f));
DecalComponent->DecalSize = FVector(500.f, 500.f, 500.f);
RootComponent = DecalComponent;
PrimaryActorTick.bCanEverTick = true;
}
目标捕获方式
| 捕获方式 | 说明 | 适用场景 |
|---|---|---|
| 射线检测(Line Trace) | 起点到终点的直线检测 | 射击武器、指向性技能 |
| 碰撞重叠(Overlap) | 用碰撞体检测重叠 | AOE 技能、范围效果 |
| 球形检测(Sphere Trace) | 带半径的射线检测 | 需要容错的射击 |
9.4 四种确认类型
EGameplayTargetingConfirmation::Type 决定目标何时被确认:
| 确认类型 | 行为 | 适用场景 |
|---|---|---|
Instant |
立即生成 TargetData 并销毁,不调 Tick | 即时命中武器 |
UserConfirmed |
等待玩家按确认键(绑定 Confirm 输入) | AOE 目标选择 |
Custom |
由 GA 通过 ConfirmTaskByInstanceName() 确认 |
自定义确认逻辑 |
CustomMulti |
同 Custom,但确认后不结束 Task | 多次选择目标(连锁技能) |
重要:不是每种确认类型都被所有 TargetActor 支持。例如
AGameplayAbilityTargetActor_GroundTrace不支持Instant确认。
标准用法:WaitTargetData Task
void UGA_Fireball::ActivateAbility(...)
{
UAbilityTask_WaitTargetData* TargetTask =
UAbilityTask_WaitTargetData::CreateWaitTargetData(
this, NAME_None,
EGameplayTargetingConfirmation::UserConfirmed,
TargetActorClass);
TargetTask->ValidData.AddDynamic(this, &UGA_Fireball::OnTargetDataReady);
TargetTask->Cancelled.AddDynamic(this, &UGA_Fireball::OnTargetDataCancelled);
TargetTask->ReadyForActivation();
}
void UGA_Fireball::OnTargetDataReady(const FGameplayAbilityTargetDataHandle& Data)
{
if (Data.Num() > 0)
{
const FGameplayAbilityTargetData* First = Data.Get(0);
AActor* Target = First->GetHitResult()->GetActor();
// 服务端权威:生成投射物 / 施加 GE
}
}
服务器端目标生产(ShouldProduceTargetDataOnServer)
| 配置 | 行为 | 优缺点 |
|---|---|---|
false(默认) |
客户端生成 TargetData → RPC 发到服务器 | 更响应,但需服务器验证防作弊 |
true |
客户端只发送确认事件,服务器自行生成 | 更安全、无作弊风险,但客户端可能有预测偏差 |
// TargetActor 构造函数中设置
ShouldProduceTargetDataOnServer = true;
9.5 目标过滤器(TargetData Filter)
基础过滤
// Blueprint 节点:Make GameplayTargetDataFilter + Make Filter Handle
FGameplayTargetDataFilter Filter;
Filter.ObjectFilter = SelfActor; // 排除的对象
Filter.FilterType = ETargetDataFilterSelf::Type::TDFS_NoSelf; // 排除自己
FGameplayTargetDataFilterHandle FilterHandle = MakeFilterHandle(Filter, SelfActor);
子类化过滤器
USTRUCT(BlueprintType)
struct FMyNameTargetDataFilter : public FGameplayTargetDataFilter
{
GENERATED_BODY()
virtual bool FilterPassesForActor(const AActor* ActorToBeFiltered) const override;
};
bool FMyNameTargetDataFilter::FilterPassesForActor(const AActor* ActorToBeFiltered) const
{
if (!Super::FilterPassesForActor(ActorToBeFiltered)) return false;
const AMyCharacter* MyChar = Cast<AMyCharacter>(ActorToBeFiltered);
if (!MyChar) return false;
return MyChar->GetTeam() != OwningTeam; // 只接受敌方
}
配套 Make Filter Handle 蓝图函数:
FGameplayTargetDataFilterHandle UMyBlueprintLibrary::MakeMyFilterHandle(
FMyNameTargetDataFilter Filter, AActor* FilterActor)
{
FGameplayTargetDataFilter* NewFilter = new FMyNameTargetDataFilter(Filter);
NewFilter->InitializeFilterContext(FilterActor);
FGameplayTargetDataFilterHandle FilterHandle;
FilterHandle.Filter = TSharedPtr<FGameplayTargetDataFilter>(NewFilter);
return FilterHandle;
}
9.6 World Reticle(瞄准准星/指示器)
AGameplayAbilityWorldReticle 用于可视化"你在瞄准谁",由 TargetActor 生成和销毁。
实现(GASShooter 用 WidgetComponent)
UCLASS()
class AMyReticle : public AGameplayAbilityWorldReticle
{
GENERATED_BODY()
public:
AMyReticle()
{
ReticleWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("ReticleWidget"));
ReticleWidget->SetWidgetSpace(EWidgetSpace::Screen); // 屏幕空间 UI
RootComponent = ReticleWidget;
}
// Blueprint 事件供设计师用
UFUNCTION(BlueprintImplementableEvent) void OnValidTargetChanged(bool bNewValue);
UFUNCTION(BlueprintImplementableEvent) void OnTargetingAnActor(bool bNewValue);
UFUNCTION(BlueprintImplementableEvent) void OnParametersInitialized();
};
关键 Blueprint 事件
| 事件 | 触发时机 |
|---|---|
OnValidTargetChanged |
目标有效性变化时 |
OnTargetingAnActor |
是否瞄准到 Actor 时 |
OnParametersInitialized |
Reticle 参数初始化完成 |
SetReticleMaterialParamFloat/Vector |
设置材质参数 |
目标持久化(锁定系统)
默认 TargetActor 目标只在射线/碰撞范围内有效。要"记住最后一个有效目标"(追踪锁定),自定义实现:
void AMyTargetActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FHitResult HitResult;
if (PerformTrace(HitResult))
{
LastValidTarget = HitResult.GetActor(); // 找到新目标更新
LastValidHitResult = HitResult;
}
// 没找到则保持 LastValidTarget(目标持久化)
if (Reticle && LastValidTarget)
Reticle->SetActorLocation(LastValidTarget->GetActorLocation());
}
Reticle 默认只在目标有效时显示;持久化需要自定义 TargetActor 管理 Reticle 生命周期。
9.7 GE Container(Action RPG 便捷封装)
Epic 的 Action RPG 提供 FGameplayEffectContainer——把 TargetData 生产 + GE 应用封装为数据驱动结构,用 CDO 运行、不生成 Actor:
// 基类:目标选择器
UCLASS(Blueprintable, EditInlineNew)
class URPGTargetType : public UObject
{
GENERATED_BODY()
public:
virtual void GetTargets_Implementation(
AActor* TargetingActor, FGameplayEventData EventData,
TArray<FGameplayAbilityTargetDataHandle>& OutTargetData) const;
};
// 子类:事件目标
UCLASS()
class URPGTargetType_EventTarget : public URPGTargetType
{
GENERATED_BODY()
public:
virtual void GetTargets_Implementation(
AActor* TargetingActor, FGameplayEventData EventData,
TArray<FGameplayAbilityTargetDataHandle>& OutTargetData) const override
{
if (EventData.Target)
{
FGameplayAbilityTargetDataHandle Handle;
Handle.Add(new FGameplayAbilityTargetData_SingleActor(
const_cast<AActor*>(EventData.Target)));
OutTargetData.Add(Handle);
}
}
};
// 子类:AOE 球形检测
UCLASS()
class URPGTargetType_AOE : public URPGTargetType
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere) float Radius = 500.f;
virtual void GetTargets_Implementation(
AActor* TargetingActor, FGameplayEventData EventData,
TArray<FGameplayAbilityTargetDataHandle>& OutTargetData) const override
{
TArray<FOverlapResult> Overlaps;
FCollisionQueryParams Params;
Params.AddIgnoredActor(TargetingActor);
TargetingActor->GetWorld()->OverlapMultiByChannel(
Overlaps, TargetingActor->GetActorLocation(), FQuat::Identity,
ECC_Pawn, FCollisionShape::MakeSphere(Radius), Params);
FGameplayAbilityTargetDataHandle Handle;
for (const FOverlapResult& Overlap : Overlaps)
if (Overlap.GetActor())
Handle.Add(new FGameplayAbilityTargetData_SingleActor(Overlap.GetActor()));
OutTargetData.Add(Handle);
}
};
GE Container 优点:CDO 运行不生成 Actor(性能优)、即时执行无需确认、数据驱动配置。适合瞬发自我治疗、AOE 范围伤害。
9.8 四种经典目标系统场景
场景一:射击游戏即时命中
射击输入 → WaitTargetData(Instant) → 单线射线 → 命中 → 应用伤害 GE
SingleLineTrace+Instant确认 + 排除自己 + 不需要 Reticle。
场景二:AOE 地面目标选择
技能激活 → WaitTargetData(UserConfirmed) → 地面检测 → 贴花显示范围
→ 鼠标调整 → 点击确认 → TargetData(位置+范围) → AOE 伤害 GE
GroundTrace+UserConfirmed+ 地面贴花 + Reticle。
场景三:锁定目标系统
锁定输入 → 射线找到目标 → 锁定 → Reticle 持续显示 → 攻击指向锁定目标
→ 解锁输入 或 目标死亡 → 解除锁定
- 自定义 TargetActor 目标持久化 + WidgetComponent 锁定标记。
场景四:追踪弹
发射 → 锁定目标 → 生成追踪弹 → 追踪弹飞向目标 → 碰撞 → 伤害 GE
- 自定义 TargetActor 保持锁定,目标信息存入 EffectContext 传给投射物。
9.9 目标有效性过滤与二次校验
在施加 GE 前做二次校验(防御"目标已死亡/免疫"):
// 方式一:GE 配置 Target Ignore Tags = State.Dead, State.Immune
// 方式二:代码判断
if (TargetASC->HasMatchingGameplayTag(StateDeadTag)) { /* 不施加 */ }
9.10 性能注意点
- 非 Instant 的 TargetActor 每个 Tick 执行射线检测 + Debug 绘制;
- 通常只有一个 TargetActor 运行,性能影响小;
- 复杂 TargetActor 可降低 Tick 频率(
SetActorTickInterval)优化。
9.11 本章验证
- [ ] 用
WaitTargetData+SingleLineTrace实现"指向目标火球"; - [ ] 切换
UserConfirmed,体验"选中 → 点击释放"流程; - [ ] 自定义一个扇形 TargetActor(或子类化 TargetData 携带命中部位),验证扇形内多目标命中;
- [ ] 写一个自定义过滤器只接受敌方,配合 WaitTargetData 使用;
- [ ] 给 AOE 技能加 Reticle 准星(OnValidTargetChanged/OnTargetingAnActor);
- [ ] 用 GE Container 实现瞬发自我治疗 + AOE 范围伤害;
- [ ] 联机验证:客户端选目标 → 服务端结算,模拟代理不越权。