总览
UAFStateTree 的价值在于把动画决策从图里拆出来。复杂角色很容易把“能不能跑、拿什么武器、是否空中、是否受击、是否瞄准、是否剧情锁定”堆成一团布尔分支。StateTree 更适合承载这些决策,再由 UAF 执行对应动画图或写变量。
源码依据
重点文件:
UAFStateTree/Internal/AnimNextStateTree.hUAFStateTree/Internal/AnimStateTreeTrait.hUAFStateTree/Internal/AnimNextStateTreeSchema.hUAFStateTree/Internal/AnimNode/UAFStateTreeNode.hUAFStateTree/Internal/Tasks/UAFStateTreeSetVariableTask.hUAFStateTree/Internal/Tasks/AnimNextStateTreeGraphInstanceTask.h
UAnimNextStateTree 继承 UUAFAnimGraph,内部持有 UStateTree* StateTree。这意味着它在资产层面仍是一个 UAF AnimGraph,但决策内容由 StateTree 负责。
StateTree Trait
FAnimNextStateTreeTraitSharedData 保存:
FStateTreeReference StateTreeReferenceFStateTreeReferenceOverrides LinkedStateTreeOverrides
FStateTreeTrait 继承 FAdditiveTrait,实现 IUpdate 和 IGarbageCollection。实例数据里有:
TObjectPtr<const UStateTree> StateTreeFStateTreeInstanceData InstanceDataFStateTreeExecutionContext::FExternalGlobalParameters StateTreeExternalParameters
这说明 StateTree 的执行状态是每图实例独立的,适合做动画决策,但不要把全局 gameplay 状态塞进去当单例用。
Schema 限制
UStateTreeAnimNextSchema 继承 UStateTreeSchema。源码里它覆盖:
IsStructAllowedIsClassAllowedIsExternalItemAllowedGetContextDataDescsGetGlobalParameterDataType
编辑器里还禁用了 Utility Considerations、Evaluators 和 Queued Compilation。这说明 UAF StateTree 是一个受控子集,不是完整开放的通用 StateTree 工作区。这个限制对生产反而是好事:动画决策应该保持可预测、可编译、可回放。
StateTree AnimNode
FUAFStateTreeNode 继承 FUAFBlendStack。它持有 UStateTree、FStateTreeInstanceData 和 external parameters,并提供:
PreUpdateBlendTo(Context, TConstStructView<FUAFGraphFactoryAsset> AssetData, FUAFTransitionNodeData* TransitionBlend)AddReferencedObjects
FUAFStateTreeNodeData 则是 FUAFAnimNodeData,可以引用 UAnimNextStateTree。这条路径让 StateTree 既能作为 Trait,也能作为 AnimNode 风格的图切换节点。
Set Variable Task
FUAFStateTreeSetVariableTask 是非常实用的桥。它的实例数据:
USTRUCT()
struct FUAFStateTreeSetVariableTaskInstanceData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = Variable)
FAnimNextVariableReference Variable;
UPROPERTY(EditAnywhere, Category = Variable, Meta = (FixedLayout, ShowOnlyInnerProperties))
FInstancedPropertyBag Value;
};
注释写得很清楚:这个任务在 state entry 时写一个 UAF shared variable;如果要写多个变量,就在一个 state 中叠多个 task。它适合做“进入 Aiming 状态时设置 AimAlphaTarget”、“进入 Injured 状态时设置 LayerWeight”、“进入 Weapon_Rifle 状态时设置 StanceName”这类决策输出。
项目落地
推荐拆分:
- Gameplay 状态仍由 gameplay 系统权威维护。
- UAF 公共变量接收 gameplay 的摘要数据。
- StateTree 根据摘要数据做动画状态决策。
- StateTree 进入状态时通过 Set Variable Task 写动画变量。
- 图切换通过 UAF Graph Factory Asset 和 BlendStack 完成。
示意结构:
Gameplay / Movement
-> UAF public variables
-> UAnimNextStateTree
-> State entry tasks
-> UAF graph variables / BlendTo graph
-> AnimGraph evaluation
使用案例:武器和瞄准动画决策
假设角色有空手、步枪、大剑三种武器,还有瞄准、换弹、受击三个覆盖状态。不要把这些分支全放进 AnimGraph。可以建一个 ST_Hero_AnimDecision_UAF:
Root
Locomotion
Grounded
InAir
Weapon
Unarmed
Rifle
RifleHip
RifleAim
RifleReload
GreatSword
Overlay
None
HitReact
CinematicLock
每个状态进入时只写动画变量:
| State | Task | 写入值 |
|---|---|---|
| RifleHip | UAF Set Variable |
WeaponStance = Rifle、OverlayState = None |
| RifleAim | UAF Set Variable |
WeaponStance = Rifle、AimAlphaTarget = 1 |
| RifleReload | UAF Set Variable |
OverlayState = Reload、UpperBodyWeight = 1 |
| HitReact | UAF Set Variable |
OverlayState = HitReact、HitLayerWeight = 1 |
AnimGraph 之后读取这些变量:Chooser 根据 WeaponStance 选上半身图,Layering 根据 OverlayState 和 layer weight 播放覆盖层。这样 StateTree 管“现在应该表现成什么状态”,AnimGraph 管“姿态如何混合”。
操作步骤
- 创建
UAF State Tree资产。源码里的资产定义显示它在Animation / Animation Framework分类下,显示名是UAF State Tree。 - 在 StateTree schema 可用的上下文里接入 UAF 变量上下文。
- 为每个离散动画状态建 state,不要把连续速度、方向做成 state。
- 每个 state 进入时添加一个或多个
UAF Set Variabletask。 - 过渡条件读取 gameplay 摘要变量,例如
bIsInAir、WeaponStance、bWantsAim。 - 图切换由 UAF Graph Factory Asset 或下游 Chooser 完成,StateTree 不直接处理 pose。
- 给每个状态配置明确的 fallback,例如武器状态未知时回到
Unarmed。
架构分析:StateTree 和 Chooser 谁先谁后
两种组合都能用,但用途不同:
| 组合 | 适合场景 | 示例 |
|---|---|---|
| StateTree 先决策,Chooser 后选资产 | 状态生命周期重要,需要 entry task | 瞄准进入时写 AimAlphaTarget,再由 Chooser 选 RifleAim 图 |
| Chooser 先选大类图,StateTree 在子图内决策 | 条件主要是资产差异 | 角色体型或武器先选子图,子图内再管 reload/aim |
| 两者并行,分别管不同层 | 大型角色系统 | locomotion Chooser,upper body StateTree |
判断标准:如果你关心“进入状态时做一次事”,用 StateTree;如果你关心“根据一组条件选一个结果”,用 Chooser。
使用建议
- StateTree 负责“选择什么”,AnimGraph 负责“怎么混合和评估”。
- 变量 task 负责状态入口的离散写入,不适合每帧连续驱动。
- 连续值如速度、方向、坡度仍应由 movement 每帧写入公共变量。
- 图切换时给 transition data,避免状态切换变硬切。
- 每个 StateTree 资产只负责一个清晰层级,例如 locomotion、weapon stance、hit reaction,不要一个表管全角色。
常见坑
- StateTree 不是 gameplay authority。不要让动画 StateTree 决定角色是否真的能攻击。
UStateTreeAnimNextSchema有限制,不要照搬通用 StateTree 用法。- Set Variable Task 的
FInstancedPropertyBag要和变量类型匹配;变量类型变化后要重新检查任务值。 - Graph switch 和 variable write 同时发生时,要明确顺序和过渡策略。
- StateTree 实例状态不能跨角色共享,调试时要看具体 UAF 图实例。
源码路径索引
UAFStateTree/Source/UAFStateTree/Internal/AnimNextStateTree.hUAFStateTree/Source/UAFStateTree/Internal/AnimStateTreeTrait.hUAFStateTree/Source/UAFStateTree/Internal/AnimNextStateTreeSchema.hUAFStateTree/Source/UAFStateTree/Internal/Tasks/UAFStateTreeSetVariableTask.h