In this lesson, you will learn how to implement state caching to bypass redundant computations, ensuring your AI logic only updates when strictly necessary.
The Problem of "Every-Tick" Evaluation
In complex AI, you often have decorators or C++ tick functions evaluating expensive logic, such as pathfinding feasibility or line-of-sight traces, every single frame. When you have dozens of NPCs, this creates massive CPU overhead. Even if the AI’s "state" hasn't fundamentally changed, the logic keeps running. State caching is the solution: you store the result of a complex calculation and only re-run it after a set duration or when a specific state change trigger occurs.
Implementing a Cached Decorator Pattern
Instead of running a logic check inside a TickNode, you can store the result in a local variable or a Blackboard Key and add an "expiration timer."
Consider this logic for a C++ custom decorator:
bool UBTDecorator_CachedCheck::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
FBTDecoratorMemory* MyMemory = CastMemory<FBTDecoratorMemory>(NodeMemory);
// Only re-run the expensive logic if the cache has expired
if (GetWorld()->GetTimeSeconds() > MyMemory->LastCheckTime + CacheDuration)
{
MyMemory->CachedResult = PerformExpensiveCheck();
MyMemory->LastCheckTime = GetWorld()->GetTimeSeconds();
}
return MyMemory->CachedResult;
}
Strategic Benefits
- Temporal Batching: By setting a
CacheDuration(e.g., 0.5 seconds), you effectively reduce the CPU cost of that specific logic by 90% or more. - State-Driven Invalidations: Rather than relying solely on timers, you can use an event-driven approach. When a Blackboard key changes (e.g.,
TargetActorbecomes invalid), you can manually clear theLastCheckTimein your node’s memory. This forces an immediate re-evaluation only when the AI's "worldview" changes. - Stability: Caching prevents "flickering" decisions, where an AI oscillates between two states because a trace returns a borderline result every few frames.
By decoupling the request for a decision from the execution of the calculation, you create a responsive AI system that scales gracefully as your population grows.
Key Takeaway: Never compute an AI decision more often than the game state requires; use timers and event-driven triggers to cache logic results and slash your CPU overhead.