2026.7.28笔记
State Tree
State Tree 的层级结构:Root → 守卫检查(Phase/Stagger/Death)→ 战斗行为(TacticalPause → StanceSwitch → AttackExecution → DefaultMovement),比 Behavior Tree 更接近状态机思维
State 与 Task 是分离的——画了树 ≠ 挂了任务,每个 State 的 Details 面板必须显式添加 Task
Transition 条件可能静默卡死整棵树——State Tree 是线性流转,前面的 Transition 返回
FALSE,后面全部不可达STT(StateTreeTask)每次重入都新建实例——实例变量无法跨循环保持状态,这是和 Behavior Tree
的关键差异State Tree 安全限制:禁止直接引用关卡 Actor,必须用 GetAllActorsOfClass 或 EQS 绕行
数据驱动战斗设计
Boss 系统采用完全数据驱动的架构。每个攻击、每个姿态都是独立的 DataAsset。
DA_BossStance(姿态配置)
├── AvailableAttacks: Array
├── MoveSpeed, MinStanceDuration
└── ComboChains
DA_BossAttack(攻击配置)
├── AttackMontage, Damage, Range
├── MinUseDistance / MaxUseDistance
└── Cooldown, VFX/SFX 引用
DataAsset 比 DataTable更适合结构化配置——支持嵌套引用(姿态引用攻击列表),强类型检查,可在编辑器中直接拖拽赋值
Component 负责逻辑,DataAsset 负责数据——AttackSelector 组件持有算法,CurrentStanceData 提供参数
AttackSelector 的选择算法:遍历 CurrentStance.AvailableAttacks → 检查玩家距离 ∈ [MinUseDistance, MaxUseDistance] → 返回匹配攻击
AI 移动系统 — NavMesh + EQS + MoveTo 的三角关系
AI MoveTo(GameplayTask) → 在 State Tree 上下文中无声失败
AIController.MoveToActor → then 引脚在请求发出后立刻触发,不是移动完成
Branch + bAlreadyMoving 护卫 → STT 每次重入重置变量,护卫无效
ReceiveTick + AddMovementInput → 蓝图编译报错”此蓝图并非 Pawn”
关键认知:
StateTreeMoveToTask 需要 EQS + MoveTo 双 Task 配合——参考敌人的 ST_CombatEnemy 模式:RunEnvQueryTask 提供目标位置 + MoveToTask 执行移动
EQS 绑定链路复杂:EQS Result → Parameter → MoveTo.Destination,在蓝图界面操作容易断链
移动逻辑可能不适合放在 State Tree 的子链里——考虑放到 DefaultMovement 作为”始终运行”的基础状态
UE5 AI 调试: State Tree 逐层验证法
为什么 State Tree 调试这么难?
State Tree没有Behavior Tree那样的可视化运行时调试器。 你无法在编辑器中看到当前哪个State在跑、哪个Task在执行——至少在写这篇文章时(UE5.8),State Tree Debugger 的功能还非常有限。
故障是静默的。 一个 Transition条件返回FALSE不会报错不会警告不会闪红——后面的State就是永远不会执行。你甚至不知道问题出在”条件判断”还是”State Tree 根本没启动”。
State Tree的架构和Behavior Tree完全不同。 抱着Behavior Tree的直觉来用State Tree,会被它的”线性流转 + 每次重入新建实例”模型反复背刺。
State Tree 是线性流,不是并行树
Behavior Tree 的模型是”从根往叶子并行评估,多个分支可以同时跑”。State Tree 不是这样——它更接近层级状态机:
1 | Root |
一个时刻只有一条路径上的 State 是激活的。前面的 State 卡住 = 后面全部不可达。
Transition 条件控制一切流转
State Tree 的流转由 Transition 上的 Condition(STC,StateTreeCondition)控制:
STC 返回 TRUE → Transition 触发 → 进入下一个 State
STC 返回 FALSE → Transition 不触发 → 当前 State 永远不退出(除非 State 自己有退出逻辑)
关键点:如果State没有挂Task,它永远不会”完成”,Transition也永远不会被评估。 除非把Transition的触发模式设为OnTick(每帧检查条件)。
逐层验证法:7 步定位任何问题
这套流程的核心思想是:从物理层往逻辑层逐层验证,每加一层就测一次。 永远不要一次性把所有State、Task、Condition 配满,出问题了你根本不知道卡在哪一层。
1:确认角色生成
在 EventGraph 的 BeginPlay 后面拖一个 Print String,内容写 “Boss Alive”。
2:确认AI控制器启动
在 BeginPlay 加 Print String “AI Start”。
常见坑:AI 控制器缺少 StateTreeAI 组件。 我是从已有的敌人 AI 控制器(BP_CombatAIController)复制结构创建 Boss 控制器的,但漏掉了这个关键组件——State Tree 完全不会启动,且没有任何报错。
3:确认 State Tree 启动了
打开 State Tree 资产,找到根节点(或第一层 State),在 Tasks 里挂一个最简单的STT。如果没有现成的简单 STT,可以创建一个只做一件事的:EnterState → Print String("Tree Running") → FinishTask(true)。
4:逐层加 Transition
State Tree 里每一个 Transition 上的 STC 条件都可能静默返回 FALSE,导致后面的 State 永远无法到达。所以你必须一层一层加:
1 | 策略:从简到繁 |
先用无条件 Transition 跑通整条链,确认所有 State 都能被到达。然后在 State_CombatBehavior里挂一个 Print String 的任务,验证”从根到叶子”的路径是通的。
5:验证每个 STT(Task)的逻辑
Print String 必须串在 exec 链中。
6:修复 STC(Condition)逻辑
最常见的 STC Bug:
| Bug | 现象 | 修复 |
|---|---|---|
GetAllActorsOfClass 没连 exec |
STC 永远返回默认值 | FunctionEntry → GetAllActorsOfClass exec 引脚 |
VariableGet self 悬空 |
读取不到变量值 | 从 GetAllActorsOfClass 的 Out Actors Get[0] 拖线连到 VariableGet 的 self |
Branch True/False 连反 |
条件逻辑反转 | 检查 True → Return True / False → Return False |
NOT Boolean 位置错误 |
Invert 逻辑搞反 | 在比较结果后加 NOT,或者在 State Tree Transition 中勾选 Invert |
变量没有勾选 Instance Editable |
State Tree 里看不到该变量的设置 | 在蓝图变量详情里勾选Instance Editable |
GetAllActorsOfClass 拿到的 Actor 还没初始化 |
属性为默认值 | 加 IsValid 守卫,无效时 Return False |
7:清理编译警告
检查 Output Log 里的警告。





