【AI笔记】【UE5】GAS学习笔记


一、 AttributeSet(属性集)

1. 问题引出:属性散落在多个组件的困境

用 Component 管理属性时,容易陷入”一个属性一个组件”的拆分:

属性 放在哪个组件
当前生命值、生命值上限 受伤组件
攻击力 攻击组件
移速 移动组件

随着属性增多(暴击率、法力值、防御力、韧性……),组件的数量也跟着膨胀。

🔥 三大痛点:

  • 跨组件耦合:受伤组件计算伤害时需要读取防御组件里的防御力,受伤组件必须”认识”防御组件
  • 连锁反应断裂:血量归零触发死亡,死亡需要禁用攻击组件、移动组件、动画组件——在多个组件之间来回通知
  • 新增成本高:每加一个新属性,就要决定”放哪个组件?要不要新建组件?已有组件要不要加引用?”

2. GAS 的解法:AttributeSet

AttributeSet = 将逻辑上互相关联的战斗属性打包在一起。

1
2
3
4
5
6
7
8
9
10
11
AttributeSet(一本属性大字典)
│
├── 生命值上限 (MaxHealth)
├── 当前生命值 (CurrentHealth)
├── 攻击力 (AttackPower)
├── 防御力 (Defense)
├── 暴击率 (CritRate)
├── 移速 (MoveSpeed)
├── 法力值 (Mana)
├── 韧性 (Poise)
└── ...

所有属性在一个池子里,互相之间天然可见——受伤时直接读防御力,死亡时直接禁移速,不需要跨组件修路。

💡 粒度选择:不是”所有属性一个大 Set”,也不是”一个属性一个 Set”,而是**”逻辑上同生共死的属性放一个 Set”**。

Component 拆散方案 一个属性一个 Set GAS 一类属性一个 Set
跨属性访问 ❌ 需要跨组件引用 ✅ 可以访问 ✅ 直接访问
属性修改管道 ❌ 自己手写 ❌ 每个 Set 配一套管道 ✅ 同 Set 共用管道
连锁反应 ❌ 跨组件通知 ❌ 跨 Set 通知 ✅ 同 Set 内搞定

🔥 多套 AttributeSet 按需组合:

1
2
3
4
5
6
7
玩家角色(什么都要):
AttributeSet_生存 ← 血量、防御、韧性
AttributeSet_战斗 ← 攻击力、暴击、攻速
AttributeSet_资源 ← 法力、耐力

可破坏木桶(只要生存):
AttributeSet_生存 ← 血量就够了

二、 修饰器系统(Modifier)

1. 问题引出:直接改属性值的死结

方案一:Buff 直接修改属性值。例如”力量药水:攻击力 ×1.2”。

1
2
3
4
5
6
7
基础攻击力 = 100

t=1s 🔵 喝力量药水 → 攻击力 = 100 × 1.2 = 120
t=3s 🟢 队友放战吼 → 攻击力 = 120 × 1.5 = 180
t=5s 🟡 触发暴怒 → 攻击力 = 180 + 50 = 230

t=8s 🔵 药水到期 → 应该恢复多少?

🚨 多个 Buff 叠加后,原始值和每个 Buff 的贡献被”拍扁”成一个数字 230,到期时无法精确还原。

就像一张纸折了三次,你试图只展开第一折,但纸已经被后两折压死了。

2. GAS 的解法:修饰器 = 便签条

Buff 不直接改属性值,而是在属性上”贴一张便签”。贴上生效,撕掉失效。基础值永不改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
攻击力 = 100 (基础值,永远不变)

t=1s 🔵 贴便签1: 攻击力 ×1.2(力量药水)
当前攻击力 = 100 × 1.2 = 120

t=3s 🟢 贴便签2: 攻击力 ×1.5(战吼)
当前攻击力 = 100 × 1.2 × 1.5 = 180

t=5s 🟡 贴便签3: 攻击力 +50(暴怒被动)
当前攻击力 = (100 × 1.2 × 1.5) + 50 = 230

