【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 = 战斗系统的通用分类语言。

它是贴在游戏对象上的分层标签,用于替代硬编码的类型检查(如 Castif 字符串判断)。所有战斗规则都可以用 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.PhysicalDamage.FireDamage.Lightning 分类伤害本质,用于免疫/加成判断
角色状态 State.StunnedState.SilencedState.Invulnerable 标记当前状态,技能前置条件检查用
阵营 Faction.PlayerFaction.EnemyFaction.Neutral AI 目标选择、友伤判断
技能分类 Ability.MeleeAbility.RangedAbility.Ultimate 格挡系统、成就统计
免疫关系 目标有 Immunity.FireDamage.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 📡 信号 不按键也能放的技能

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