UE 的 GAS 是什么#

GAS(Gameplay Ability System)是 UE 用于构建技能、属性、Buff/Debuff、状态控制和多人同步的一套玩法框架。它主要解决的不是“播放一个技能动画”,而是技能规模扩大以后出现的复杂问题:

  • 技能能否释放;
  • 消耗与冷却;
  • 动画、命中和伤害的时序;
  • 眩晕、霸体、无敌等状态互斥;
  • Buff叠层和持续时间;
  • 客户端预测与服务器校验;
  • 特效、音效和玩法逻辑分离。

可以把GAS概括为:

1ASC负责统筹
2GA负责执行行为
3GE负责修改状态
4Attribute负责保存数值
5Tag负责描述和约束状态
6AbilityTask负责异步流程
7GameplayCue负责表现


一、GAS的核心组成#

1. Ability System Component:系统中枢#

UAbilitySystemComponent,简称 ASC,是Actor进入GAS体系的入口。

它负责管理:

  • 当前拥有的技能;
  • 技能的授予、激活和移除;
  • Attribute和AttributeSet;
  • 当前生效的GameplayEffect;
  • 当前拥有的GameplayTag;
  • GameplayEvent;
  • 技能网络复制和预测。

一般让Actor实现IAbilitySystemInterface,通过GetAbilitySystemComponent()返回ASC。ASC既可以放在Character上,也可以放在PlayerState上。将ASC放在PlayerState上,可以让属性、技能和长时间冷却在角色死亡、换Pawn后继续存在。(Epic Games Developers)

ASC放在哪里#

放在Character:

  • 实现简单;
  • 适合AI、怪物、生命周期与角色一致的对象;
  • Character销毁时,ASC及其状态通常也随之销毁。

放在PlayerState:

  • 适合玩家角色;
  • Pawn死亡重生后仍可保留等级、技能和冷却;
  • 需要处理PlayerState和新Pawn之间的初始化关系。

面试中经常问:

玩家死亡并重新生成Character,技能冷却不应该重置,ASC应该放在哪里?

通常回答:放在PlayerState,PlayerState作为长期Owner,当前Character作为技能实际操作的Avatar。


2. Gameplay Ability:一个可执行的能力#

UGameplayAbility,简称 GA,描述“角色要做什么”。

例如:

  • 普通攻击;
  • 翻滚;
  • 冲刺;
  • 格挡;
  • 喝药;
  • 火球术;
  • 受击;
  • 死亡;
  • 被动技能。

GA不只是一个函数。它可以跨越多帧执行,等待动画、输入、命中数据或GameplayEvent,并在结束或取消时统一清理。它支持成本、冷却、标签约束、网络执行策略和客户端预测。(Epic Games Developers)

一个Ability的基本生命周期#

 1服务器授予Ability
 2        ↓
 3GiveAbility → FGameplayAbilitySpec
 4        ↓
 5TryActivateAbility
 6        ↓
 7CanActivateAbility
 8        ↓
 9ActivateAbility
10        ↓
11CommitAbility
12        ↓
13启动AbilityTask并等待动画/输入/事件
14        ↓
15造成效果或修改状态
16        ↓
17EndAbility / CancelAbility

GiveAbility通常只能由权威端执行,并返回FGameplayAbilitySpecHandle。TryActivateAbility会先检查能否激活,再真正调用激活逻辑;CommitAbility一般负责应用技能消耗和冷却;能力执行结束后必须调用EndAbility,否则GAS会继续认为技能处于激活状态,相关阻塞标签也可能一直存在。(Epic Games Developers)

CommitAbility为什么重要#

一般不要一进入ActivateAbility就无条件扣蓝、扣体力。

更典型的流程是:

1ActivateAbility
2→ 检查目标或开始瞄准
3→ 确定技能确实可以执行
4→ CommitAbility
5→ 扣除资源并进入冷却
6→ 执行后续技能流程

如果CommitAbility失败,通常应立即结束Ability。


3. Attribute与AttributeSet:角色数值#

Attribute是GAS管理的浮点型玩法数值,例如:

1Health
2MaxHealth
3Mana
4Stamina
5AttackPower
6Defense
7MoveSpeed
8CriticalRate

这些属性通常放在继承自UAttributeSet的类中。当前官方文档要求AttributeSet及其Attribute在原生C++中定义。(Epic Games Developers)

Base Value与Current Value#

FGameplayAttributeData包含两个重要值:

  • Base Value:基础值或永久值;
  • Current Value:考虑当前临时Modifier后的最终值。

