UE5.8 Gameplay Ability System 专题系列

UE5.8 GAS 专题(四):GameplayEffect、Spec 与 GE Component

深入 GameplayEffect 资产、FGameplayEffectSpec、ActiveGameplayEffect、Modifier、Duration、Stacking、SetByCaller 和 UE5.8 的 GameplayEffectComponent 路线。

总览

如果说 Ability 表达“我要做什么”,GameplayEffect 表达的就是“我对状态做了什么”。GAS 里属性修改、冷却、消耗、Buff、Debuff、免疫、授予能力、阻塞能力、Cue 触发,最终大多会落到 GE 或 GE Component。

本篇重点拆开三个概念:UGameplayEffect 是设计时资产,FGameplayEffectSpec 是本次应用的数据包,FActiveGameplayEffect 是 ASC 上的运行时持续状态。分不清这三层,后面写 SetByCaller、堆叠、预测和移除都会混乱。

UE5.8 GAS 专题(四):GameplayEffect、Spec 与 GE Component 配图
GameplayEffect 是模板,FGameplayEffectSpec 是本次施放的数据包,ActiveGameplayEffect 才进入持续状态和复制。

源码依据

关键文件:

  • GameplayEffect.h
  • GameplayEffectTypes.h
  • GameplayEffectComponent.h
  • AbilitySystemComponent.cpp

UE5.8 源码注释明确说:GE 是资产模板,运行时应视为不可变;Spec 是运行时包装,携带 Level、Context、SetByCaller、捕获属性和动态标签;持续效果进入 Active Gameplay Effects 容器。

AbilitySystemComponent.cpp 里的 ApplyGameplayEffectSpecToSelf 会做这些事:

  1. 检查 Authority 或 PredictionKey。
  2. 拒绝不该预测的周期效果。
  3. 检查 Application Query 和 CanApply
  4. 验证 Modifier 属性有效性。
  5. 预测 Instant GE 时临时按 Infinite 处理。
  6. 持续效果加入 ActiveGameplayEffects。
  7. Instant 效果立即 Execute。
  8. 触发 GameplayCue。
  9. 调用 GE 的 OnApplied

Asset、Spec、ActiveEffect

层级 类型 生命周期 作用
资产模板 UGameplayEffect 编辑器资产/CDO 定义 Duration、Modifier、Execution、Cue、Component
应用数据 FGameplayEffectSpec 一次施放 携带 Level、Context、SetByCaller、动态 Tag
活跃状态 FActiveGameplayEffect ASC 上持续存在 管理剩余时间、Stack、复制、移除

工程上要避免在 GE 资产上保存运行时数据。比如本次火球伤害是 120,不应该写进 GE CDO,而应该写进 Spec 的 SetByCaller。

FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(DamageEffect, AbilityLevel, ASC->MakeEffectContext());
if (SpecHandle.IsValid())
{
    static const FGameplayTag DamageTag = FGameplayTag::RequestGameplayTag(TEXT("Data.Damage"));
    SpecHandle.Data->SetSetByCallerMagnitude(DamageTag, RuntimeDamage);
    ASC->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
}

Duration Policy

类型 运行时行为 适合
Instant 立即执行,通常修改 BaseValue 伤害、治疗、资源消耗
Duration 进入 ActiveGameplayEffects,到时移除 眩晕、加速、护盾
Infinite 进入 ActiveGameplayEffects,直到主动移除 装备被动、光环、形态

Periodic 是持续 GE 的附加行为:每隔 Period 执行一次。源码里明确周期效果不预测,所以燃烧、流血、持续治疗这类效果要按服务器权威设计 UI 和表现。

Modifier 与聚合

GE Modifier 的操作类型来自 EGameplayModOp。源码里的计算模型不是简单从上到下依次执行,而是把 Add、Multiply、Divide、Override 等通道聚合后算最终值。

操作 用途 注意
Additive 加减固定值 最常用,伤害和治疗常见
Multiplicitive 百分比修饰 多个乘法修饰通常先聚合
Division 除法修饰 注意除零和数值设计
Override 覆盖属性 强控制状态谨慎使用

数值策划要知道:两个同通道百分比加成通常不是逐个连乘。比如 10% 和 30% 可能按同通道聚合为 40%,而不是 43%。

GameplayEffectComponent

UE5.8 延续了 GE Component 的方向。旧版 GE 里许多单体字段已经有 deprecated 路径,新项目更推荐用组件组合 GE 行为。

常见组件:

组件 作用
UAbilitiesGameplayEffectComponent GE 存在期间授予 Ability
UAdditionalEffectsGameplayEffectComponent 应用附加 GE
UAssetTagsGameplayEffectComponent 给 GE 资产声明标签
UBlockAbilityTagsGameplayEffectComponent 阻止某类 Ability
UCancelAbilityTagsGameplayEffectComponent 应用后取消某类 Ability
UChanceToApplyGameplayEffectComponent 概率应用
UImmunityGameplayEffectComponent 免疫规则
UTargetTagsGameplayEffectComponent 给目标授予标签
UTargetTagRequirementsGameplayEffectComponent 目标标签需求

GE Component 生活在 GE CDO/资产上,不应该保存每次执行的可变状态。如果需要运行时数据,放到 Spec、EffectContext、SetByCaller 或目标 ASC 上。

堆叠设计

Stacking 是 Buff/Debuff 系统的核心。设计时要回答:

  1. Stack 上限是多少。
  2. 新 Stack 是刷新 Duration,还是独立计时。
  3. Stack 溢出是拒绝、刷新、触发额外效果,还是替换旧 Stack。
  4. Stack 变化是否触发 Cue。
  5. UI 显示 Stack 数量还是只显示剩余时间。

不要等策划开始加“中毒可叠 5 层,满层爆炸”时才临时补字段。GE 资产一开始就应该按 Stack 规则分类。

项目落地分类

推荐按语义组织 GE:

前缀 示例 说明
GE_Init_ GE_Init_HeroAttributes 初始化属性
GE_Cost_ GE_Cost_ManaSmall Ability 消耗
GE_Cooldown_ GE_Cooldown_Fireball 冷却标签
GE_Damage_ GE_Damage_Fire 伤害
GE_Buff_ GE_Buff_Haste 正面状态
GE_Debuff_ GE_Debuff_Burning 负面状态
GE_Passive_ GE_Passive_SwordMastery 装备或天赋被动

每个 GE 资产都应该能回答:它改哪些属性、授予哪些标签、触发哪些 Cue、持续多久、是否可预测、谁能移除。

常见坑

  • 把运行时数值写进 GE 资产。 应使用 Spec 的 SetByCaller 或 EffectContext。
  • Instant 和 Duration 混用。 永久扣血用 Instant,临时加速用 Duration。
  • Periodic 想做客户端预测。 源码明确不预测周期效果。
  • GE 资产过度复用。 一个万能 GE 靠十几个 SetByCaller 控制,会让调试和内容审查变差。
  • 忽略 GE Component。 新项目还按旧字段堆逻辑,会越来越偏离 5.8 的演进方向。
  • 不写移除策略。 Infinite GE 必须知道谁移除、何时移除、断线或死亡时怎么处理。

本篇结论:GE 是 GAS 状态变化的标准通道。把 Asset、Spec、ActiveEffect 分清,再用 GE Component 组合行为,项目里的 Buff、冷却、消耗和被动才不会散成一堆特殊逻辑。

源码路径索引

  • 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