总览
Smart Object 最容易出线上 bug 的地方不是“找不到”,而是“找到了以后没有正确释放”。一个 Slot 如果停在 Claimed 或 Occupied,后面所有用户都会以为它不可用。你要把 Claim 看成资源锁,把 Occupied 看成正在执行,把 Release 看成必须发生的清理。
生命周期图
Free
-> FindSmartObject 得到候选
-> MarkSlotAsClaimed
Claimed
-> AI MoveTo 入口或 Slot
-> MarkSlotAsOccupied
Occupied
-> 播放动画 / GameplayBehavior / StateTree / Ability
-> MarkSlotAsFree
Free
FSmartObjectRequestResult 不是锁;FSmartObjectClaimHandle 才是锁的凭据。ClaimHandle 里有 SmartObjectHandle、SlotHandle 和 UserHandle。只要要跨帧移动、播放动画或等行为结束,就必须保存 ClaimHandle。
使用案例
两个 NPC 同时想坐同一把椅子。正确行为是:第一个 NPC 成功 Claim,第二个 NPC 的普通优先级 Claim 失败,除非第二个以更高 ESmartObjectClaimPriority 抢占一个还没 Occupied 的低优先级 Claim。已经 Occupied 的 Slot 不应该被普通逻辑抢走。
对“玩家打断 NPC 坐椅子”的设计可以这样做:玩家交互优先级为 High,如果 NPC 只是 Claimed 还没坐下,可以取消 NPC MoveTo 并释放;如果 NPC 已 Occupied,则播放请起身流程,等 StateTree/行为结束后释放,而不是直接 MarkSlotAsFree。
架构分析
FSmartObjectRuntimeSlot::CanBeClaimed 的核心条件是 Slot enabled,并且状态是 Free,或状态是 Claimed 且已有优先级低于新请求。Occupied 不在这个抢占条件里。MarkSlotAsOccupied 会根据 ClaimHandle 找到对应 BehaviorDefinition;MarkSlotAsFree 会释放 Claimed 或 Occupied 状态。
Slot 失效也要处理。USmartObjectSubsystem 提供 RegisterSlotInvalidationCallback 和 UnregisterSlotInvalidationCallback。当对象被禁用、卸载或运行时状态无效时,用户应该收到通知并停止行为,避免角色继续朝一个已经不可用的点移动。
项目落地
为所有使用 Smart Object 的任务写统一清理规则:成功结束释放,Abort 释放,外部 Stop 释放,Owner 销毁释放,Slot invalidated 释放。BT Task、StateTree Task、AbilityTask、Mass Task 都要有同样的生命周期守卫。不要让每个业务程序各写各的释放逻辑。
常见坑
- MoveTo 前没 Claim:路上被别人抢走,两个角色重叠。
- MoveTo 失败没释放:Slot 永远 Claimed。
- 行为开始没 MarkSlotAsOccupied:调试器看不到正在使用,且抢占语义不清。
- 释放时使用旧 SlotHandle:释放必须用 ClaimHandle,避免释放别人占的 Slot。
- 高优先级滥用:所有人都是 High 就等于没有优先级。
源码依据
ESmartObjectSlotState 包含 Free、Claimed、Occupied。FSmartObjectClaimHandle 保存对象、Slot 和 User 三个句柄,并说明有效句柄不保证对象仍在 simulation,需要通过 Subsystem 校验。USmartObjectSubsystem 暴露 CanBeClaimed、MarkSlotAsClaimed、MarkSlotAsOccupied、MarkSlotAsFree、GetSlotState、RegisterSlotInvalidationCallback 和 UnregisterSlotInvalidationCallback。
源码路径索引
SmartObjectsModule/Public/SmartObjectRuntime.hSmartObjectsModule/Public/SmartObjectSubsystem.hSmartObjectsModule/Private/SmartObjectRuntime.cppSmartObjectsModule/Public/SmartObjectTypes.h