例如:

1基础移动速度:600
2减速Debuff:-30%
3Current MoveSpeed:420

减速结束后,Current恢复到600,而Base没有改变。AttributeSet将属性集中管理,并支持属性复制、临时修改和变化回调。(Epic Games Developers)

常见回调#

PreAttributeChange#

属性变化前执行,常用于限制范围:

 1void UMyAttributeSet::PreAttributeChange(
 2    const FGameplayAttribute& Attribute,
 3    float& NewValue)
 4{
 5    Super::PreAttributeChange(Attribute, NewValue);
 6
 7    if (Attribute == GetMoveSpeedAttribute())
 8    {
 9        NewValue = FMath::Clamp(NewValue, 0.0f, 1200.0f);
10    }
11}

不建议在这里处理死亡、受击等复杂玩法反应。

PostGameplayEffectExecute#

Instant GameplayEffect完成修改后执行,适合:

  • 把Health限制在0~MaxHealth;
  • 处理Damage元属性;
  • 触发死亡;
  • 更新玩法状态。

官方也将PreAttributeChange定位为数值约束,将PostGameplayEffectExecute定位为效果执行后的最终处理和玩法反应。(Epic Games Developers)


4. Gameplay Effect:改变角色状态#

UGameplayEffect,简称 GE,负责描述“角色状态如何改变”。

例如:

  • 造成100点伤害;
  • 恢复50点生命;
  • 增加20%攻击力,持续10秒;
  • 每秒受到5点毒伤,持续8秒;
  • 永久增加最大生命;
  • 消耗30点体力;
  • 添加眩晕状态;
  • 添加技能冷却。

GameplayEffect本身通常是不可变的数据资产;运行时真正携带等级、来源、目标、动态数值和上下文的是FGameplayEffectSpec。(Epic Games Developers)

GE的三种持续类型#

类型

用途

示例

Instant

立即执行,不进入Active GE容器

伤害、治疗、永久加点

Has Duration

持续一段时间

10秒加速、5秒眩晕

Infinite

无限持续,直到主动移除

装备加成、被动状态

带持续时间的GE会进入ASC的Active Gameplay Effects Container;Instant GE执行后不会作为持续效果保存在其中。Periodic GE则会按设定周期重复执行。(Epic Games Developers)

Modifier、MMC与Execution Calculation#

Modifier#

适合简单数值操作:

1Health - 100
2AttackPower + 20
3MoveSpeed × 0.7

MMC#

UGameplayModMagnitudeCalculation用于计算一个Modifier的Magnitude。

例如:

1护盾值 = 技能等级 × 50 + 法术强度 × 0.6

它主要负责“算出一个数”,然后交给Modifier使用。(Epic Games Developers)

Execution Calculation#

UGameplayEffectExecutionCalculation适合复杂计算,并且可以一次输出多个属性修改。

例如伤害公式:

1基础伤害
2→ 读取攻击者攻击力
3→ 读取目标防御力
4→ 计算暴击
5→ 计算元素抗性
6→ 计算最终Damage
7→ 同时产生生命削减、韧性削减和吸血

Execution可以读取来源和目标的属性快照,再向输出中添加多个Modifier。(Epic Games Developers)

面试中经常问:

MMC和Execution Calculation有什么区别?

可以回答:

MMC通常为一个Modifier计算Magnitude,更适合可复用的单值公式;Execution Calculation适合一次执行涉及来源属性、目标属性、多步判断和多个输出的复杂公式,例如伤害结算。


5. Gameplay Tag:GAS的状态语言#

GameplayTag是层级化标签,例如:

 1Ability.Attack.Light
 2Ability.Attack.Heavy
 3Ability.Movement.Dodge
 4
 5State.Attacking
 6State.Stunned
 7State.Dead
 8State.Invincible
 9
10Cooldown.Skill.Fireball
11Event.Melee.Hit
12GameplayCue.Hit.Fire

Tag不是普通字符串,也不仅是Enum。它们可以形成层级关系:

1State.CrowdControl.Stun

同时也匹配更上层的:

1State.CrowdControl
2State

GameplayTag可以用于描述对象类型、当前状态、事件和触发条件。(Epic Games Developers)

Tag在Ability中的典型用途#

假设角色处于:

1State.Stunned

翻滚Ability可以配置:

1ActivationBlockedTags:
2    State.Stunned
3    State.Dead

攻击期间授予:

1ActivationOwnedTags:
2    State.Attacking

