void UAISense_Sight::CleanseInvalidSources() { bool bInvalidSourcesFound = false; for (TMap<FName, FAISightTarget>::TIterator ItTarget(ObservedTargets); ItTarget; ++ItTarget) { if (ItTarget->Value.Target.IsValid() == false) { // remove affected queries RemoveAllQueriesToTarget(ItTarget->Key, DontSort); // remove target itself ItTarget.RemoveCurrent(); bInvalidSourcesFound = true; } } if (bInvalidSourcesFound) { // remove holes ObservedTargets.Compact(); SortQueries(); } else { UE_VLOG(GetPerceptionSystem(), LogAIPerception, Error, TEXT("UAISense_Sight::CleanseInvalidSources called and no invalid targets were found")); } }
void UAISense_Sight::UnregisterSource(AActor& SourceActor) { const FAISightTarget::FTargetId AsTargetId = SourceActor.GetFName(); FAISightTarget* AsTarget = ObservedTargets.Find(AsTargetId); if (AsTarget != nullptr) { RemoveAllQueriesToTarget(AsTargetId, DontSort); } }
void UAISense_Sight::OnListenerRemovedImpl(const FPerceptionListener& UpdatedListener) { RemoveAllQueriesByListener(UpdatedListener, DontSort); DigestedProperties.FindAndRemoveChecked(UpdatedListener.GetListenerID()); if (UpdatedListener.Listener.IsValid()) { // see if this listener is a Target as well const FAISightTarget::FTargetId AsTargetId = UpdatedListener.GetBodyActorName(); FAISightTarget* AsTarget = ObservedTargets.Find(AsTargetId); if (AsTarget != NULL) { RemoveAllQueriesToTarget(AsTargetId, Sort); } } else { //@todo quite possible there are left over sight queries with this listener as target } }
void UAISense_Sight::OnListenerUpdateImpl(const FPerceptionListener& UpdatedListener) { SCOPE_CYCLE_COUNTER(STAT_AI_Sense_Sight_ListenerUpdate); // first, naive implementation: // 1. remove all queries by this listener // 2. proceed as if it was a new listener // remove all queries RemoveAllQueriesByListener(UpdatedListener, DontSort); // see if this listener is a Target as well const FAISightTarget::FTargetId AsTargetId = UpdatedListener.GetBodyActorName(); FAISightTarget* AsTarget = ObservedTargets.Find(AsTargetId); if (AsTarget != NULL) { RemoveAllQueriesToTarget(AsTargetId, DontSort); if (AsTarget->Target.IsValid()) { RegisterTarget(*(AsTarget->Target.Get()), DontSort); } } const FPerceptionListenerID ListenerID = UpdatedListener.GetListenerID(); if (UpdatedListener.HasSense(GetSenseID())) { const UAISenseConfig_Sight* SenseConfig = Cast<const UAISenseConfig_Sight>(UpdatedListener.Listener->GetSenseConfig(GetSenseID())); check(SenseConfig); FDigestedSightProperties& PropertiesDigest = DigestedProperties.FindOrAdd(ListenerID); PropertiesDigest = FDigestedSightProperties(*SenseConfig); GenerateQueriesForListener(UpdatedListener, PropertiesDigest); } else { DigestedProperties.FindAndRemoveChecked(ListenerID); } }
float UAISense_Sight::Update() { static const FName NAME_AILineOfSight = FName(TEXT("AILineOfSight")); SCOPE_CYCLE_COUNTER(STAT_AI_Sense_Sight); const UWorld* World = GEngine->GetWorldFromContextObject(GetPerceptionSystem()->GetOuter()); if (World == NULL) { return SuspendNextUpdate; } int32 TracesCount = 0; static const int32 InitialInvalidItemsSize = 16; TArray<int32> InvalidQueries; TArray<FAISightTarget::FTargetId> InvalidTargets; InvalidQueries.Reserve(InitialInvalidItemsSize); InvalidTargets.Reserve(InitialInvalidItemsSize); AIPerception::FListenerMap& ListenersMap = *GetListeners(); FAISightQuery* SightQuery = SightQueryQueue.GetData(); for (int32 QueryIndex = 0; QueryIndex < SightQueryQueue.Num(); ++QueryIndex, ++SightQuery) { if (TracesCount < MaxTracesPerTick) { FPerceptionListener& Listener = ListenersMap[SightQuery->ObserverId]; ensure(Listener.Listener.IsValid()); FAISightTarget& Target = ObservedTargets[SightQuery->TargetId]; const bool bTargetValid = Target.Target.IsValid(); const bool bListenerValid = Listener.Listener.IsValid(); // @todo figure out what should we do if not valid if (bTargetValid && bListenerValid) { AActor* TargetActor = Target.Target.Get(); const FVector TargetLocation = TargetActor->GetActorLocation(); const FDigestedSightProperties& PropDigest = DigestedProperties[SightQuery->ObserverId]; const float SightRadiusSq = SightQuery->bLastResult ? PropDigest.LoseSightRadiusSq : PropDigest.SightRadiusSq; if (CheckIsTargetInSightPie(Listener, PropDigest, TargetLocation, SightRadiusSq)) { // UE_VLOG_SEGMENT(Listener.Listener.Get()->GetOwner(), Listener.CachedLocation, TargetLocation, FColor::Green, TEXT("%s"), *(Target.TargetId.ToString())); FVector OutSeenLocation(0.f); // do line checks if (Target.SightTargetInterface != NULL) { int32 NumberOfLoSChecksPerformed = 0; if (Target.SightTargetInterface->CanBeSeenFrom(Listener.CachedLocation, OutSeenLocation, NumberOfLoSChecksPerformed, Listener.Listener->GetBodyActor()) == true) { Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 1.f, OutSeenLocation, Listener.CachedLocation)); SightQuery->bLastResult = true; } else { // UE_VLOG_LOCATION(Listener.Listener.Get()->GetOwner(), TargetLocation, 25.f, FColor::Red, TEXT("")); Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, TargetLocation, Listener.CachedLocation, FAIStimulus::SensingFailed)); SightQuery->bLastResult = false; } TracesCount += NumberOfLoSChecksPerformed; } else { // we need to do tests ourselves /*const bool bHit = World->LineTraceTest(Listener.CachedLocation, TargetLocation , FCollisionQueryParams(NAME_AILineOfSight, true, Listener.Listener->GetBodyActor()) , FCollisionObjectQueryParams(ECC_WorldStatic));*/ FHitResult HitResult; const bool bHit = World->LineTraceSingle(HitResult, Listener.CachedLocation, TargetLocation , FCollisionQueryParams(NAME_AILineOfSight, true, Listener.Listener->GetBodyActor()) , FCollisionObjectQueryParams(ECC_WorldStatic)); ++TracesCount; if (bHit == false || (HitResult.Actor.IsValid() && HitResult.Actor->IsOwnedBy(TargetActor))) { Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 1.f, TargetLocation, Listener.CachedLocation)); SightQuery->bLastResult = true; } else { // UE_VLOG_LOCATION(Listener.Listener.Get()->GetOwner(), TargetLocation, 25.f, FColor::Red, TEXT("")); Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, TargetLocation, Listener.CachedLocation, FAIStimulus::SensingFailed)); SightQuery->bLastResult = false; } } } else { // UE_VLOG_SEGMENT(Listener.Listener.Get()->GetOwner(), Listener.CachedLocation, TargetLocation, FColor::Red, TEXT("%s"), *(Target.TargetId.ToString())); Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, TargetLocation, Listener.CachedLocation, FAIStimulus::SensingFailed)); SightQuery->bLastResult = false; } SightQuery->Importance = CalcQueryImportance(Listener, TargetLocation, SightRadiusSq); // restart query SightQuery->Age = 0.f; } else { // put this index to "to be removed" array InvalidQueries.Add(QueryIndex); if (bTargetValid == false) { InvalidTargets.AddUnique(SightQuery->TargetId); } } } else { // age unprocessed queries so that they can advance in the queue during next sort SightQuery->Age += 1.f; } SightQuery->RecalcScore(); } if (InvalidQueries.Num() > 0) { for (int32 Index = InvalidQueries.Num() - 1; Index >= 0; --Index) { // removing with swapping here, since queue is going to be sorted anyway SightQueryQueue.RemoveAtSwap(InvalidQueries[Index], 1, /*bAllowShrinking*/false); } if (InvalidTargets.Num() > 0) { for (const auto& TargetId : InvalidTargets) { // remove affected queries RemoveAllQueriesToTarget(TargetId, DontSort); // remove target itself ObservedTargets.Remove(TargetId); } // remove holes ObservedTargets.Compact(); } } // sort Sight Queries SortQueries(); //return SightQueryQueue.Num() > 0 ? 1.f/6 : FLT_MAX; return 0.f; }