t=8s 🔵 撕掉便签1(药水到期)
当前攻击力 = (100 × 1.5) + 50 = 200 ← 精确还原

🔥 核心价值:每层 Buff 独立存在,撕掉哪张只影响那一张,其他便签照旧,无需手动恢复。

3. 运算类型

每张便签由策划决定运算方式:

运算类型 含义 战斗场景举例
加算 (Add) 固定值增减 装备 +50 攻击力、Buff +200 生命
乘算 (Multiply) 百分比缩放 力量药水 攻击力×1.2、虚弱 攻击力×0.7
覆盖 (Override) 直接设置新值 无敌状态下防御力 = 99999
除算 (Divide) 除以系数 减速 移速/2、破甲 防御力/5

4. 执行顺序(管道)

GAS 的管道顺序是硬编码的,不能调整:

1
2
3
4
5
6
7
8
9
10
① 先检查覆盖便签(Override)
│
▼
② 计算所有加算便签(Add)
│
▼
③ 计算所有乘算便签(Multiply)
│
▼
④ 最终属性 = (基础值 + 加算总和) × 乘算总乘积

💡 如果需要”先乘再加”的效果:拆成两个属性,一个只受乘算影响(如基础攻击力),一个只受加算影响(如加成攻击力),最终求和。

1
最终攻击力 = 基础攻击力(乘算层) + 加成攻击力(加算层)

三、 属性修改三段式管道

1. 为什么不能”改值的同时处理连锁反应”

如果像普通 Component 那样在 TakeDamage 里同时做扣血、死亡判定、通知UI、清护盾:

  • 函数越来越臃肿
  • 后续策划需求(死亡演出期间继续扣血统计、临终诅咒自动释放)找不到合适的位置插入

2. GAS 的三段管道

1
2
3
4
5
6
7
PreAttributeChange()     ← 🚪 门卫:改之前检查能不能进
│
▼
实际属性修改 ← 📦 搬运工:只做一件事——改数值
│
▼
PostGameplayEffectExecute() ← 🏠 管家:改之后触发连锁反应

🎯 各司其职,互不污染。

3. 各阶段职责速查

阶段 角色比喻 职责
PreAttributeChange 🚪 门卫 钳制(血量不能为负/超上限)、拦截(无敌状态直接返回)、修正(护盾吸收,传入值从 100 修正为 30)
实际修改 📦 搬运工 只改数值,不思考任何逻辑
PostGameplayEffectExecute 🏠 管家 死亡判定(血量 ≤ 0?)、护盾清零、临终诅咒、通知 UI、通知 AI

4. 实战分配示例

策划需求 放在哪个阶段 原因
Boss 演出期间继续扣血(用于统计) Pre 不拦截,Post 做死亡标记 扣血归扣血,死亡归死亡,拆开
死亡时护盾清零 PostExecute 改完后判定死亡,顺手清护盾
临终诅咒技能 PostExecute 血量已经是 0 了,再触发技能,不会”先放技能再死”
血量低于 20% 自动获得护盾 PostExecute 扣完血再检查比例,不会”先套盾再扣血”

四、 GameplayEffect(效果)

1. 统一所有战斗行为的载体

GE 是 GAS 最核心的设计洞察——世界上只有一种东西:GE。

在你的 Component 方案里,”一刀砍 30 血”和”挂一个灼烧每秒扣 5 血”需要走两条完全不同的通道。GAS 把它们统一成一个东西:

你之前的分类 GAS 的分类 本质
通道1:直接伤害 TakeDamage(30) Instant GE 立刻结账,瞬间改完属性
通道2:灼烧 Buff Tick扣5 Periodic GE 分期付款,每 N 秒自动执行一次
通道2:减速 Buff 改移速 Duration GE 贴一张便签,到期自动撕

🎯 三条通道合为一条:GE。伤害是 GE,Buff 是 GE,Debuff 是 GE,治疗也是 GE。

