02-GAS的核心模型

2933 字
15 分钟
02-GAS的核心模型

第 2 章:GAS 的核心心智模型#

问题导入#

上一章已经看到,火球术的问题不是“缺一个更大的技能基类”,而是同一个 Cast() 同时回答了太多不同的问题。团队开始接触 GAS 后,通常会遇到另一个误区:把 AbilityEffectTag 等术语记住,却仍然不知道一段新逻辑应该放在哪里。

例如,火球术按下后要扣 30 Mana、进入 2 秒冷却、命中时造成 120 点直接伤害、给目标施加 5 秒 Burning,并且允许在抬手期间被眩晕打断。下面这些问题看起来都属于“技能”:

  • 技能什么时候开始、等待什么、何时结束;
  • 角色当前有多少 Mana;
  • 这次施法到底修改了哪些状态;
  • 眩晕和冷却如何阻断激活;
  • 动画事件和目标确认如何异步到达;
  • 哪些客户端表现可以提前播放;
  • 服务器如何确认最终结果。

1. 观察一次火球术#

维度火球术要回答的问题典型状态或信息
流程从输入到结束经过哪些阶段激活、等待、命中、取消、完成
数值状态角色和目标当前有哪些可计算数值Mana、Health、AttackPower
状态变化这次能力对世界做了什么改变扣 30 Mana、造成 120 伤害、添加 Burning
条件什么情况下允许或阻断能力Mana 足够、没有眩晕、冷却结束
异步等待需要等待哪些未来事件动画标记、目标确认、命中、超时
表现玩家和旁观者应该看到什么动画、投射物、特效、音效、UI
权威谁能把意图变成最终事实服务器验证并结算

1.1 流程#

流程描述的是时间顺序和生命周期:

输入
-> 检查条件
-> 提交资源和冷却
-> 等待目标或动画事件
-> 生成投射物
-> 命中并应用变化
-> 等待收招
-> 结束或取消

流程需要知道自己是否激活、正在等待、被取消或已经完成,还需要在结束时清理自己创建的运行时对象。

1.2 数值状态#

数值状态描述可以参与计算、查询、复制或保存的量:

施法者 Mana = 80
目标 Health = 500
施法者 AttackPower = 100

火球术的 Mana Cost = 30 是配置,施法者当前 Mana = 80 是运行时属性。前者不会因为某个角色受伤而改变,后者会被回复、装备、Buff 和服务器校正影响。

把数值集中到统一的属性上下文后,能力只需要请求“应用一个消耗变化”,不必知道 Mana 存在字段、网络变量还是某个组件内部。这种解耦也让伤害、治疗和资源回复可以共享同一套聚合规则。

1.3 状态变化#

状态变化关注一次操作的差值和生命周期:

立即变化:
Mana -= 30
Target.Health -= 120
持续变化:
Target 获得 Burning
Burning 持续 5 秒
每 1 秒触发一次周期结算

1.4 条件#

条件的规则:

Required:
Resource.Mana >= 30
Blocked:
State.Dead
State.Stunned
State.Silenced
Cooldown.Fireball

条件至少要区分三种时机:

  1. 激活前检查:当前是否允许开始;
  2. 流程中检查:等待期间是否被打断;
  3. 应用时检查:目标、资源和服务器状态是否仍然有效。

1.5 异步等待#

火球术可能需要等待动画事件来生成投射物,等待目标确认来确定方向,等待命中事件来应用伤害,还要等待超时或取消:

等待 Animation.Release
等待 Target.Confirmed
等待 Projectile.Hit
等待 Cast.Cancelled 或 Cast.Timeout

而等待对象必须知道:

  • 谁创建了它;
  • 能力取消时如何结束;
  • 事件来自本地、服务器还是网络消息;
  • 超时后是否撤销预测;
  • 结束时是否注销回调。

因此,异步等待需要自己的生命周期语义。执行工具可以是协程、Task、事件订阅或自定义调度器,但工具本身不能代替取消和清理规则。

1.6 表现#