重攻击启动时,可以配置:

1CancelAbilitiesWithTag:
2    Ability.Attack.Light

GAS原生支持使用Tag:

  • 阻止其他技能;
  • 取消正在执行的技能;
  • 要求角色必须具有某状态;
  • 要求角色不得具有某状态;
  • 判断目标是否可被技能命中。(Epic Games Developers)

这比散落在各处的布尔变量更适合复杂技能系统:

1if (!bDead && !bStunned && !bAttacking && !bClimbing && ...)

会逐渐变成:

1ActivationBlockedTags:
2    State.Dead
3    State.Stunned
4    State.Climbing


6. Ability Task:跨帧异步流程#

GameplayAbility通常不依靠自己的Tick驱动流程,而是启动UAbilityTask。

常见Task包括:

  • 播放Montage并等待;
  • 等待GameplayEvent;
  • 等待输入按下或释放;
  • 等待延时;
  • 等待目标数据;
  • 等待属性变化;
  • 移动角色到指定位置。

AbilityTask可以跨越多帧,通过Delegate或蓝图输出引脚通知Ability,并会在所属Ability结束时最迟终止。(Epic Games Developers)

例如蓄力攻击:

1ActivateAbility
2→ WaitInputRelease
3→ 玩家持续按住
4→ 计算蓄力时间
5→ 输入释放
6→ 播放攻击Montage
7→ 等待命中事件
8→ 应用伤害GE
9→ EndAbility

AbilityTask和普通异步任务的区别#

AbilityTask并不等同于“把工作放到其他CPU线程”。

它主要是 玩法流程上的异步等待:

  • 等动画完成;
  • 等网络目标数据;
  • 等玩家输入;
  • 等事件;
  • 等时间。

它通常仍然参与游戏线程上的Ability流程。


7. Gameplay Cue:只负责表现#

GameplayCue用于表现性反馈:

  • Niagara特效;
  • 音效;
  • 镜头震动;
  • 材质变化;
  • 命中特效;
  • Buff持续特效。

常见生命周期:

  • OnActive;
  • WhileActive;
  • Removed;
  • Executed。

GameplayCue需要使用以GameplayCue开头的Tag,例如:

1GameplayCue.Hit.Slash
2GameplayCue.Buff.Burning

GameplayCue主要是网络友好的外观表现机制,不应承载扣血、添加状态等关键玩法逻辑。官方明确指出Cue不使用可靠复制,因此关键玩法结果不能依赖Cue是否成功播放。(Epic Games Developers)

正确关系是:

1GameplayEffect决定目标受到伤害
2GameplayCue负责播放受击火花和音效


二、用你的近战攻击项目理解GAS#

假设把你现在UE5 RPG项目里的近战攻击改成GAS,可以设计成下面这样。

1. 按下攻击键#

Enhanced Input不直接调用“扣血函数”,而是让ASC尝试激活:

1Ability.Attack.Light

2. GAS检查能否激活#

检查:

1角色是否死亡
2角色是否眩晕
3角色是否正在攀爬
4体力是否足够
5技能是否处于冷却
6当前是否允许进入下一段连击

这些条件主要由Tag、Cost GE、Cooldown GE和CanActivateAbility共同处理。

3. 激活攻击Ability#

1GA_LightAttack
2→ CommitAbility
3→ 应用体力消耗
4→ 应用攻击冷却
5→ 添加State.Attacking
6→ 播放Montage

4. 动画到达命中帧#

Anim Notify发送GameplayEvent:

1Event.Melee.Trace.Begin

Ability等待该Event,然后开始武器Trace。

5. 检测到目标#

生成Effect Spec:

1GE_Damage

其中保存:

  • 攻击者;
  • 目标;
  • 技能等级;
  • 命中位置;
  • 动态伤害;
  • 暴击信息;
  • Effect Context。

之后将Spec应用到目标ASC。

6. 目标结算伤害#

Execution Calculation读取:

1攻击者AttackPower
2目标Defense
3技能倍率
4暴击率
5伤害类型
6目标抗性

算出最终Damage。

目标AttributeSet在PostGameplayEffectExecute中:

1Health -= Damage
2→ Clamp Health
3→ Health <= 0时进入死亡流程

7. 播放表现#

触发:

1GameplayCue.Hit.Slash

播放粒子、声音、受击闪白,但不在Cue里执行扣血。

8. 攻击完成或被打断#

