总览
Chooser 资产不会自己运行。你需要在技能组件、动画实例、AI Task 或 UI 页面里构造 Context,调用表,检查结果,再把结果交给 GAS、动画或 UI。
蓝图调用有两条路线
简单路线:
Evaluate Chooser(ContextObject, ChooserTable, ObjectClass)
适合只需要一个对象输入的表,比如 AnimInstance 里根据自身属性选动画。
完整路线:
Make Chooser Evaluation Context
Add Chooser Object Input
Add Chooser Struct Input
Add Chooser Struct Input Output
Make Evaluate Chooser
Evaluate Object Chooser Base
Get Chooser Struct Output
技能连招、玩家配置、输出参数、多结果都应该用完整路线。
C++ 单结果模板
UComboMoveData* UComboResolverComponent::ResolveNextMove(
const FComboChooserContext& Input,
FComboChooserOutput& Output) const
{
if (!ComboChooser)
{
return nullptr;
}
FComboChooserContext InputCopy = Input;
FChooserEvaluationContext Context;
Context.AddObjectParam(GetOwner());
Context.AddStructParam(InputCopy);
Context.AddStructParam(Output);
UComboMoveData* ResultMove = nullptr;
UChooserTable::EvaluateChooser(
Context,
ComboChooser,
FObjectChooserBase::FObjectChooserIteratorCallback::CreateLambda(
[&ResultMove](UObject* Result)
{
ResultMove = Cast<UComboMoveData>(Result);
return ResultMove
? FObjectChooserBase::EIteratorStatus::Stop
: FObjectChooserBase::EIteratorStatus::Failed;
}));
return ResultMove;
}
注意 AddStructParam 持有的是引用视图,结构体生命周期必须长于本次 Evaluate。不要传临时右值。
C++ 多结果模板
TArray<UComboMoveData*> Results;
UChooserTable::EvaluateChooser(
Context,
ComboChooser,
FObjectChooserBase::FObjectChooserIteratorCallback::CreateLambda(
[&Results](UObject* Result)
{
if (UComboMoveData* Move = Cast<UComboMoveData>(Result))
{
Results.Add(Move);
return FObjectChooserBase::EIteratorStatus::Continue;
}
return FObjectChooserBase::EIteratorStatus::Failed;
}));
返回 Continue 会继续找后面的结果,返回 Stop 会停止,返回 Failed 会告诉 Chooser 这行没有成功。
用 Function Library 包装 ObjectChooserBase
如果你把 Chooser 表包装成 FInstancedStruct,可以用:
FInstancedStruct EvalChooser = UChooserFunctionLibrary::MakeEvaluateChooser(ComboChooser);
UObject* Result = UChooserFunctionLibrary::EvaluateObjectChooserBase(
Context,
EvalChooser,
UComboMoveData::StaticClass(),
false);
这个路线和蓝图里的 Make Evaluate Chooser 更接近,适合你要在同一接口里支持 EvaluateChooser、LookupProxy、Nested 等多种 ObjectChooserBase。
缓存怎么做
可以缓存:
- ChooserTable 软引用加载后的指针。
- 常用 GameplayTag。
- Context Struct 的字段填充逻辑。
- 玩家连招配置的运行时快照。
不要缓存:
FChooserEvaluationContext本身。- 表里返回结果的“最终决定”,除非上下文完全不变。
- 指向临时输出结构体的引用。
Chooser 每次 Evaluate 依赖当下上下文。技能连招、目标距离、玩家 Tag、冷却状态都可能帧间变化。
使用案例:Ability 中调用
UGameplayAbility 里可以在 Commit 前解析:
FComboChooserOutput Output;
UComboMoveData* Move = ComboResolver->ResolveNextMove(Context, Output);
if (!Move || !CanActivateMove(Move))
{
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true);
return;
}
CommitAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo);
PlayMontageSection(Output.MontageSection);
更推荐把解析放在角色的 ComboResolverComponent 或 CombatSubsystem 里,Ability 只消费解析结果。这样 UI、AI、预测和测试也能复用。
架构分析
运行时代码应该是一道“防火墙”。表作者可以填错,玩家配置可能过期,资产可能没加载,Result 类型可能不匹配。调用层必须做空值、类型、Fallback、日志、GAS 合法性和失败提示。Chooser 表越可调,运行时代码越要稳。
常见坑
FChooserEvaluationContext存成成员变量,内部FStructView指向已经失效的栈内存。- 多结果回调返回 Stop,实际只拿到第一个。
- 忘记检查 Result 类型,UClass 和 UObject 混用。
- 输出结构体被拷贝了,读回的是旧值。
- 动画线程/AnyThread 场景调用了非线程安全的项目对象。
源码依据
UChooserFunctionLibrary::EvaluateChooser 是单对象输入的便捷函数。AddChooserStructInput 蓝图 thunk 把结构体参数作为 FStructView 加入 Context。AddChooserStructInputOutput 还会记录输出数组来源。EvaluateObjectChooserBase 会调用 FObjectChooserBase::ChooseMulti 并按 ObjectClass 和 bResultIsClass 做类型过滤。
源码路径索引
Engine/Plugins/Chooser/Source/Chooser/Public/ChooserFunctionLibrary.hEngine/Plugins/Chooser/Source/Chooser/Private/ChooserFunctionLibrary.cppEngine/Plugins/Chooser/Source/Chooser/Public/Chooser.hEngine/Plugins/Chooser/Source/Chooser/Public/IObjectChooser.hEngine/Plugins/Chooser/Source/Chooser/Private/Chooser.cpp