2. 四种 GE 类型详解

GE 类型 比喻 持续时间 使用场景
Instant 一刀砍下去 无(立即生效并消失) 挥砍命中伤害、子弹命中伤害、药品瞬间回血
Duration 贴便签,到期自动撕 有明确时长 减速 DEBUFF(持续 3 秒)、力量药水(持续 30 秒)
Infinite 贴便签,手动才撕 永久(直到手动移除) 升级成长属性、被动技能、装备属性加成
Periodic 贴上后每隔 N 秒咬一口 有明确时长 + 执行间隔 DOT 持续扣血、HOT 持续回血、周期性 AOE 光环

💡 Duration 和 Periodic 的区别:Duration 挂上后持续生效(如减速),Periodic 挂上后每隔 N 秒执行一次瞬时效果(如每 1 秒扣 5 血)。Periodic 本质上 = Duration 的计时器 + Instant 的生效方式。

3. Magnitude 三层计算方式

GE 不问你”效果值是多少”,而是问你”效果值怎么算“。有三种层次:

层次 方式 说明 使用场景
第1层:ScalableFloat 固定值 / 曲线值 伤害 = 50,或根据等级查曲线 固定伤害陷阱、等级成长的固定数值
第2层:AttributeBased 基于属性 伤害 = 攻击方的攻击力 ×1.2 技能倍率直接乘属性,简单直观
第3层:CustomCalculationClass 自定义公式 读取双方任意属性自由组合 复杂伤害公式(见下方 ExecCalc)

4. ExecutionCalculation(ExecCalc)—— 伤害公式引擎

ExecCalc 是 GAS 内建的伤害公式引擎。它对应你方案里的”伤害计算公式组件”——但它是数据驱动的、可复用的、策划可配置的。

你的方案 GAS 的 ExecCalc
位置 技能激活 → 伤害公式组件 → TakeDamage(结果) 技能激活 → 施加GE → GE触发ExecCalc → 改目标AttributeSet
可读取的数据 手动传入的有限属性 攻击方和被攻击方的任意属性
公式修改 改组件代码 换一个 ExecCalc 类 / 在蓝图里调整公式
复用 每个技能可能用不同公式组件 不同技能可共用同一个 ExecCalc
示例 TakeDamage(Atk * 1.5 - Def * 0.3) Damage = (AttackPower × 1.5 - Defense × 0.3) × 暴击判定 × 属性克制

📐 同一个 GE 上可以多层共存:

1
2
3
4
火焰刀 GE:
├── 基础伤害 → ExecCalc 算(AttackPower × 1.5 - Defense × 0.3)
├── 灼烧 DOT → AttributeBased(取 AttackPower 的 10%,每 Tick 扣一次)
└── BUFF 时长 → ScalableFloat(固定 3 秒)

5. GE 与三段管道的对应关系

1
2
3
4
5
6
7
8
9
10
施加 GE
│
▼
PreAttributeChange() ← 门卫:钳制、拦截、修正
│
▼
属性实际修改 ← 搬运工:按 GE 的 Magnitude 改值
│
▼
PostGameplayEffectExecute() ← 管家:连锁反应

五、 GameplayAbility(技能)

1. GA 的定位:技能的老板

GAS 三块核心拼图的关系:

1
2
3
AttributeSet  = 属性的字典(数据在哪)
GameplayEffect = 修改属性的便签(怎么改)
GameplayAbility = 技能的逻辑容器(什么时候改、改谁、改完怎么办)

GA 自己不管数据,也不直接改属性——但它决定一切。

2. GA 的五项职责

职责 对应概念 说明
谁能用 赋予(Grant) 角色学了/装备了这个技能才能使用
什么时候能放 触发条件 + 冷却 + 消耗检查 按键触发 / 被动触发 / 事件触发;检查冷却、法力、沉默状态
对谁放 目标选择(TargetData) 自己 / 敌人 / 范围 / 地面点
放了有什么效果 施加 GE GA 的核心动作就是施加一个或多个 GE 到目标
放完之后干什么 冷却启动、连招窗口 给自己挂冷却 GE;可能开启下一段连招窗口

