总览
StateTree 让人头疼的第一类问题是“不跳”,第二类是“跳错”。这篇专门讲 Transition。你要先知道:Transition 不是随便触发的,它有触发类型、条件、目标、优先级,还受到状态层级影响。多个 Transition 同时满足时,StateTree 会按规则选一个。
触发类型先分清
| Trigger | 什么时候检查 | 守卫例子 |
|---|---|---|
| On Tick | 每次树 Tick 时 | 巡逻中看到玩家 -> Alert |
| On State Succeeded | 当前状态成功完成 | Wait 结束 -> Patrol |
| On State Failed | 当前状态失败 | Move To 失败 -> Search |
| On Event | 收到事件 | 听到声音 -> Investigate |
| On Delegate | 某个委托广播 | Montage 结束 -> Next |
新手建议:能用 OnStateSucceeded 就不要用 OnTick。OnTick 每帧检查,适合紧急打断;状态正常流程用完成触发更清楚。
优先级怎么用
EStateTreeTransitionPriority 有 Low、Normal、Medium、High、Critical。守卫案例里,发现玩家应该比巡逻完成更重要。否则刚好 Move To 成功那帧也看到玩家,树可能先走普通巡逻流程。
推荐优先级:
| 跳转 | 优先级 |
|---|---|
| 死亡、硬打断、被控制 | Critical |
| 发现玩家、受到攻击 | High |
| 追击丢失目标 | Medium |
| 巡逻点到达、等待结束 | Normal |
| 装饰性随机切换 | Low |
父子状态的影响
Transition 可以放在父状态,也可以放在子状态。父状态的 Transition 适合“所有子状态都适用”的规则,例如 Combat 下任何子状态血量为 0 都去 Dead。子状态 Transition 适合局部流程,例如 PatrolA 到 PatrolB。
如果一个规则在十个子状态都复制了一遍,优先考虑放父状态。复制越多,后期越容易漏改。
源码依据
EStateTreeTransitionTrigger 定义 OnTick、OnStateSucceeded、OnStateFailed、OnEvent、OnDelegate。EStateTreeTransitionPriority 定义 Low、Normal、Medium、High、Critical,并有比较运算。EStateTreeStateSelectionRules 定义完成状态如何重新创建、完成状态在 Transition source 前如何处理。FStateTreeExecutionContext 的 Tick 会更新任务并触发 Transition,源码还提供 Trace/Debugger 相关入口。
架构分析
Transition 是状态之间的合同。架构上建议把“全局打断”放在父状态或更高层,例如看到玩家打断巡逻;把“局部完成”放在当前状态,例如 MoveTo 成功后 NextState。这样你能从树结构上看出跳转范围,不会在每个叶子状态里复制同一批高优先级条件。
使用案例
守卫追击流程:
| 当前状态 | Transition | 条件 | 优先级 |
|---|---|---|---|
| Patrol | Goto Alert | CanSeePlayer | High |
| Alert | Goto Chase | CanSeePlayer | Normal |
| Alert | Goto Patrol | !CanSeePlayer | Normal |
| Chase | Goto Search | DistanceToPlayer > LoseSightRange | Medium |
| Chase | Goto Combat | DistanceToPlayer < AttackRange | High |
| Any Combat Child | Goto Dead | Health <= 0 | Critical |
调试时先问:这个跳转有没有被检查?条件值是什么?是否被更高优先级跳转抢走?目标状态是否可进入?
项目落地
给每个复杂 Transition 写描述,不要只靠节点名字。比如“看见玩家时从任何巡逻子状态进入 Alert,High 优先级”。调试记录里保留当前 Active State、上一次 Transition、失败 Condition 的值。对 AI 来说,能复盘“为什么它刚才没追”比能看到一堆节点更重要。
常见坑
不要把所有跳转都设成 Critical。优先级失去差异后就没有意义。不要用 OnTick 承担正常流程,它会让树每帧都处于高压判断。不要忘记目标状态也有 Enter Conditions,Transition 条件通过不代表目标状态能选中。不要在父状态和子状态写互相冲突的跳转。
源码路径索引
StateTreeModule/Public/StateTreeTypes.hStateTreeModule/Public/StateTreeExecutionTypes.hStateTreeModule/Public/Debugger/StateTreeTrace.hStateTreeModule/Public/Debugger/StateTreeDebugger.h