【AI笔记】【UE5】GAS学习笔记
【AI笔记】【UE5】GAS学习笔记
一、 AttributeSet(属性集)
1. 问题引出:属性散落在多个组件的困境
用 Component 管理属性时,容易陷入”一个属性一个组件”的拆分:
| 属性 | 放在哪个组件 |
|---|---|
| 当前生命值、生命值上限 | 受伤组件 |
| 攻击力 | 攻击组件 |
| 移速 | 移动组件 |
随着属性增多(暴击率、法力值、防御力、韧性……),组件的数量也跟着膨胀。
🔥 三大痛点:
- 跨组件耦合:受伤组件计算伤害时需要读取防御组件里的防御力,受伤组件必须”认识”防御组件
- 连锁反应断裂:血量归零触发死亡,死亡需要禁用攻击组件、移动组件、动画组件——在多个组件之间来回通知
- 新增成本高:每加一个新属性,就要决定”放哪个组件?要不要新建组件?已有组件要不要加引用?”
2. GAS 的解法:AttributeSet
AttributeSet = 将逻辑上互相关联的战斗属性打包在一起。
1 | AttributeSet(一本属性大字典) |
所有属性在一个池子里,互相之间天然可见——受伤时直接读防御力,死亡时直接禁移速,不需要跨组件修路。
💡 粒度选择:不是”所有属性一个大 Set”,也不是”一个属性一个 Set”,而是**”逻辑上同生共死的属性放一个 Set”**。
| Component 拆散方案 | 一个属性一个 Set | GAS 一类属性一个 Set | |
|---|---|---|---|
| 跨属性访问 | ❌ 需要跨组件引用 | ✅ 可以访问 | ✅ 直接访问 |
| 属性修改管道 | ❌ 自己手写 | ❌ 每个 Set 配一套管道 | ✅ 同 Set 共用管道 |
| 连锁反应 | ❌ 跨组件通知 | ❌ 跨 Set 通知 | ✅ 同 Set 内搞定 |
🔥 多套 AttributeSet 按需组合:
1 | 玩家角色(什么都要): |
二、 修饰器系统(Modifier)
1. 问题引出:直接改属性值的死结
方案一:Buff 直接修改属性值。例如”力量药水:攻击力 ×1.2”。
1 | 基础攻击力 = 100 |
🚨 多个 Buff 叠加后,原始值和每个 Buff 的贡献被”拍扁”成一个数字 230,到期时无法精确还原。
就像一张纸折了三次,你试图只展开第一折,但纸已经被后两折压死了。
2. GAS 的解法:修饰器 = 便签条
Buff 不直接改属性值,而是在属性上”贴一张便签”。贴上生效,撕掉失效。基础值永不改变。
1 | 攻击力 = 100 (基础值,永远不变) |
🔥 核心价值:每层 Buff 独立存在,撕掉哪张只影响那一张,其他便签照旧,无需手动恢复。
3. 运算类型
每张便签由策划决定运算方式:
| 运算类型 | 含义 | 战斗场景举例 |
|---|---|---|
| 加算 (Add) | 固定值增减 | 装备 +50 攻击力、Buff +200 生命 |
| 乘算 (Multiply) | 百分比缩放 | 力量药水 攻击力×1.2、虚弱 攻击力×0.7 |
| 覆盖 (Override) | 直接设置新值 | 无敌状态下防御力 = 99999 |
| 除算 (Divide) | 除以系数 | 减速 移速/2、破甲 防御力/5 |
4. 执行顺序(管道)
GAS 的管道顺序是硬编码的,不能调整:
1 | ① 先检查覆盖便签(Override) |
💡 如果需要”先乘再加”的效果:拆成两个属性,一个只受乘算影响(如基础攻击力),一个只受加算影响(如加成攻击力),最终求和。
1 | 最终攻击力 = 基础攻击力(乘算层) + 加成攻击力(加算层) |
三、 属性修改三段式管道
1. 为什么不能”改值的同时处理连锁反应”
如果像普通 Component 那样在 TakeDamage 里同时做扣血、死亡判定、通知UI、清护盾:
- 函数越来越臃肿
- 后续策划需求(死亡演出期间继续扣血统计、临终诅咒自动释放)找不到合适的位置插入
2. GAS 的三段管道
1 | PreAttributeChange() ← 🚪 门卫:改之前检查能不能进 |
🎯 各司其职,互不污染。
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 | 火焰刀 GE: |
5. GE 与三段管道的对应关系
1 | 施加 GE |
五、 GameplayAbility(技能)
1. GA 的定位:技能的老板
GAS 三块核心拼图的关系:
1 | AttributeSet = 属性的字典(数据在哪) |
GA 自己不管数据,也不直接改属性——但它决定一切。
2. GA 的五项职责
| 职责 | 对应概念 | 说明 |
|---|---|---|
| 谁能用 | 赋予(Grant) | 角色学了/装备了这个技能才能使用 |
| 什么时候能放 | 触发条件 + 冷却 + 消耗检查 | 按键触发 / 被动触发 / 事件触发;检查冷却、法力、沉默状态 |
| 对谁放 | 目标选择(TargetData) | 自己 / 敌人 / 范围 / 地面点 |
| 放了有什么效果 | 施加 GE | GA 的核心动作就是施加一个或多个 GE 到目标 |
| 放完之后干什么 | 冷却启动、连招窗口 | 给自己挂冷却 GE;可能开启下一段连招窗口 |
3. GA 完整生命周期
1 | 【赋予】技能给了这个角色(学技能/装备技能) |
4. 冷却机制:GA 检测 + GE 倒计时
| 角色 | 职责 |
|---|---|
| GA(老板) | 检测冷却状态 + 决定是否放技能 + 放完后启动冷却 |
| 冷却 GE(便签) | 挂 N 秒自动失效——只负责”存在/不存在” |
1 | 按技能键 → GA 查看"冷却 GE 还在吗?" |
GA 是老板,GE 是便签。老板看到便签还在,就不干活。老板干完活,贴一张新便签。
六、 GameplayTag(游戏标签)
1. GameplayTag 是什么
GameplayTag = 战斗系统的通用分类语言。
它是贴在游戏对象上的分层标签,用于替代硬编码的类型检查(如 Cast、if 字符串判断)。所有战斗规则都可以用 Tag 的包含/排除关系来表达。
2. 分层结构
1 | Damage ← 父标签 |
🔥 检测父标签时,子标签全部命中。 比如检测 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 | 火焰刀 GA ──施加──→ 伤害 GE |
七、 GameplayCue(战斗表现)
1. 核心拆分:GE 管数据,Cue 管表现
GE 只负责数据层——改了多少属性、贴了什么便签。它对视觉表现一无所知。
🔥 同一个 GE、同一个伤害数据,不同目标的表现完全不同:
| 谁受到了同样的”火焰伤害” | 应该怎么表现 |
|---|---|
| 普通敌人 | 火花粒子 + 后仰动画 + 音效 |
| Boss | 火花粒子(缩小版)+ 毫不动摇 |
| 玩家自己 | 屏幕边缘闪红 + 手柄震动 |
| 可破坏木桶 | 碎裂特效 + 木屑飞溅 |
如果 GE 自己决定表现,它就必须知道目标是玩家还是 Boss 还是木桶——破坏了 GE 的纯粹性,又回到了”必须认识目标类型”的老问题。
GAS 的拆分:
| GE(GameplayEffect) | GameplayCue(游戏表现提示) | |
|---|---|---|
| 管什么 | 属性怎么改(数据层) | 伤害长什么样(表现层) |
| 比喻 | 银行转账记录 | 收到转账的短信铃声 |
| 触发方式 | 施加 GE 时自动触发 | 通过 GE 上带的 Tag 自动匹配到 Cue |
| 谁决定表现 | 不决定 | 每个目标自己决定自己的表现方式 |
2. 触发流程
1 | 施加伤害 GE(Tag: Damage.Fire) |
🔥 同一个 Tag,每个目标有自己的”反应剧本”。GE 只发信号:”火焰伤害发生了!”——至于怎么表演,每个角色自己决定。
3. 触发时机:Cue 在数据确认改变后才触发
1 | 施加伤害 GE |
🚨 如果先播 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 | GameplayCueManager(翻译表) |
🔥 Cue 用的 Tag 必须以 GameplayCue. 开头——这是 GAS 的硬性约定。GE 上带 Damage.Fire,GAS 自动在前面拼上 GameplayCue. → 变成 GameplayCue.Damage.Fire → GameplayCueManager 查表 → 找到对应的特效/音效。
八、 AbilityTask(技能时间线控制)
1. 问题引出:技能内部的时间线谁管?
以”蓄力重斩”为例——按下按键蓄力,按住 2 秒后挥出,中途松手则取消。
技能涉及多个时间节点:
1 | 时间轴: 0s 0.5s 1.5s 2.0s |
🔥 三大角色各管一摊:
| 角色 | 管什么 | 蓄力重斩中干了什么 |
|---|---|---|
| Enhanced Input | 玩家的手指 | Started → 激活 GA;松手 → 信号传给 AbilityTask |
| GA(老板) | 技能的启动和收尾 | 激活 → 启动 AbilityTask → 收到结果后决定砍人还是取消 → 启动冷却 |
| AbilityTask(导演) | 技能内部的时间线 | 每帧推进蓄力进度 → 触发阶段 Cue → 100% 或 Canceled 后通知 GA |
2. AbilityTask 的本质
AbilityTask = 技能内部的导演。 它不关心输入从哪来,只关心”现在该干什么、下一步是什么、什么时候结束”。
每个 AbilityTask 都有两种结局:
1 | AbilityTask 运行 |
3. 蓄力重斩完整分工
1 | GA 激活(按键触发) |
4. 与蒙太奇 Notify 的关系
蒙太奇的 Notify 窗口也管时间线上”窗口开在哪”,但它和 AbilityTask 职责不同:
1 | 第一刀 GA 激活 → 播放攻击蒙太奇 |
蒙太奇管”窗口开在动画的哪一帧”,AbilityTask 管”窗口内等什么、超时怎么办”。
5. 常用 AbilityTask 速查
| AbilityTask | 等什么 | 典型场景 |
|---|---|---|
| WaitDelay | 纯等 N 秒 | 蓄力计时、技能前摇、延时爆炸 |
| WaitInputPress | 等玩家按某个键 | 连招窗口、蓄力松手、提前引爆 |
| WaitTargetData | 等玩家选目标 | 指向性技能、AOE 范围选择 |
| WaitGameplayEvent | 等某个 Tag 事件发生 | 被动触发、受击反击、队友信号 |
| PlayMontageAndWait | 等动画播完 | 技能动画播完再进入冷却 |
| WaitOverlap | 等碰撞体碰到东西 | 刀光命中敌人、陷阱检测 |
九、 GameplayEvent(事件驱动技能)
1. 问题引出:不按按键的技能怎么触发?
很多战斗技能不是玩家”按”出来的:
| 技能 | 触发条件 |
|---|---|
| 荆棘光环 | 被攻击时自动反伤 |
| 底力(低保护盾) | 血量低于 20% 时自动获得护盾 |
| 终结技解锁 | 目标血量低于 10% 时图标亮起 |
| 复仇(队友死亡) | 队友死亡后攻击力提升 |
这些技能等的是一个**”世界通知”**,而不是玩家按键。
2. GameplayEvent 的本质
1 | 主动技能: |
🔥 GAS 用 Tag 字符串当信号,替代了事件分发器对象。
3. 完整链路
1 | PostExecute 检测血量 < 20% |
4. 被动技能的运作模式
被动技能 = 角色出生时自动激活的 GA,激活后立即启动 WaitGameplayEvent 卡住等待。
1 | 角色 BeginPlay |
被动技能 = 一个激活后就卡在 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 | 📡 信号 | 不按键也能放的技能 |
后续课程将继续深入网络同步(策划视角)、数据驱动配置等内容。