3. GA 完整生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
【赋予】技能给了这个角色(学技能/装备技能)
│
▼
【触发】玩家按了键 / 被动条件满足 / 事件触发
│
├── 检查冷却 GE 还在不在? → 在 → 不放
├── 检查法力够不够? → 不够 → 不放
├── 检查有没有被沉默? → 沉默了 → 不放
│
▼
【激活】扣法力 → 选目标 → 施加伤害 GE → 施加其他 GE → 给自己挂冷却 GE
│
▼
【结束】技能完成 → 可能开启连招窗口 / 进入下一段

4. 冷却机制:GA 检测 + GE 倒计时

角色 职责
GA(老板) 检测冷却状态 + 决定是否放技能 + 放完后启动冷却
冷却 GE(便签) 挂 N 秒自动失效——只负责”存在/不存在”
1
2
3
按技能键 → GA 查看"冷却 GE 还在吗?"
├── 在 → "冷却中,不放"
└── 不在 → 执行技能 → 给自己挂一个新的冷却 GE(CD=5秒)

GA 是老板,GE 是便签。老板看到便签还在,就不干活。老板干完活,贴一张新便签。


六、 GameplayTag(游戏标签)

1. GameplayTag 是什么

GameplayTag = 战斗系统的通用分类语言。

它是贴在游戏对象上的分层标签,用于替代硬编码的类型检查(如 Cast、if 字符串判断)。所有战斗规则都可以用 Tag 的包含/排除关系来表达。

2. 分层结构

1
2
3
4
5
6
Damage                          ← 父标签
├── Damage.Physical ← 子标签
│ ├── Damage.Physical.Slash
│ └── Damage.Physical.Blunt
├── Damage.Fire
└── Damage.Lightning

🔥 检测父标签时,子标签全部命中。 比如检测 Damage.Fire 免疫 → 所有火焰伤害全拦截。

3. 应用场景速查

用在哪 标签示例 说明
伤害类型 Damage.Physical、Damage.Fire、Damage.Lightning 分类伤害本质,用于免疫/加成判断
角色状态 State.Stunned、State.Silenced、State.Invulnerable 标记当前状态,技能前置条件检查用
阵营 Faction.Player、Faction.Enemy、Faction.Neutral AI 目标选择、友伤判断
技能分类 Ability.Melee、Ability.Ranged、Ability.Ultimate 格挡系统、成就统计
免疫关系 目标有 Immunity.Fire → Damage.Fire 类 GE 无效 火元素免疫火焰伤害

4. GE 上的 Tag vs GA 上的 Tag

🔥 这是一个容易被混淆的关键设计点:

GE 上的 Tag GA 上的 Tag
描述什么 “你受到了什么”(伤害的本质类型) “你做了什么”(攻击的形式与来源)
用途 目标用来判断免疫/加成 格挡系统、成就统计、AI 反应
例子 Damage.Fire—踩了岩浆、被火球砸、被火焰刀砍都带这个 Ability.Melee—格挡只能挡近战,就检查这个

💡 实际组合:

1
2
3
火焰刀 GA ──施加──→ 伤害 GE
├── GA 自带 Tag: Ability.Melee, Ability.FireSlash ← 格挡、成就统计用
└── GE 自带 Tag: Damage.Fire, Damage.Physical ← 目标免疫判断用

七、 GameplayCue(战斗表现)

1. 核心拆分:GE 管数据,Cue 管表现

GE 只负责数据层——改了多少属性、贴了什么便签。它对视觉表现一无所知。

🔥 同一个 GE、同一个伤害数据,不同目标的表现完全不同:

谁受到了同样的”火焰伤害” 应该怎么表现
普通敌人 火花粒子 + 后仰动画 + 音效
Boss 火花粒子(缩小版)+ 毫不动摇
玩家自己 屏幕边缘闪红 + 手柄震动
可破坏木桶 碎裂特效 + 木屑飞溅

