Quick answer: Store the FSmartObjectClaimHandle and call Release on every exit path — behavior complete, aborted, AI killed, level streamed out.
AI uses Smart Objects for “use bench” behaviors. Over time, benches stop being usable — AI that claimed slots died or aborted without releasing them.
Claim/Use/Release Lifecycle
FSmartObjectClaimHandle ClaimHandle =
SmartObjectSubsystem->MarkSlotAsClaimed(SlotHandle, ...);
// ... AI walks to and uses the slot ...
SmartObjectSubsystem->MarkSlotAsFree(ClaimHandle); // MUST happen
A claimed slot is unavailable to others until freed. Miss the free and the slot leaks.
Release on Every Exit Path
Behaviors abort for many reasons. Release in all of them:
- Behavior completed normally.
- Behavior aborted (player interrupted, better goal found).
- AI pawn destroyed / killed mid-use.
- Level / Smart Object streamed out.
In a State Tree task, release in ExitState — it runs for both success and abort.
Owner-Tracked Cleanup
Bind to the AI’s OnDestroyed / OnEndPlay to release any held claim as a safety net:
Pawn->OnEndPlay.AddDynamic(this, &UMyTask::ReleaseClaimOnDeath);
Verifying
Run AI through use cycles, killing some mid-use. Slots free up correctly. smartobject.debug console shows no permanently-claimed leaked slots.
“Claim and free must be balanced on every code path. ExitState plus an OnDestroyed safety net covers it.”
Audit with the Smart Object debugger periodically — a slowly-growing count of claimed slots is the early warning sign of a leak.