1545 字
8 分钟
GAS 深入——技能系统的注册、执行与预测

一、回顾:GAS 是什么#

在第 8 篇(Gameplay 框架)里简介了 GAS 的四个核心概念——ASC、Ability、Effect、Tag。这篇往下钻一层——实际做一个技能需要知道什么。

GAS 的典型技能执行流程:

玩家按下技能键
→ Input → ASC::TryActivateAbility(AbilityTag)
→ CanActivateAbility? (Cost? Cooldown? Tag Blocked?)
→ 是 → CommitAbility() → ActivateAbility()
→ 应用 GameplayEffect (Cost, Cooldown)
→ 执行技能逻辑(生成 Projectile、播放动画、造成伤害)
→ 结束时应用 GameplayEffect (Damage, Buff)

二、技能注册:如何把技能配到角色身上#

1 三种授予方式#

// 方式 1:C++ 硬编码——最简单
void AMyCharacter::BeginPlay() {
Super::BeginPlay();
if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent()) {
ASC->GiveAbility(FGameplayAbilitySpec(MyAbilityClass, 1));
// ↑ Level
}
}
// 方式 2:DataTable 批量授予——策划在表格里配
// DT_Abilities.csv:
// Character | Ability | Level | InputTag
// Sofia | GA_RevealItem | 1 | InputTag.Skill.Q
// Raven | GA_SeeAll | 3 | InputTag.Skill.Q
// 方式 3:GameplayEffect 授予——技能给技能("获得新技能"本身是一种 GE)
// 当玩家升级时,一个 GE 可以 GrantAbility

FGameplayAbilitySpec 不只是存技能类类型——它还存技能的 Level、InputTag 绑定、激活状态、冷却剩余时间等运行时状态。

2 ASC 放在哪#

放在哪适用为什么
PlayerState多人游戏(推荐)PlayerState 跨 Pawn 持久——死亡/重生 ASC 还在
Pawn/Character单人或简单场景Pawn 销毁 ASC 就没了——断线重连丢技能状态
PlayerController特殊用途Controller 换 Pawn 时技能不丢,但 AI 没有 Controller

多数项目放在 PlayerState 上——和 第 8 篇说的”PlayerState 是跨 Pawn 的个人记分卡”一脉相承。


三、GameplayEffect 的计算管线#

1 GE 的结构#

GameplayEffect
├── Duration Policy(Instant / HasDuration / Infinite)
├── Modifiers(属性修改——伤害、治疗、Buff)
│ ├── Attribute(要改哪个属性——如 Health)
│ ├── ModifierOp(Add / Multiply / Override)
│ └── Magnitude(改多少——固定值/曲线/AttributeBased/自定义计算)
├── Executions(自定义计算——比 Modifier 更灵活)
├── GameplayTags(GE 运行时给目标贴 Tag、移除 Tag)
├── Granted Abilities(这个 GE 给目标添加技能)
└── Stacking(叠层规则——如"中毒"能不能叠 5 层)

2 Instant vs Duration#

// Instant:立刻生效一次
// 如:GA_Fireball 中的 DamageEffect
// 应用 → 扣 50 血 → 效果结束(GE 被销毁)
// HasDuration:持续一段时间
// 如:GA_PoisonBlade 中的 PoisonEffect
// 应用 → 每 1 秒扣 5 血 → 持续 10 秒 → 效果结束
// Duration 期间,GE 一直"挂"在目标身上
// Infinite:永久有效,直到手动移除
// 如:被动技能"剑圣——基础攻击力 +20%"
// 应用 → 永不被移除 → 直到剑圣死亡或技能被清

3 Modifier 的计算顺序#

当 Health 需要计算当前值时:

1. 找到所有影响 Health 的 GE(Active Effects)
2. 按优先级排序
3. 从 BaseValue 开始
4. 累加所有 Add 类型的 Modifier
5. 乘以 Multiply 类型的 Modifier
6. 应用 Override(如果有,直接覆盖前面的结果)