如果 GE 自己决定表现,它就必须知道目标是玩家还是 Boss 还是木桶——破坏了 GE 的纯粹性,又回到了”必须认识目标类型”的老问题。

GAS 的拆分:

GE(GameplayEffect) GameplayCue(游戏表现提示)
管什么 属性怎么改(数据层) 伤害长什么样(表现层)
比喻 银行转账记录 收到转账的短信铃声
触发方式 施加 GE 时自动触发 通过 GE 上带的 Tag 自动匹配到 Cue
谁决定表现 不决定 每个目标自己决定自己的表现方式

2. 触发流程

1
2
3
4
5
6
7
8
9
10
施加伤害 GE(Tag: Damage.Fire)
│
├── 数据层:改 AttributeSet ← GE 负责
│
└── 表现层:触发 GameplayCue ← Cue 负责
│
└── 每个目标自己响应同一个 Tag:
玩家 → 屏幕闪红 + 手柄震动
敌人 → 火花粒子 + 受击动画
木桶 → 碎裂特效 + 碎片飞溅

🔥 同一个 Tag,每个目标有自己的”反应剧本”。GE 只发信号:”火焰伤害发生了!”——至于怎么表演,每个角色自己决定。

3. 触发时机:Cue 在数据确认改变后才触发

1
2
3
4
5
6
7
8
9
10
11
12
13
施加伤害 GE
│
▼
PreAttributeChange ← 门卫(可能拦截)
│
▼
实际属性修改 ← 数据真的变了
│
▼
PostGameplayEffectExecute ← 管家
│
├── 死亡判定
└── ⚡ 触发 GameplayCue ← 数据确认已改变,现在播表现

🚨 如果先播 Cue 再改数据:攻击无敌 Boss → 先播火花 → 再被 PreAttributeChange 拦截 → 扣血 0 → 玩家困惑”我明明砍中了”。

✅ 先改数据再播 Cue:数据被拦截 → Cue 不触发。如果要给反馈,单独发一个”免疫” Cue(飘字”免疫”)。

4. 两种 Cue 类型

类型 比喻 触发方式 使用场景
Instant Cue 相机”咔嚓”一声 触发一瞬间,播完即止 火花粒子、屏幕闪红、伤害飘字
Duration Cue 收音机一直响 一直持续,直到手动停止 灼烧持续特效、眩晕头顶星星、Buff 光环、CD 遮罩旋转、沉默图标变灰

Instant 是一锤子买卖,Duration 是开着的收音机——不关就一直响。

5. 表现场景速查

状态/事件 对应的 GameplayCue 表现 Cue 类型
受到伤害 火花粒子、屏幕闪红、手柄震动 Instant
冷却中 技能图标上的 CD 遮罩旋转 Duration
被沉默 技能图标变灰 + 沉默倒计时 UI Duration
被眩晕 眩晕动画 + ⭐星星特效 Duration
死亡 死亡动画 + 屏幕变灰 Duration
获得 Buff UI 上弹出 Buff 图标 Instant
治疗 绿色粒子 + 飘字 “+50” Instant

6. Tag 到 Cue 的映射机制

GAS 有一个全局的 GameplayCueManager,里面维护了一张翻译表:

1
2
3
4
5
6
7
GameplayCueManager(翻译表)
│
├── Tag: "GameplayCue.Damage.Fire" → 火花粒子特效
├── Tag: "GameplayCue.Damage.Physical" → 刀光 + 飙血粒子
├── Tag: "GameplayCue.Buff.PowerUp" → 红色光环
├── Tag: "GameplayCue.Status.Stun" → ⭐星星 + 眩晕动画
└── ...

