I’m trying to retrieve an actor that has been sensed by an AIPerceptionComponen that has not expired yet. The AIPerceptionComponent is part of the AIController and the max age of the AI Sight Config has been set to 2 seconds for testing. The following code is run every 0.5 seconds:
TArray<AActor*> Actors;
AISensingComponent->GetPerceivedActors(UAISenseConfig_Sight::StaticClass(), Actors);
if (Actors.Num() < 1)
return;
const FActorPerceptionInfo* ActorInfo = AISensingComponent->GetActorInfo(*Actors[0]);
if (ActorInfo->LastSensedStimuli[0].IsExpired())
return;
GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Red, "I still know where you are!");
GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Red, FString::FromInt(ActorInfo->LastSensedStimuli[0].GetAge()));
However, the two messages are always shown on the screen once an Actor has been seen. The age also seems to go from 0 to 1, to -2147483648 where the last number seems to me some sort of indication that the stimuli has expired. This gives me the idea that IsExpired() might possibly be not working, any ideas?
Have you tried to use a gameplay debugger, and what is its visual output?
/** this means the stimulus was originally created with a "time limit" and this time has passed.
* Expiration also results in calling MarkNoLongerSensed */
uint32 bExpired:1;
What a result of the MarkNoLongerSensed()? Result = WasSuccessfullySensed()
bool UAIPerceptionComponent::AgeStimuli(const float ConstPerceptionAgingRate)
{
bool bExpiredStimuli = false;
for (TActorPerceptionContainer::TIterator It(PerceptualData); It; ++It)
{
FActorPerceptionInfo& ActorPerceptionInfo = It->Value;
for (FAIStimulus& Stimulus : ActorPerceptionInfo.LastSensedStimuli)
{
// Age the stimulus. If it is active but has just expired, mark it as such
if (Stimulus.AgeStimulus(ConstPerceptionAgingRate) == false
&& (Stimulus.IsActive() || Stimulus.WantsToNotifyOnlyOnPerceptionChange())
&& Stimulus.IsExpired() == false)
{
AActor* TargetActor = ActorPerceptionInfo.Target.Get();
if (TargetActor)
{
Stimulus.MarkExpired(); // try to debug there
RegisterStimulus(TargetActor, Stimulus);
bExpiredStimuli = true;
}
}
}
}
return bExpiredStimuli;
}
/** @return false when this stimulus is no longer valid, when it is Expired */
FORCEINLINE bool AgeStimulus(float ConstPerceptionAgingRate)
{
Age += ConstPerceptionAgingRate;
return Age < ExpirationAge;
}
At HandleExpiredStimulus bExpired was reseted to 0. Maybe it’s a bug. But the more it seems that this behavior is not provided. LastSensedStimuli[0] zero should be replaced with the ID of the sense FAIStimulus::Type. LastSensedStimuli can be rewritten, I think you can not rely on the data that is written in it, or they will have to check every tick.
if (PerceptualInfo->LastSensedStimuli.Num() <= SourcedStimulus->Stimulus.Type)
{
const int32 NumberToAdd = SourcedStimulus->Stimulus.Type - PerceptualInfo->LastSensedStimuli.Num() + 1;
for (int32 Index = 0; Index < NumberToAdd; ++Index)
{
PerceptualInfo->LastSensedStimuli.Add(FAIStimulus());
}
}
check(SourcedStimulus->Stimulus.Type.IsValid());
FAIStimulus& StimulusStore = PerceptualInfo->LastSensedStimuli[SourcedStimulus->Stimulus.Type];
I’m seeing this in 4.11 as well. I’m kind of lost on what the right answer was here though. My workaround so far is to check perception on my target every once in awhile because I’m not getting notified about expired Sight stimuli.
After digging into the source code, I have found that IsExpired() is actually set to true as soon as Age reaches MaxAge. However, the callback (OnPerceptionUpdated) does not get called at that time. So you have to handle expiration elsewhere. Luckily, there is one in AIPerceptionComponent!