玩家看到的是动画、火球、命中特效、灼烧火焰和 UI 冷却条,而不是 EffectInstance 的生命周期。表现层应该消费规则事件:

Fireball.CastStarted
Fireball.ProjectileSpawned
Fireball.HitConfirmed
Target.BurningStarted
Target.BurningEnded

1.7 权威#

在多人游戏中,客户端拥有输入和体验,服务器拥有最终裁决。客户端可以说:

“我在本地时间 T 按下了火球术,目标方向是 D。”

服务器需要独立验证:

“这个角色在服务器状态下确实有资格施法,目标也确实有效。”

只有服务器确认后,扣除 30 Mana、启动 2 秒冷却、应用 120 点伤害和添加 Burning 才成为最终事实。客户端可以预测部分结果,但必须能接受确认、拒绝和校正。

通用原则:网络协议传输的是意图、上下文和结果,不是让不可信的一端直接提交最终战斗事实。

2. 如何落到对象#

2.1 Ability#

在 UE GAS 中GameplayAbility 表示一个可激活的能力,负责能力流程、激活条件、提交、等待、取消和结束。

在本文的 Unity 方案中AbilityRuntime 是本文自定义的运行时对象,用来保存某次能力激活的阶段和取消状态;AbilityDefinition 保存静态配置。

Ability 应该回答:

  • 什么时候开始;
  • 要先检查什么;
  • 什么时候提交资源和冷却;
  • 要等待哪些事件;
  • 何时应用一个或多个 Effect;
  • 哪些条件会取消它;
  • 结束时清理什么。

2.2 Attribute#

在 UE GAS 中:Attribute 通常由 AttributeSet 承载和访问,表示 Health、Mana、Armor 等可量化状态。

在本文的 Unity 方案中AttributeStore 是本文自定义的规则层容器,负责基础值、修改器和当前聚合结果;它不是 Unity 的组件或内置服务。

Attribute 负责“当前是多少”,不负责“为什么在等待动画”。它可以参与:

  • Mana 是否足够的条件判断;
  • 伤害和治疗的最终计算;
  • 装备、Buff、Debuff 的修改器聚合;
  • 服务器快照和客户端显示。

属性系统可以只提供查询和应用变化的边界,具体的能力流程仍由 Ability 组织。

2.3 Effect#

在 UE GAS 中GameplayEffect 描述属性修改、持续时间、周期、授予标签等效果语义。

在本文的 Unity 方案中EffectDefinitionEffectInstance 是本文自定义的静态定义与运行时实例,用来表达即时、持续和周期性变化。

火球术至少可以拆成:

Mana Cost Effect
-> Mana 减少 30
Cooldown Effect
-> 添加 Cooldown.Fireball
-> 持续 2 秒
Direct Damage Effect
-> 目标受到 120 点直接伤害
Burning Effect
-> 添加 Effect.Burning
-> 持续 5 秒
-> 每 1 秒触发一次周期结算

2.4 Tag#

在 UE GAS 中GameplayTag 是结构化标签,用来表达状态、能力条件、阻断和冷却等可查询事实。

在本文的 Unity 方案中TagSetTagQuery 和标签路径是本文自定义的数据结构与查询规则,不等于 Unity 的字符串 API。

火球术可能使用:

State.Stunned
State.Silenced
State.Casting
Cooldown.Fireball
Effect.Burning

Tag 适合描述“是否处于某种状态”,不适合代替所有数值。State.Stunned 可以阻断能力,Mana 是否大于等于 30 仍然是 Attribute 条件。两者组合后,激活检查才完整。

2.5 Task#

在 UE GAS 中AbilityTask 为能力提供等待事件、延迟、目标确认、动画通知和取消等异步流程。

在本文的 Unity 方案中AbilityTask 可以是本文自定义的纯 C# 等待对象、事件订阅对象或调度器节点。

Task 的职责是把等待纳入能力生命周期:

Task 创建
-> 订阅事件或注册计时
-> 收到结果、取消或超时
-> 通知 AbilityRuntime
-> 注销订阅并释放自己