1Montage完成
2→ EndAbility
3
4收到State.Stunned
5→ CancelAbility
6→ 停止Montage
7→ 清理Trace
8→ 移除State.Attacking

完整数据流就是:

 1输入
 2→ ASC
 3→ GameplayAbility
 4→ AbilityTask播放动画
 5→ GameplayEvent开启攻击检测
 6→ TargetData
 7→ GameplayEffectSpec
 8→ Execution Calculation
 9→ AttributeSet
10→ GameplayCue
11→ EndAbility


三、GAS的多人网络逻辑#

GAS并不是让客户端直接决定伤害,而是在服务器权威的基础上,提供本地预测来改善操作手感。

Local Predicted#

玩家按下翻滚时:

  1. 客户端立即播放翻滚和预测性状态;
  2. 同时向服务器申请激活;
  3. 服务器重新检查体力、冷却、Tag和权限;
  4. 服务器接受则继续;
  5. 服务器拒绝则客户端撤销可回滚的预测结果。

官方提供四种主要Net Execution Policy:

哪些技能适合Local Predicted#

  • 普通攻击;
  • 翻滚;
    -冲刺;
  • 跳跃;
  • 本地玩家频繁使用且对延迟敏感的技能。

哪些适合Server Only或Server Initiated#

  • AI能力;
  • 服务器控制的环境机关;
  • 权威掉落;
  • 只在服务端完成的被动结算;
  • 不需要客户端即时响应的逻辑。

需要注意:可预测的非Instant GE通常更容易在服务器拒绝时回滚,而伤害等Instant属性变化不能简单依赖同样的回滚方式。因此通常让客户端预测动作、动画和部分状态,最终伤害仍由服务器权威确认。(Epic Games Developers)


四、Ability的实例化策略#

GAS提供三种Ability Instancing Policy。

Non-Instanced#

使用Ability的CDO执行,不生成实例。

优点:

  • 开销最低。

限制:

  • 不能保存运行时成员状态;
  • 不能绑定依赖实例的Delegate;
  • 不能使用蓝图图表执行核心逻辑;
  • 不能复制成员变量或处理Ability自身RPC。

适合大规模单位频繁执行、且完全无状态的简单能力。

Instanced Per Actor#

每个Actor持有一个Ability实例,多次执行重复使用。

优点:

  • 可保存成员变量;
  • 适合复制和RPC;
  • 不会每次释放都创建新UObject。

缺点:

  • 每次激活前必须清理上一次残留状态。

大多数角色技能可以优先考虑这一策略。

Instanced Per Execution#

每次激活都创建一个新实例。

优点:

  • 每次状态完全独立;
  • 可以让同一Ability并发执行多份。

缺点:

  • 对象创建成本最高。

适合低频、复杂、可能并发执行的能力。(Epic Games Developers)


五、GAS最常见的错误#

1. 忘记EndAbility#

结果:

  • Ability永远处于Active;
  • 激活标签不消失;
  • 被它阻塞的其他Ability无法使用;
  • AbilityTask和Delegate不能正常结束。

2. 在GameplayCue里写玩法逻辑#

Cue可能丢失,只能用于表现,不能负责扣血或决定死亡。

3. 直接修改Health#

例如:

1Health -= Damage;

这样会绕过:

  • GE上下文;
  • 网络预测;
  • Effect Tag;
  • 属性回调;
  • 伤害来源记录;
    -统一结算流程。

复杂项目中应尽量通过GE修改GAS Attribute。

4. 所有状态都做成Attribute#

Health适合Attribute,但“眩晕”“正在攻击”“无敌”更适合GameplayTag。

一般:

1连续数值 → Attribute
2离散状态 → GameplayTag
3行为流程 → GameplayAbility
4数值变化 → GameplayEffect
5外观反馈 → GameplayCue

5. 在Ability中堆积所有伤害公式#

伤害公式应尽量放在可复用的MMC或Execution Calculation中,而不是每个攻击GA各写一次。

6. Ability取消时没有清理外部资源#

需要清理:

  • Montage;
  • 碰撞检测;
  • 定时器;
  • 自定义Delegate;
  • 临时生成的Actor;
  • 锁定目标;
  • 输入模式。

AbilityTask能够随Ability结束,但Ability之外手工创建的资源仍需要自己管理。


六、总结GAS:#

GAS以ASC为中枢,用GA组织行为流程,用GE修改Attribute和Tag,用AbilityTask处理跨帧时序,用GameplayCue分离表现,并通过服务器权威与本地预测支持多人游戏。

原文:GAS