UE5.8 Chooser 喂饭级专题

UE5.8 Chooser 专题(十):调试、性能、Cook 和生产规范

整理 Chooser Debug Target、Debug Selected Rows、Trace/RewindDebugger、CookedResults、禁用行剥离、Context 校验、性能预算、命名拆表和上线检查清单。

总览

Chooser 表越多,越要建立规范。生产级 Chooser 不只是“能选出东西”,还要能回答:为什么选了这一行?为什么没选?Cook 后数据还在吗?玩家旧档案会不会导致空结果?多人下客户端和服务器会不会选出不同动作?

UE5.8 Chooser 专题(十):调试、性能、Cook 和生产规范 配图
生产级 Chooser 要证明三件事:输入清楚、命中可解释、失败可回退。

命名规范

推荐:

CHT_Combo_NextMove_Sword
CHT_Combo_EditCandidates_Sword
CHT_Anim_Locomotion_Start
CHT_AI_MeleeAttack_Select
PXA_Move_Light02_Montage
PT_Skin_Samurai_ComboAssets

前缀区分资产类型,后缀表达用途。不要叫 NewChooserTable_12,调试 Trace 时会很难受。

表拆分规范

一张表只解决一个问题:

职责
NextMove按战斗上下文选下一段技能
EditCandidates给 UI 列出可编辑候选
AnimationVariant为已确定 Move 选动画变体
CameraCue为已确定动作选相机参数

如果一张表里同时决定技能、动画、镜头、音效、掉落,后期会很难读。

Debug Target 和选中行

UChooserTable 编辑器期有 Debug Target、Recent Context Objects、Debug Selected Rows 等字段。运行时 Evaluate 时,表会更新调试信息,编辑器能显示哪些行通过或失败。

调试顺序建议:

  1. 确认 Context Object 是否传对。
  2. 确认结构体参数是否出现在 Parameters。
  3. 打开测试值,看每列是否按预期通过。
  4. 看最终 Selected Row。
  5. 如果走 Fallback,逐列缩小是哪一列筛掉了所有行。

Trace 和 Rewind Debugger

源码里有 ChooserTrace.cppRewindDebuggerChooserRuntime.cpp 和编辑器侧 Rewind Debugger 支持。项目调试时可以把 Chooser 命中看作时间线事件:某一帧、某个角色、某张表、命中哪一行。动作游戏调连招时,这比只看日志舒服很多。

Cook 行为

UChooserTable 编辑器期有 ResultsStructsDisabledRowsNestedObjects。Cook 时会调用 PopulateCookedData,把未禁用结果写到 CookedResults,并移除禁用行和无效列。运行时在 Cooked 包里用 CookedResults

这意味着:

  • 禁用行不会成为运行时逻辑的一部分。
  • 编辑器里测试和 Cook 后测试都要做。
  • 动态引用的资产仍然要进入正确 Cook 边界。

性能预算

Chooser 通常不贵,但别乱用:

场景建议
玩家按键解析可以按输入触发 Evaluate
动画 OnBecomeRelevant通常没问题
动画 OnUpdate 每帧表要小,Context 要轻
Mass 大批实体谨慎,优先批处理/缓存分类
UI 候选列表打开面板时算,别每帧刷

不要在列绑定里触发昂贵函数或同步加载。Context 应该提前准备好轻量字段。

多人一致性

玩家编辑连招项目里,服务器和客户端都可能需要解析。要注意:

  • 玩家档案由服务器校验和同步。
  • 随机选择要么只在服务器做,要么使用可复现 RandomStream。
  • 客户端预测可以先选,但服务器拒绝时要回滚。
  • Ability 最终仍由 GAS 权威判断。
  • 动画变体可以客户端表现不同,但不能影响伤害判定。

上线检查清单

  • 每张表都有 Fallback 或明确的空结果处理。
  • Context Struct 有版本意识,字段改名有迁移方案。
  • 玩家 SaveGame 只存稳定 ID/Tag,不存易变对象路径。
  • 编辑候选表和战斗解析表规则一致或有明确差异。
  • Result 类型和调用侧类型检查一致。
  • Cook 包里资源存在,禁用行符合预期。
  • Trace 或日志能定位命中行。
  • ProxyTable 停用后没有强引用残留。
  • GAS 冷却、消耗、Tag 阻塞仍是最终权威。

使用案例:排查玩家“第二段不出招”

先看输入缓冲是否产生 Input.Attack.Light。再看玩家档案里第二槽 MoveTag 是否存在。然后用 Debug Target 选中该玩家,Evaluate CHT_Combo_NextMove,看 PreviousMoveTag 是否是第一段,TargetDistance 是否落在范围里,PlayerTags 是否包含 Stunned。若全部通过但 GAS 拒绝,再看冷却、Cost、ActivationBlockedTags。这样排查路径很清晰,不会在表、输入、动画、GAS 之间来回猜。

架构分析

Chooser 的可维护性来自三件事:小 Context、小表、清楚的执行边界。Context 太大,表会脆;表太大,调试会慢;边界不清,GAS、动画、UI 会互相推锅。生产规范的目的不是让表变保守,而是让它能被多人长期修改。

常见坑

  • 只在 Editor PIE 测试,Cook 后 CookedResults 或资源引用出问题。
  • 没 Fallback,玩家旧档案一升级就空结果。
  • 每帧 Evaluate 大表,并且绑定深层对象属性。
  • 随机结果客户端和服务器不一致。
  • ProxyTable 继承链和 Nested Chooser 太深,调试选中行无法解释。

源码依据

UChooserTable::PopulateCookedData 会在 Cook 时移除禁用数据并填充 CookedResultsEvaluateChooser 在编辑器下会更新 Debugging,并记录 Selected Rows。ChooserTrace 和 Rewind Debugger 相关源码提供运行时追踪支持。CHOOSER_TRACE_ENABLED 在非 Shipping/Test 且启用 Trace 时打开,Shipping 构建不要依赖调试信息。

源码路径索引

  • Engine/Plugins/Chooser/Source/Chooser/Public/Chooser.h
  • Engine/Plugins/Chooser/Source/Chooser/Private/Chooser.cpp
  • Engine/Plugins/Chooser/Source/Chooser/Private/ChooserTrace.cpp
  • Engine/Plugins/Chooser/Source/Chooser/Private/RewindDebuggerChooserRuntime.cpp
  • Engine/Plugins/Chooser/Source/ChooserEditor/Private/RewindDebuggerChooser.cpp
  • Engine/Plugins/Chooser/Source/ChooserEditor/Private/ChooserAnalyzer.cpp
  • Engine/Plugins/Chooser/Source/Chooser/Public/IObjectChooser.h