如果使用协程实现等待,也需要额外的取消令牌、生命周期绑定和事件注销。

2.6 Event:把外部事实送入流程#

在 UE GAS 中:Gameplay Event 可以把输入、命中、动画标记或其他上下文送入能力系统。

在本文的 Unity 方案中GameplayEvent 是本文自定义的事件消息,可能来自输入桥接、动画适配器、投射物系统或服务器结果。

Event 描述“发生了什么”,不自动决定“这件事是否有权改变最终状态”。

例如 Projectile.Hit 可以触发 Ability 申请应用伤害,但服务器仍然需要验证命中上下文。

2.7 Cue:规则到表现的边界#

在 UE GAS 中GameplayCue 用来根据 Gameplay Event 或 Effect 状态驱动动画、粒子、音效、镜头和 UI 等表现。

在本文的 Unity 方案中GameplayCue 是本文自定义的表现事件或适配接口,不是 Unity 内置的视觉系统。

Cue 应该让规则层:

Fireball.CastStarted
Fireball.HitConfirmed
Target.BurningStarted

表现层再决定是播放粒子、更新 UI,还是在服务器拒绝时撤销预测效果。Cue 不应该反过来修改 Mana 或直接确认伤害。

2.8 ASC#

在 UE GAS 中AbilitySystemComponent(ASC)把 Actor 的 Ability、Attribute、Effect、Tag、Event 和 Task 放进同一个能力系统上下文,并提供激活、取消、查询和相关运行时协调。

在本文的 Unity 方案中AbilitySystemComponent 可以作为本文自定义的协调接口;项目也可以选择 CombatRuntime 或其他名称,只要保留相同语义。

ASC 的价值是让 Ability 能在同一个 Actor 上下文中找到属性、标签、效果和事件,而不是让它直接拥有每一个具体技能的业务分支。

2.9 Server#

在 UE GAS 中:Server 不是 GAS 对象,而是多人网络中的运行角色。GAS 可以在网络模型中配合服务器权威、客户端预测和复制工作。

在本文的 Unity 方案中:Server 表示负责最终验证和结算的权威运行端。它可能由 Unity Server、Dedicated Server 或项目自己的网络宿主承载。

Server 的边界包括:

  • 验证能力归属和请求合法性;
  • 检查 Mana、冷却和阻断标签;
  • 验证目标、范围和命中上下文;
  • 提交权威 Effect;
  • 广播结果和必要的校正;
  • 处理重复请求和失序消息。

3. 完成一次火球术#

阶段流程数值状态状态变化条件异步等待表现权威
输入请求激活读取当前 Mana尚未提交是否拥有能力等待输入事件按键反馈服务器接收请求
提交进入施法Mana 至少为 30Mana -30、Cooldown 2 秒无眩晕、无冷却可等待动画标记播放抬手服务器确认提交
释放生成投射物不直接改变属性创建投射物上下文目标仍有效等待命中火球飞行服务器决定命中
命中应用效果目标 Health 参与计算直接伤害 120、添加 Burning命中规则通过等待 Effect 生命周期命中特效、灼烧服务器写入结果
结束清理能力读取最终状态Burning 仍可持续 5 秒取消或完成注销等待与回调收招、UI 更新广播确认或校正

这张表里,Ability 主要组织“流程”列,Attribute 主要承载“数值状态”列,Effect 描述“状态变化”列,Tag 和查询负责“条件”列,Task/Event 处理“异步等待”列,Cue 处理“表现”列,Server 贯穿“权威”列。ASC 则把这些运行时对象放在同一个 Actor 上下文里协调。

02-GAS的核心模型
https://blog.rouming-fei.top/posts/02-gas-core-model/
作者
废江流
发布于
2026-07-25
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
废江流
提灯寻影,灯到影灭。
分类
标签
站点统计
文章
15
分类
4
标签
18
总字数
37,248
运行时长
0
最后活动
0 天前

文章目录