这就是为什么 GE 体系比手写属性逻辑强——你不管理更新顺序、不管理 GE 的生效/失效通知。ASC 自动追踪所有 Active GE,任何 GE 变化时自动重算属性。


四、Tag 的阻挡与免疫#

1 Ability 的 Tag 约束#

// GA_Stun(眩晕技能)
AbilityTags: Ability.CC.Stun // 这个技能是什么
CancelAbilitiesWithTag: Ability.Movement // 激活时取消移动技能
BlockAbilitiesWithTag: Ability.Active // 激活时阻挡所有主动技能
ActivationBlockedTags: Status.Dead // 死亡时无法激活
ActivationRequiredTags: Status.Alive // 必须存活才能激活

Tag 之间的关系形成了技能系统的”交通规则”。 眩晕期间不能移动、死亡期间不能放技能——不用在 GameMode 里写 if (IsDead) return;,全由 Tag 规则来表达。

2 免疫#

// 法师的 魔法护盾 GE
GrantedTags: Status.ImmuneToStun // 有这个 Tag,眩晕技能无法激活
// GA_Stun 的配置
ActivationBlockedTags: Status.ImmuneToStun // 如果目标有这个 Tag,技能灰掉

策划在技能表里配 Tag 规则就行——不需要程序员加 if (HasMagicShield)


五、客户端预测#

GAS 在多人场景下支持技能预测——和第 7 篇(网络模型)的客户端预测是同样的思路:

1. 客户端按下技能键
2. 客户端 ASC 本地执行 CanActivate(Cost 检测、Tag Block、Cooldown)
→ 如果本地判断可以放,立刻播放技能动画(Prediction)
3. 同时发送 ServerTryActivateAbility RPC
4. 服务端重新执行 CanActivate(防作弊)
→ 如果通过 → 权威执行技能
→ 如果不通过 → 发回 PredictionRejected → 客户端回滚

预测键(Prediction Key):每次预测激活有一个唯一的 Key。服务端回传确认时带上这个 Key——“Key=42 的技能批准了”或”Key=42 被拒绝了”。客户端根据 Key 匹配自己发出的预测。


六、一个完整技能:RPG 里的”法师火球”#

GA_Fireball(法师——向目标区域释放火球)
[Ability Definition]
AbilityTags: Ability.Mage.Fireball
ActivationBlockedTags: Status.Dead ← 死亡时不能用
ActivationRequiredTags: Status.Weapon.Equipped ← 必须装备法杖
[Activation Logic]
1. CommitAbility() → 消耗 Cooldown GE
2. 服务端:验证目标区域和施法者状态
3. 客户端:播放施法动画,生成预测的火球特效
4. 服务端:计算命中结果 → 对范围内敌人应用伤害 GE
[Cooldown]
一个 GameplayEffect (Duration=5s, 结束后移除冷却 Tag)

这个技能横跨了 Tag、GE、RPC 三个 GAS 子系统——但每个子系统只做自己的事。 这就是 GAS 的设计哲学:把复杂的技能逻辑拆成可组合的小块,而不是在代码里手写长 if-else


七、总结#

概念一句话
GiveAbility把技能注册到 ASC——可以代码给、DataTable 给、GE 给
ModifierGE 里声明”改什么属性、怎么改”——BaseValue → Add → Multiply
ExecutionGE 里自定义计算——比 Modifier 更灵活(可访问多个属性、做复杂判断)
Tag 阻挡用 Tag 声明技能的前置条件/互斥关系——不需要写 if
Prediction客户端本地预执行 → 服务端验证 → Rejected 就回滚
GAS 的核心哲学技能规则化(Tag + GE)而非代码化(if-else

GAS 学习曲线陡峭——但一旦理解了”ASCD能力注册 + GE状态变化 + Tag阻挡规则”这三件事,你就能把大部分 Gameplay 需求表达为”给这个角色配什么技能”而不是”在这个类的第 300 行加一个 else if”。

GAS 深入——技能系统的注册、执行与预测
https://www.m4doka.xyz/posts/ue/ue-1-gas-deep-dive/
作者
m4doka
发布于
2026-08-02
许可协议
CC BY-NC-SA 4.0