UE5.8 Smart Objects 专题系列

UE5.8 Smart Objects 专题(三):查找、过滤与空间索引

讲清 FSmartObjectRequest、FSmartObjectRequestFilter、QueryBox、ActivityRequirements、UserTags、Predicate、HashGrid/Octree、EQS Generator 和调试拒绝原因。

总览

Smart Object 查询不是“找最近椅子”。源码里的 FindSmartObject 明确说返回的是空间分区里第一个有效对象,不保证最近。真实项目如果要最近、最优、队列最短、离目标最近,应该先拿一批候选,再自己排序或用 EQS/Mass 处理。

UE5.8 Smart Objects 专题(三):查找、过滤与空间索引 配图
查询不是简单找最近对象,而是空间范围、对象级过滤、Definition 过滤、Slot 过滤和条件评估的流水线。

查询流水线

  1. QueryBox 定义搜索范围。
  2. 用空间结构取出范围内的 Smart Object 候选。
  3. 检查对象是否 enabled、是否通过 Predicate、对象级 Preconditions。
  4. 对每个 Definition 和 Slot 进行行为类型、ActivityRequirements、UserTags 过滤。
  5. 检查 Slot enabled、是否已被高优先级占用、Slot Preconditions。
  6. 返回 FSmartObjectRequestResult,包含对象句柄和 Slot 句柄。

使用案例

巡逻 NPC 想找 800cm 内能坐的椅子:

FSmartObjectRequestFilter Filter;
Filter.UserTags = UserTagsFromNPC;
Filter.ActivityRequirements = SitActivityQuery;
Filter.ClaimPriority = ESmartObjectClaimPriority::Normal;

FSmartObjectRequest Request;
Request.QueryBox = FBox::BuildAABB(NPC.GetActorLocation(), FVector(800.0));
Request.Filter = Filter;

TArray<FSmartObjectRequestResult> Results;
Subsystem->FindSmartObjects(Request, Results, FConstStructView::Make(FSmartObjectActorUserData(&NPC)));

Results.Sort([&](const FSmartObjectRequestResult& A, const FSmartObjectRequestResult& B)
{
    return FVector::DistSquared(*Subsystem->GetSlotLocation(A), NPC.GetActorLocation())
         < FVector::DistSquared(*Subsystem->GetSlotLocation(B), NPC.GetActorLocation());
});

上面示意里 GetSlotLocation 返回 TOptional<FVector>,生产代码要处理无效值。重点是:如果你需要“最近”,不要直接相信 FindSmartObject 的第一个结果。

架构分析

FSmartObjectRequestFilter 的几个字段非常关键:UserTags 给 Definition/Slot 的 UserTagFilter 使用;ClaimPriority 决定是否能抢占低优先级 Claimed Slot;ActivityRequirements 匹配 ActivityTags;BehaviorDefinitionClasses 可以限定只找含某类 BehaviorDefinition 的 Slot;bShouldEvaluateConditions 控制是否执行 World Conditions;bShouldIncludeClaimedSlotsbShouldIncludeDisabledSlots 用于调试或特殊逻辑。

空间索引层的实现由 USmartObjectSubsystem 维护,源码里有 SmartObjectHashGridSmartObjectOctree。你不需要直接操作它们,但要知道查询范围太大时不是免费的;大量 AI 每帧全图查找,会把好系统用坏。

EQS 接入

UEnvQueryGenerator_SmartObjects 可以从 EQS Origin 出发,用 QueryBoxExtentFSmartObjectRequestFilter 生成 Smart Object Slot 项。它还有 bOnlyClaimable,用于只返回当前可 Claim 的 Slot。Behavior Tree 里配合 EQS 的好处是可以用现有 EQS Test 做距离、可见性、路径成本排序。

项目落地

把查询拆成两层:基础过滤交给 Smart Object,策略排序交给调用方。基础过滤回答“这个对象是否可以被这个用户做这件事”;策略排序回答“现在选哪个最好”。比如坐椅子按距离排序,找掩体按面向敌人排序,找工作台按任务优先级排序。

常见坑

  • 每 Tick 查询:加冷却、缓存、事件触发或 StateTree Tick 间隔。
  • QueryBox 太大:城市人群应配合 ZoneGraph/Mass 请求,不要所有实体都全局扫。
  • 把 ActivityRequirements 当对象类型:Activity 是行为能力,不是 Mesh 类型。
  • Predicate 里做昂贵逻辑:Predicate 会参与过滤流水线,保持轻量。
  • 忽略 DebugRejections:UE5.8 的请求过滤有 Definition/Object/Slot 三类拒绝原因,排查时很有用。

源码依据

FSmartObjectRequestQueryBoxFSmartObjectRequestFilter 组成。FSmartObjectRequestFilter 包含 UserTagsClaimPriorityActivityRequirementsBehaviorDefinitionClassesbShouldEvaluateConditionsbShouldIncludeClaimedSlotsbShouldIncludeDisabledSlots 和 Predicate。USmartObjectSubsystem 提供 FindSmartObjectFindSmartObjectsFindSmartObjectsInListFindSmartObjectsInTargetingRequestFindSlots 和条件过滤 API。UEnvQueryGenerator_SmartObjects 是 EQS Generator。

源码路径索引

  • SmartObjectsModule/Public/SmartObjectRequestTypes.h
  • SmartObjectsModule/Public/SmartObjectSubsystem.h
  • SmartObjectsModule/Public/SmartObjectHashGrid.h
  • SmartObjectsModule/Public/SmartObjectOctree.h
  • SmartObjectsModule/Public/EnvQueryGenerator_SmartObjects.h