状态生命周期
从施加第一层到主动移除或自然过期,理解状态层数、行为入口、Hook 与事件的完整运行过程
状态进入单位后会形成独立的实时层数,并在施加、修改、回合衰减、主动移除或自然过期时触发对应 Behavior 与事件,Game Card Monitor 的 Unit 标签页可以直接核对每个单位此刻持有的状态

上图把玩家与敌人的 HP、护甲和状态层数放在同一份运行快照中,状态定义决定叠加与衰减规则,单位状态则记录当前层数和施加者,两者不能混为同一层数据
状态定义与实时层数
GameStatus 是创作定义,保存名称、图标、叠层方式、层数上限、衰减规则与 Behavior,战斗中,每个 UnitState 独立记录自己持有的状态、当前层数和已知施加者
修改某个单位的状态不会影响其他单位,也不会回写 GameStatus 资产,字段配置和完整创作流程见状态创作
| 需求 | API |
|---|---|
| 读取当前层数 | GCSApi.GetStatusStacks(unit, status) |
| 检查是否至少有 N 层 | GCSApi.HasStatus(unit, status, minStacks) |
| 读取最近一次成功施加者 | GCSApi.GetStatusApplier(unit, status) |
| 读取单位的全部状态 | GCSApi.StatusesOn(unit) |
单位为 null 或没有状态时,这些方法会返回 0、false、null 或空映射,不需要调用方额外创建占位集合
施加状态
使用 GCSApi.ApplyStatus(target, status, stacks, source) 或 FlowGraph 的 Change Status 节点施加正数层数,运行时按以下顺序处理:
- 检查目标、状态和传入层数
- 让来源单位的
OnInflictStatusHook 修改或取消层数 - 让目标单位的
OnReceiveStatusHook 继续修改或取消层数 - 按
Additive、Max或Replace与现有层数合并 MaxStacks > 0时限制最终层数;0和负数均不限制,其中0会触发编辑器警告- 层数发生变化时保存结果与最近一次施加者,并发布状态变化事件
- 仅在最终层数实际变化后执行施加方与接收方的状态 Behavior 入口
source 为空时,运行时把状态持有者记录为施加者;后续每次让已存储层数发生变化的成功施加都会用本次来源或持有者替换记录,Max 规则或 MaxStacks 上限造成的无变化结果、ModifyStatusStacks 与 RemoveStatus 都不会改写它;无变化结果也不会发布虚假的 OnStatusInflicted,或执行 On Inflicted、On Received 与 On Stacks Changed Entry
修改、移除与重新触发
| 任务 | 推荐入口 |
|---|---|
| 增加状态层数 | ApplyStatus() 或 Change Status 的正数 Delta |
| 减少指定层数 | RemoveStatus() 或 Change Status 的负数 Delta |
| 把层数调整到目标值 | ModifyStatusStacks() 或 Change Status 的 Set 模式 |
| 不改层数,重新执行状态行为 | Reapply Status 或 Controller 的 RetriggerStatus |
- 状态不存在时,Set 到正数会执行完整施加流程,包括 Hook、叠层规则、施加者记录、事件与
OnApplied;已有状态使用 Set 时只精确调整层数,不会重复施加流程或OnApplied - 主动把层数降至
0会进入 Removed 生命周期,PerTurn自然衰减到0会进入 Expired 生命周期,两者可以播放不同表现或触发不同规则
生命周期入口与 Hook
状态 Behavior 包含两类入口
| 类型 | 执行时机 | 典型用途 |
|---|---|---|
| 生命周期 Entry | 某项变化已经发生后 | 状态被施加、回合开始、回合结束、状态移除 |
| Hook | 数值提交之前 | 修改伤害、费用、抽牌数、护甲或状态层数 |
- 生命周期 Entry 使用普通 Action 节点完成后续效果,Hook 必须用
Write Hook提交修改后的值 - 在没有写回时,运行时继续使用原值
- Status 与 Card 注册的 Hook 会按同一
Priority排序,数字越小越先执行,同值保持注册顺序 - Card Hook 使用
Scope = Turns时按未来玩家回合开始计时,不会在中间的敌方阶段提前消失,例如 Aegis 的Turns = 1会覆盖随后的敌方阶段,并在下一个玩家回合开始时到期 - Hook 控制必须同步完成,直接或经嵌套操作触达的
Wait、Choice与Delayed Trigger都不能提供延迟返回的 Hook 结果
状态入口和 Hook 的可用范围见Entry 节点与Hook 节点
Status Behavior 中,Host 是状态持有者,Source 是最近一次成功施加者,未知时回退到持有者,Attacker 则保留伤害或反应上下文提供的实际攻击者;三者彼此独立,因此持有者可以响应当前攻击者,同时继续读取该状态记录的来源
回合时机与自动衰减
上图展示了 PerTurn 状态的默认生命周期,玩家状态在玩家回合结束时衰减,敌人状态则在该敌人的行动和回合结束行为完成后衰减
| 时机 | 状态可能执行的内容 |
|---|---|
| Battle Start | 战斗开始行为 |
| Player Turn Start | 玩家状态的回合开始行为 |
| Draw / Discard | 抽牌或弃牌阶段行为 |
| Player Turn End | 玩家状态的回合结束行为、Tick 与衰减 |
| Enemy Turn | 当前敌人的回合开始、行动、回合结束、Tick 与衰减 |
| Battle End | 所有单位的战斗结束行为 |
精确阶段顺序见战斗生命周期
同一持有者的多个状态收到相同时机时,GCS 按捕获的状态顺序执行;其中一个分支进入 Wait 或 Choice 后,后续状态分支会等它完成,捕获序列全部排空后才发出一次时机完成通知
状态变化事件
| 事件 | 适用场景 |
|---|---|
OnStatusChanged | 层数变化后重建状态列表 |
OnStatusTicked | 播放一次状态结算造成的 HP 或层数变化 |
OnStatusInflicted | 响应一次成功施加 |
OnStatusExpired | 播放自然到期反馈 |
OnStatusRemoved | 播放净化或主动移除反馈 |
OnStatusChanged是状态 UI 的主要刷新信号,Transition 事件适合动画、音效、统计或其他一次性处理,不应替代当前状态查询- 公共
OnDamageDealt、OnDamageTaken事件与对应伤害 Entry 的Amount都是目标实际损失的 HP,已经经过 Hook、护甲与剩余 HP 边界处理,适合用于飘字、战斗日志和伤害后规则 - 状态层数或 Card 注册的活动 Hook 发生变化时,GCS 会立即重新解析敌人公开意图并发布
OnEnemyUnitIntentChanged,意图 UI 应响应事件后读取新的PendingIntents
状态 UI 的刷新方式
状态 Widget 收到 OnStatusChanged 后,应使用 GCSApi.StatusesOn(unit) 重新读取该单位的完整状态映射,再根据 Icon、DisplayName、IsDebuff、ShowStackCount 和当前层数更新界面
不要只修改本地缓存中的一项层数,一次 FlowGraph 可能同时改变多个状态,事件参数只说明哪项变化触发了通知
常见错误
| 错误 | 正确做法 |
|---|---|
直接编辑 UnitState.Statuses | 使用状态 API、Controller 或 FlowGraph 节点 |
| 用生命周期 Entry 修改尚未提交的数值 | 使用对应 Hook |
期待已有状态的 Set 再次触发 OnApplied | 使用正数 Delta 或 ApplyStatus 表达重新施加 |
| 把 Removed 和 Expired 当作同一事件 | 根据主动移除和自然到期分别处理 |
| 仅使用事件参数维护本地状态列表 | 事件后重新读取 StatusesOn(unit) |