能力组合:GA 是动作事务,不是交互物基类
自动关联目录:能力组合:GA 是动作事务,不是交互物基类
核心判断:Gameplay Ability 适合表达角色发起的、有激活条件、消耗、冷却、取消和预测窗口的动作事务;交互物的空间发现、长期状态、占用、持久化和大规模索引应由组件与服务负责。把场景里每个交互物都做成 GA,只是把 Actor 膨胀换成 ASC 与 Ability Spec 膨胀。
一、业务冲突:玩法需要统一能力,又不能让统一框架吞掉所有状态
战斗、武器、移动技能和部分交互都具有相似流程:输入、检查条件、扣除资源、播放动作、命中、取消、冷却、DS 结算和客户端反馈。GA 能复用这些机制。但宝箱是否开启、座位被谁占用、任务节点进度、可拾取物空间索引和跨世界持久数据,并不是一次 Ability 的生命周期。
业务真正需要的是“能力组合”,不是“所有行为继承同一个重基类”。
二、实际实现:正确的复用与边界泄漏同时存在
现有实践中,主动/被动能力通过 ASC 授予,并保存 AbilitySpecHandle;属性变化用 Gameplay Effect Handle 管理。装备实例保留来源物品身份,装备时由 Ability Set 一次授予能力,卸装时用已记录 Handles 成对撤销,并只允许 Authority 修改。这是 GA 与领域组件协作的正确例子。
另一类跟随能力由组件维护领域关系,再按状态动态 grant/clear 若干 Ability;句柄避免了重复授予,但可用性仍在服务器 Tick 中持续查询。自定义 Cost 已能读取体力或物品耐久,不过某些异常来源走了 fail-open,说明“接入了 Cost 扩展”不等于权威资源一定安全。
交互框架本身仍使用轻量组件和服务管理范围、优先级、对象状态与互斥,而没有给每个场景物件挂 ASC。这一选择应被明确成架构规则。
flowchart LR
I[输入/AI 意图] --> S[交互与目标服务]
S --> P{动作是否需要<br/>预测/消耗/冷却/取消?}
P -->|是| A[角色 ASC 上的 GA]
P -->|否| C[轻量命令处理器]
A --> D[领域组件/共享服务提交状态]
C --> D
D --> N[权威快照/事件]
N --> V[动画 UI 音频 特效]三、值得保留:Handle 成对管理把授予变成可撤销资源
装备条目记录来源实例和 Granted Handles,使“为什么拥有此能力”可追溯,卸装能精确撤销,不必按 Class 猜测。Authority Guard、Fast Array 脏标记、Gameplay Effect 和 Gameplay Cue 也分别承担权威修改、增量状态与表现反馈,职责较清楚。
Cost、Cooldown、Gameplay Tag、Ability Task 和 Prediction Key 是 GAS 的真正价值。它们特别适合角色中心、短生命周期、需要被打断或预测的动作,而不是用来替代库存、目标注册表和世界对象状态机。
四、结构性缺陷:动态 grant 可能变成高频状态同步手段
若组件每帧扫描附近目标,再通过 grant/clear 表达按钮是否可用,会产生 Spec 变更、输入槽更新、复制和实例生命周期成本。更合适的是稳定授予少量动作 Ability,用 Tag/条件查询决定能否激活,并由事件更新候选目标。
把 GA 放到每个交互 Actor 上还有 UE 所有权问题:客户端通常不拥有场景 Actor,无法沿标准 Owned Actor RPC/预测链发起能力;为每物体创建 ASC 又会增加属性聚合、Tag 容器、Spec 和复制负担。自定义 Cost 若缺数据仍返回成功,则会把错误配置变成免费施放,权威扣费必须 fail-closed。
五、目标设计:以动作复杂度决定是否进入 GAS
世界服务先产生已验证 Target Handle;角色上的稳定 Ability 接收 TargetData,使用 GAS 处理 Commit、Cost、Cooldown、Cancel 和预测,真正的库存、装备、目标或任务状态仍提交给领域服务。纯开关、一次性领取等无预测需求的动作可直接走轻量 DS Command Handler。
ExecutionPath Choose(ActionDefinition a)
{
if (a.needsPrediction || a.hasCost || a.hasCooldown ||
a.isInterruptible || a.composesEffects)
return CharacterAbility;
return LightweightCommand;
}
AbilityResult CommitAbility(ActorInfo owner, TargetHandle target)
{
if (!target.IsCurrent() || !CheckCost(owner) || !CheckTags(owner))
return FailClosed;
ScopedPrediction prediction(owner);
DomainCommit result = Services.Execute(owner.id, target);
return result.ok ? CommitCostAndCooldown() : RollbackPrediction();
}无状态动作优先 Non-Instanced;确需保存每角色执行状态时用 Instanced Per Actor;只有执行间必须并发隔离时才承担 Per Execution 成本。
六、决策规则:抽象边界按所有权与生命周期划分
| 能力或状态 | 首选承载 | 原因 |
|---|---|---|
| 消耗、冷却、Buff、可取消技能 | GA/GE/Tag | GAS 已提供权威、预测与组合语义 |
| 装备授予的一组动作 | Equipment Instance + Ability Set | 来源可追溯,Handles 可成对撤销 |
| 空间发现与目标排序 | World/Player Interaction Service | 与 Ability 实例无关,需规模化查询 |
| 对象占用与持久状态 | DS Domain Component/Service | 生命周期长于一次 Ability |
| 简单幂等开关 | Typed Command Handler | 无需承担 ASC/Spec 成本 |
| 视听反馈 | Gameplay Cue 或 Presentation Event | 不成为权威状态源 |
GA 是好工具,但“场景里全是 GA”不是统一,而是边界缺失。更成熟的问题不是能否用 GA 实现,而是这段状态是否属于角色动作事务;只有答案为是,GAS 的复杂度才有回报。