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 调试这么难?

  1. State Tree没有Behavior Tree那样的可视化运行时调试器。 你无法在编辑器中看到当前哪个State在跑、哪个Task在执行——至少在写这篇文章时(UE5.8),State Tree Debugger 的功能还非常有限。

  2. 故障是静默的。 一个 Transition条件返回FALSE不会报错不会警告不会闪红——后面的State就是永远不会执行。你甚至不知道问题出在”条件判断”还是”State Tree 根本没启动”。

  3. State Tree的架构和Behavior Tree完全不同。 抱着Behavior Tree的直觉来用State Tree,会被它的”线性流转 + 每次重入新建实例”模型反复背刺。

State Tree 是线性流,不是并行树

Behavior Tree 的模型是”从根往叶子并行评估,多个分支可以同时跑”。State Tree 不是这样——它更接近层级状态机

1
2
3
4
5
Root
└── State A(激活中...)
├── Child A1 → 完成 → 流转到 Child A2
└── Child A2 → 完成 → 流转到 Child A3

一个时刻只有一条路径上的 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
2
3
4
5
6
7
策略:从简到繁
第一轮:所有 Transition 用无条件 GotoState(不挂任何 STC)
→ 验证整棵树的 State 流转是否通畅

第二轮:逐一替换为 STC 条件
→ 每替换一个就 Play 测试一次
→ 哪个替换后流转卡住了,问题就在那个 STC

先用无条件 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 里的警告。