🔥 Cue 用的 Tag 必须以 GameplayCue. 开头——这是 GAS 的硬性约定。GE 上带 Damage.Fire,GAS 自动在前面拼上 GameplayCue. → 变成 GameplayCue.Damage.Fire → GameplayCueManager 查表 → 找到对应的特效/音效。


八、 AbilityTask(技能时间线控制)

1. 问题引出:技能内部的时间线谁管?

以”蓄力重斩”为例——按下按键蓄力,按住 2 秒后挥出,中途松手则取消。

技能涉及多个时间节点:

1
2
3
4
5
6
时间轴:  0s              0.5s             1.5s             2.0s
│ │ │ │
▼ ▼ ▼ ▼
按键 蓄力 蓄力 蓄满
50% 90% 100%
特效变色 即将满 自动挥出

🔥 三大角色各管一摊:

角色 管什么 蓄力重斩中干了什么
Enhanced Input 玩家的手指 Started → 激活 GA;松手 → 信号传给 AbilityTask
GA(老板) 技能的启动和收尾 激活 → 启动 AbilityTask → 收到结果后决定砍人还是取消 → 启动冷却
AbilityTask(导演) 技能内部的时间线 每帧推进蓄力进度 → 触发阶段 Cue → 100% 或 Canceled 后通知 GA

2. AbilityTask 的本质

AbilityTask = 技能内部的导演。 它不关心输入从哪来,只关心”现在该干什么、下一步是什么、什么时候结束”。

每个 AbilityTask 都有两种结局:

1
2
3
4
AbilityTask 运行
│
├── ✅ Completed → "任务完成了,继续下一步"
└── ❌ Canceled → "任务被中断了,走取消逻辑"

3. 蓄力重斩完整分工

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
GA 激活(按键触发)
│
▼
启动 AbilityTask: WaitInputHold ← 监听着蓄力计时
│
├── Tick 0.0s → 蓄力 0%
├── Tick 0.5s → 蓄力 25% → 触发 GameplayCue(蓄力光效)
├── Tick 1.0s → 蓄力 50% → 触发 GameplayCue(特效变色)
├── Tick 1.5s → 蓄力 75%
│
├── Tick 2.0s → 蓄力 100%
│ └── ✅ Completed → 通知 GA:"蓄满了!"
│ │
│ ▼
│ GA 执行挥砍:
│ ├── 施加伤害 GE(满蓄力伤害加成)
│ ├── 播放挥砍动画(PlayMontageAndWait)
│ ├── 触发 Cue(满蓄力斩击特效)
│ └── 启动冷却 GE
│
└── 玩家松手 → ❌ Canceled → 通知 GA:"取消了!"
│
▼
GA 执行取消逻辑:
├── 不施加伤害 GE
├── 播放收刀动画
└── 可能返还部分法力/冷却

4. 与蒙太奇 Notify 的关系

蒙太奇的 Notify 窗口也管时间线上”窗口开在哪”,但它和 AbilityTask 职责不同:

1
2
3
4
5
6
7
8
9
10
第一刀 GA 激活 → 播放攻击蒙太奇
│
├── 🔔 Notify 窗口 开启 ← 蒙太奇负责:"哪一帧到哪一帧允许接下一刀"
│ │
│ └── ⏳ AbilityTask: WaitInputPress ← GAS 负责:"等不等、等多久、等到没等到"
│ │
│ ├── 0.3 秒内按了 → ✅ Completed → 激活第二刀 GA
│ └── 超时未按 → ❌ Canceled → 连击中断
│
└── 🔔 Notify 窗口 关闭

蒙太奇管”窗口开在动画的哪一帧”,AbilityTask 管”窗口内等什么、超时怎么办”。

5. 常用 AbilityTask 速查

AbilityTask 等什么 典型场景
WaitDelay 纯等 N 秒 蓄力计时、技能前摇、延时爆炸
WaitInputPress 等玩家按某个键 连招窗口、蓄力松手、提前引爆
WaitTargetData 等玩家选目标 指向性技能、AOE 范围选择
WaitGameplayEvent 等某个 Tag 事件发生 被动触发、受击反击、队友信号
PlayMontageAndWait 等动画播完 技能动画播完再进入冷却
WaitOverlap 等碰撞体碰到东西 刀光命中敌人、陷阱检测

