总览
先用一句话解释:StateTree 是“状态机 + 行为树的一部分思路 + 数据绑定”的组合。它让你把一个对象的行为拆成状态,例如 Idle、Patrol、Chase;每个状态里放要执行的 Task;用 Condition 判断能不能进状态;用 Transition 决定什么时候离开当前状态。
如果你做过动画状态机,StateTree 的 State/Transition 会很熟;如果你做过 Behavior Tree,StateTree 的 Task/Condition 会很熟;如果你做过蓝图逻辑,StateTree 会把原来散在 Tick、Timer、Branch、DoOnce 里的流程收进一张可调试的资产。
先看守卫案例
不要从类名开始。想象一个守卫:
- 站在岗位上等待 2 秒。
- 去第一个巡逻点。
- 到达后等 2 秒。
- 去下一个巡逻点。
- 如果看到玩家,停止巡逻,进入警戒。
- 警戒 0.5 秒后,如果还看到玩家,开始追击。
- 如果玩家跑远,去最后看到的位置搜索。
- 搜索失败,回岗位。
这就是 StateTree 很适合的逻辑:它有明确状态,每个状态做有限的事情,状态之间有清楚条件。
它和其他工具怎么选
| 工具 | 更适合 | 不适合 |
|---|---|---|
| Blueprint Branch/Tick | 很小的临时逻辑 | 多状态、多跳转、要调试的 AI |
| Anim State Machine | 动画姿态切换 | AI 行为、MoveTo、EQS、GameplayTask |
| Behavior Tree | 传统 AI 决策,已有黑板体系 | 状态层级很强、想把局部流程封装成资产 |
| StateTree | 分层状态、任务生命周期、可绑定数据 | 完全无状态的算法、复杂感知系统本身 |
初学者可以这样记:Behavior Tree 像“每帧从上到下找现在该干什么”,StateTree 更像“我现在就在这个状态里,直到任务完成或跳转条件满足”。
编辑器里你会看到什么
创建 StateTree 资产后,核心区域通常有三块:左侧是状态树结构,中间是当前状态的 Task/Transition/Condition,右侧是 Details 面板。先不要急着看所有属性,第一天只认这几件:
- State:左侧树里的每一行,例如 Idle。
- Task:选中状态后添加的动作,例如 Move To、Wait、自定义任务。
- Transition:状态底部的跳转规则,例如 On State Completed -> Next State。
- Condition:进入状态或触发跳转时要判断的条件,例如距离小于 1000。
- Parameters:整棵树可配置的输入,例如巡逻半径。
源码依据
源码里 EStateTreeStateType 包含普通 State、Group、Linked、LinkedAsset、Subtree。EStateTreeStateSelectionBehavior 定义 Try Enter、按顺序选子状态、随机选子状态、按 Utility 选择等行为。EStateTreeTransitionTrigger 支持 OnStateCompleted、OnStateSucceeded、OnStateFailed、OnTick、OnEvent、OnDelegate。这些枚举基本就是编辑器里你会点到的概念。
架构分析
StateTree 在架构上适合放在“系统之间的编排层”。守卫能不能看见玩家,是感知系统给出的事实;怎么移动到目标,是导航和 GameplayTask 做的事;播放什么动画,是动画系统做的事;StateTree 决定的是这些系统按什么顺序组合,以及什么条件下切换组合。
使用案例
守卫最初只需要三个状态:
| State | Task | Transition |
|---|---|---|
| Idle | Wait 2s | On Succeeded -> Patrol |
| Patrol | Move To PatrolPoint | On Succeeded -> Idle |
| Alert | Wait 0.5s + Face Player | On Succeeded -> Chase |
这张表就是你动手前的蓝图。后面所有教程都只是在这张表上加数据和边界。
项目落地
第一次用 StateTree,不要把完整 AI 一口气搬进去。先找一个局部流程,比如“巡逻和追击”或“交互物使用流程”。如果这个局部流程能稳定运行,再把更多状态接进来。这样遇到问题时,你知道是 StateTree 本身没跑,还是某个业务条件写错。
常见坑
不要把 StateTree 叫成“另一个 Behavior Tree”。它们能配合,但心智模型不同。不要把 Perception、动画、伤害、背包都塞进 StateTree,StateTree 负责调度这些系统,不应该成为这些系统本身。不要先写 C++ Task,再想它要解决什么问题;先写状态表。
源码路径索引
StateTreeModule/Public/StateTree.hStateTreeModule/Public/StateTreeTypes.hGameplayStateTreeModule/Public/Components/StateTreeComponent.hGameplayStateTreeModule/Public/BehaviorTree/Tasks/BTTask_RunStateTree.h