总览
UAFChooser 的任务是把 Chooser 选择表接到 UAF 图资产。项目里常见需求是“根据速度、武器、姿态、地面状态、角色体型选择不同动画子图或资产”。如果把这些判断硬写进动画图,后期会变成难以维护的布尔分支。Chooser 更适合把条件和结果表格化。
源码依据
重点文件:
UAFChooser/Public/UAFAnimChooser.hUAFChooser/Public/ChooserPlayerTraitData.hUAFChooser/Internal/ChooserParameters.hUAFChooser/Internal/ObjectChooser_UAFGraph.hUAFChooser/Public/UAFGraphFactoryAsset_Chooser.hUAFChooser/Internal/AnimNode/UAFChooserPlayerNode.h
UUAFAnimChooserTable 是 UChooserTable 的 UAF 专用子类,用于选择能生成 UAF AnimGraph 的资产。FUAFChooserPlayerTraitSharedData 是图里的 Chooser Player trait data,保存 Chooser 表和 EChooserEvaluationFrequency。
Chooser 结果如何变成 UAF 图
FUAFGraphChooser 继承 FObjectChooserBase,DisplayName 是 UAFGraph,ResultType 是 Object。它不直接保存旧的 Asset 字段,而是用 TInstancedStruct<FUAFGraphFactoryAsset> AssetData。源码里旧 Asset_DEPRECATED 和 FAnimNextFactoryParams Parameters 都标了 deprecated,UE5.8 这条线明显转向“FactoryAsset 结构体”。
FUAFGraphFactoryAsset_Chooser 则反过来把 Chooser Table 包装成 UAF 图资产工厂:
USTRUCT(DisplayName="Chooser Asset")
struct FUAFGraphFactoryAsset_Chooser : public FUAFGraphFactoryAsset
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = Chooser)
TObjectPtr<const UUAFAnimChooserTable> ChooserTable;
UPROPERTY(EditAnywhere, Category = Settings)
EChooserEvaluationFrequency EvaluationFrequency =
EChooserEvaluationFrequency::OnBecomeRelevant;
};
工程上可以这样理解:Chooser 的每一行结果不是“播放某个动画”这么窄,而是可以返回一个能被 UAF 解释为图的资产描述。
参数来源
ChooserParameters.h 里定义了 UAF 变量参数:
| 参数 | 读取类型 | 典型用途 |
|---|---|---|
FBoolAnimProperty |
bool | 是否瞄准、是否在空中、是否持盾 |
FFloatAnimProperty |
float | 速度、坡度、转向角、距离 |
FEnumAnimProperty |
enum/byte | 移动模式、武器类型、姿态 |
FNameAnimProperty |
FName | 命名状态、slot、stance |
这些参数都保存 FAnimNextVariableReference Variable。所以 Chooser 表不是自己找 Character,也不是读蓝图变量,而是通过 UAF 公共变量读取输入。
Evaluation Frequency
Chooser Player 里有 EChooserEvaluationFrequency:
OnBecomeRelevant:进入相关状态时选择一次。适合武器类型、角色体型、装备集合等不需要每帧变的条件。OnUpdate或类似每帧更新模式:适合速度区间、地面状态、短时状态。代价更高,但反应快。
默认频率要谨慎。动画系统里“每帧查表”不是不能做,但如果结果会触发图切换,就会牵涉 blending、重置、同步和性能。
项目落地
建议把 Chooser 表按职责拆开:
- Locomotion Chooser:速度、移动模式、坡度、转向角 -> locomotion graph。
- Weapon Chooser:武器类型、持握方式 -> upper body graph。
- Stance Chooser:站立、蹲伏、受伤、重载 -> pose layer 或 state graph。
- Cinematic Chooser:剧情状态、角色体型、装备 -> 特殊覆盖图。
公共变量示例:
USTRUCT(BlueprintType)
struct FProjectAnimChooserVars
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
float Speed = 0.0f;
UPROPERTY(EditAnywhere)
bool bIsInAir = false;
UPROPERTY(EditAnywhere)
FName WeaponStance = TEXT("Unarmed");
};
Chooser 表里把 Speed 绑定到 FFloatAnimProperty,把 bIsInAir 绑定到 FBoolAnimProperty,把 WeaponStance 绑定到 FNameAnimProperty。结果列使用 UAFGraph,返回对应的 UAF Graph Factory Asset。
使用案例:Locomotion Chooser 表
一个最小 locomotion Chooser 可以只关心三个输入:GroundSpeed、bIsInAir、WeaponStance。表格不要一开始写得很细,先用少量稳定行验证图选择:
| GroundSpeed | bIsInAir | WeaponStance | Result |
|---|---|---|---|
| 0 到 5 | false | Any | AG_Hero_Idle |
| 5 到 180 | false | Any | AG_Hero_WalkRun |
| Any | true | Any | AG_Hero_InAir |
| Any | false | Rifle | AG_Hero_Rifle_Locomotion |
操作步骤:
- 创建
UAF Shared Variables,至少包含GroundSpeed、bIsInAir、WeaponStance。 - 创建
UAF Anim Chooser Table,把 Shared Variables 加到 context。 - 添加
FFloatAnimProperty列绑定GroundSpeed。 - 添加
FBoolAnimProperty列绑定bIsInAir。 - 添加
FNameAnimProperty或 enum 参数列绑定WeaponStance。 - 结果列使用
UAFGraph,每行返回一个 UAF Graph Factory Asset。 - 在 UAF Animation Graph 中放
Chooser Player,指向这张表。 - 初始
EvaluationFrequency用OnBecomeRelevant验证稳定性;需要速度实时切换时再评估高频模式。
如果表格开始出现几十个速度区间,说明 Chooser 被当成 BlendSpace 用了。速度连续变化应该交给 BlendSpace、Motion Matching 或图内 blend,Chooser 只负责选择大类图。
使用案例:武器上半身图选择
武器表应该和 locomotion 表分开,避免一张表同时决定“脚怎么走”和“手怎么拿武器”:
| WeaponStance | bIsAiming | Result |
|---|---|---|
| Unarmed | false | AG_Upper_Unarmed_Relaxed |
| Rifle | false | AG_Upper_Rifle_Hip |
| Rifle | true | AG_Upper_Rifle_Aim |
| GreatSword | Any | AG_Upper_GreatSword |
这张表可以被 LayerStack 的 UpperBody 层使用。切换武器时只影响上半身层,不应该重置地面移动图。多人项目里也更容易做网络回放:WeaponStance 是离散变量,日志和 Rewind 都能看懂。
架构分析:Chooser 放在哪里
| 放置位置 | 适合场景 | 风险 |
|---|---|---|
| AnimGraph 内部 Chooser Player | 子图选择、资产选择、可视化调试 | 高频重选会触发过渡抖动 |
| StateTree 进入状态后写变量,再由 Chooser 选图 | 离散状态驱动的图选择 | 变量写入和图切换顺序要明确 |
| LayerStack 每层独立 Chooser | 上半身、表情、受击层互不干扰 | 表之间可能重复条件 |
| C++ 里手写 if/else 后设置图 | 紧急原型 | 不利于动画师维护和调试 |
建议原则是:Chooser 负责“大方向选择”,图内负责连续混合,StateTree 负责状态生命周期。
调试方法
排查 Chooser 问题按这个顺序:
- 变量是否写入了正确的
UUAFComponent或系统实例。 FAnimNextVariableReference是否指向当前 Chooser 上下文能访问的变量资产。- Chooser 参数类型是否和变量类型一致。
- EvaluationFrequency 是否导致结果没有重新计算。
- 返回对象是否能被 UAF 转成图资产。
- 图切换是否被 BlendStack、Injection 或上层 StateTree 覆盖。
常见坑
- 不要在 Chooser 里塞所有动画逻辑。Chooser 只负责选择,时间推进、姿态混合和同步应留给图。
- 不要让多个 Chooser 表同时写同一个决策变量,容易出现帧序依赖。
- 不要把高频连续值拆成大量区间后每帧重选大图,先考虑 BlendSpace 或 Motion Matching。
- UAF 的 Chooser 结果应该返回图资产工厂相关数据,旧
Asset_DEPRECATED字段只是迁移兼容。 FUAFSharedVariablesContext可以声明上下文变量资产,表格维护时要保证上下文完整。
源码路径索引
UAFChooser/Source/UAFChooser/Public/UAFAnimChooser.hUAFChooser/Source/UAFChooser/Public/ChooserPlayerTraitData.hUAFChooser/Source/UAFChooser/Internal/ChooserParameters.hUAFChooser/Source/UAFChooser/Internal/ObjectChooser_UAFGraph.h