九、 GameplayEvent(事件驱动技能)

1. 问题引出:不按按键的技能怎么触发?

很多战斗技能不是玩家”按”出来的:

技能 触发条件
荆棘光环 被攻击时自动反伤
底力(低保护盾) 血量低于 20% 时自动获得护盾
终结技解锁 目标血量低于 10% 时图标亮起
复仇(队友死亡) 队友死亡后攻击力提升

这些技能等的是一个**”世界通知”**,而不是玩家按键。

2. GameplayEvent 的本质

1
2
3
4
5
主动技能:
按键 → Enhanced Input → GA 激活

被动/条件技能:
世界发生某事 → GameplayEvent(Tag 信号) → GA 激活

🔥 GAS 用 Tag 字符串当信号,替代了事件分发器对象。

3. 完整链路

1
2
3
4
5
6
7
8
9
10
PostExecute 检测血量 < 20%
│
▼
发送 GameplayEvent(Tag: "Event.Health.Low")
│
▼
谁在等这个 Tag?
├── "底力" GA 的 WaitGameplayEvent ← 激活护盾 GA
├── UI 监听 ← 屏幕闪红警告
└── AI 监听 ← 触发撤退逻辑

4. 被动技能的运作模式

被动技能 = 角色出生时自动激活的 GA,激活后立即启动 WaitGameplayEvent 卡住等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
角色 BeginPlay
│
▼
ASC 赋予 + 自动激活 "底力" GA
│
▼
底力 GA 激活 → 不施加任何 GE,直接启动:
⏳ WaitGameplayEvent("Event.Health.Low")
│
│ ← GA 卡在这里,一直等,不消耗任何资源
│
▼
某次受击后,PostExecute 检测血量 < 20%
│
▼
发送 GameplayEvent( Tag: "Event.Health.Low" )
│
▼
WaitGameplayEvent 收到信号 → GA 继续执行:
├── 施加护盾 GE
├── 播放护盾 Cue
└── 可能启动 WaitGameplayEvent("Event.Health.Normal") ← 等血量恢复后移除

被动技能 = 一个激活后就卡在 WaitGameplayEvent 上的 GA。就像一位站岗的哨兵——不消耗资源、不发任何效果,静静等待信号。

5. 两种 GA 激活时机对比

主动技能(蓄力重斩) 被动技能(底力)
谁触发 玩家按键 被赋予后自动激活
激活时机 玩家操作时 角色出生时
激活后第一件事 施加 GE / 启动蓄力 Task 启动 WaitGameplayEvent,卡住等待
GA 生命周期 按键 → 执行 → 结束 出生 → 等待 → 触发效果 → 可能继续等待或结束

6. 典型被动技能事件速查

技能 触发事件 Tag 发送时机 GA 用什么等
荆棘光环(反伤) Event.Damage.Received PostExecute 扣完血 WaitGameplayEvent
底力(低保护盾) Event.Health.Low PostExecute 血量 < 20% WaitGameplayEvent
终结技解锁 Event.Target.HealthLow 目标的 PostExecute WaitGameplayEvent
复仇(队友死亡) Event.Teammate.Died 队友死亡 PostExecute WaitGameplayEvent

十、 GAS 七块核心拼图总览

模块 比喻 一句话
AttributeSet 📖 属性字典 数据存在哪
GameplayEffect 📝 便签 怎么改数据
GameplayAbility 👔 老板 什么时候改、改谁
GameplayTag 🏷️ 标签 分类和判断
GameplayCue 🔊 表现 伤害长什么样
AbilityTask 🎬 导演 技能内部时间线怎么跑
GameplayEvent 📡 信号 不按键也能放的技能

后续课程将继续深入网络同步(策划视角)、数据驱动配置等内容。