float UAISense_Touch::Update() { AIPerception::FListenerMap& ListenersMap = *GetListeners(); for (int32 EventIndex = 0; EventIndex < RegisteredEvents.Num(); ++EventIndex) { const FAITouchEvent& Event = RegisteredEvents[EventIndex]; if (Event.TouchReceiver != NULL && Event.OtherActor != NULL) { IAIPerceptionListenerInterface* PerceptionListener = Cast<IAIPerceptionListenerInterface>(Event.TouchReceiver); if (PerceptionListener != NULL) { UAIPerceptionComponent* PerceptionComponent = PerceptionListener->GetPerceptionComponent(); if (PerceptionComponent != NULL && ListenersMap.Contains(PerceptionComponent->GetListenerId())) { // this has to succeed, will assert a failure FPerceptionListener& Listener = ListenersMap[PerceptionComponent->GetListenerId()]; Listener.RegisterStimulus(Event.OtherActor, FAIStimulus(GetSenseIndex(), 1.f, Event.Location, Event.Location)); } } } } RegisteredEvents.Reset(); // return decides when next tick is going to happen return SuspendNextUpdate; }
float UAISense_Team::Update() { AIPerception::FListenerMap& ListenersMap = *GetListeners(); for (AIPerception::FListenerMap::TIterator ListenerIt(ListenersMap); ListenerIt; ++ListenerIt) { FPerceptionListener& Listener = ListenerIt->Value; if (Listener.HasSense(GetSenseID()) == false) { // skip listeners not interested in this sense continue; } for (int32 EventIndex = 0; EventIndex < RegisteredEvents.Num(); ++EventIndex) { const FAITeamStimulusEvent& Event = RegisteredEvents[EventIndex]; // @todo implement some kind of TeamIdentifierType that would supply comparison operator if (Listener.TeamIdentifier != Event.TeamIdentifier || FVector::DistSquared(Event.GetBroadcastLocation(), Listener.CachedLocation) > Event.RangeSq) { continue; } Listener.RegisterStimulus(Event.Enemy, FAIStimulus(*this, Event.Strength, Event.LastKnowLocation, Event.GetBroadcastLocation(), FAIStimulus::SensingSucceeded).SetStimulusAge(Event.InformationAge)); } } RegisteredEvents.Reset(); // return decides when next tick is going to happen return SuspendNextUpdate; }
float UAISense_Prediction::Update() { AIPerception::FListenerMap& ListenersMap = *GetListeners(); for (int32 EventIndex = 0; EventIndex < RegisteredEvents.Num(); ++EventIndex) { const FAIPredictionEvent& Event = RegisteredEvents[EventIndex]; if (Event.Requestor != NULL && Event.PredictedActor != NULL) { IAIPerceptionListenerInterface* PerceptionListener = Cast<IAIPerceptionListenerInterface>(Event.Requestor); if (PerceptionListener != NULL) { UAIPerceptionComponent* PerceptionComponent = PerceptionListener->GetPerceptionComponent(); if (PerceptionComponent != NULL && ListenersMap.Contains(PerceptionComponent->GetListenerId())) { // this has to succeed, will assert a failure FPerceptionListener& Listener = ListenersMap[PerceptionComponent->GetListenerId()]; // calculate the prediction here: const FVector PredictedLocation = Event.PredictedActor->GetActorLocation() + Event.PredictedActor->GetVelocity() * Event.TimeToPredict; Listener.RegisterStimulus(Event.PredictedActor, FAIStimulus(*this, 1.f, PredictedLocation, Listener.CachedLocation)); } } } } RegisteredEvents.Reset(); // return decides when next tick is going to happen return SuspendNextUpdate; }
float UAISense_Damage::Update() { AIPerception::FListenerMap& ListenersMap = *GetListeners(); for (int32 EventIndex = 0; EventIndex < RegisteredEvents.Num(); ++EventIndex) { const FAIDamageEvent& Event = RegisteredEvents[EventIndex]; IAIPerceptionListenerInterface* PerceptionListener = Cast<IAIPerceptionListenerInterface>(Event.DamagedActor); if (PerceptionListener != NULL) { UAIPerceptionComponent* PerceptionComponent = PerceptionListener->GetPerceptionComponent(); if (PerceptionComponent != NULL) { // this has to succeed, will assert a failure FPerceptionListener& Listener = ListenersMap[PerceptionComponent->GetListenerId()]; Listener.RegisterStimulus(Event.Instigator, FAIStimulus(GetSenseIndex(), Event.Amount, Event.Location, Event.HitLocation)); } } } RegisteredEvents.Reset(); // return decides when next tick is going to happen return SuspendNextUpdate; }
float UAISense_Aquaphobia::Update() { AIPerception::FListenerMap& ListenersMap = *GetListeners(); //For each listener who has this sense we're going to perform a sweep to determine nearby aqua actors for (auto& Elem : ListenersMap) { //Get the listener FPerceptionListener Listener = Elem.Value; const AActor* ListenerBodyActor = Listener.GetBodyActor(); for (int32 DigestedPropertyIndex = 0; DigestedPropertyIndex < DigestedProperties.Num(); DigestedPropertyIndex++) { //Create the sphere for this sense and perform the sweep to determine nearby actors FCollisionShape CollisionSphere = FCollisionShape::MakeSphere(DigestedProperties[DigestedPropertyIndex].PhobiaRadius); TArray<FHitResult> HitResults; GetWorld()->SweepMultiByChannel(HitResults, ListenerBodyActor->GetActorLocation(), ListenerBodyActor->GetActorLocation() + FVector::UpVector*CollisionSphere.GetSphereRadius(), FQuat(), ECollisionChannel::ECC_WorldDynamic, CollisionSphere); //Draw debug sphere if we have activated it via the config if (DigestedProperties[DigestedPropertyIndex].bDisplayDebugSphere) { DrawDebugSphere(GetWorld(), ListenerBodyActor->GetActorLocation(), DigestedProperties[DigestedPropertyIndex].PhobiaRadius, 8, FColor::Blue, false, 30.f, 1, 2.f); } //Check hit results for aqua actors for (int32 i = 0; i < HitResults.Num(); i++) { FHitResult hit = HitResults[i]; //To simplify things, we're going to assume that "water resources" for this post are actors that have the following game tag if (hit.GetActor()->ActorHasTag(FName("AquaActor"))) { if ((hit.GetActor()->GetActorLocation() - ListenerBodyActor->GetActorLocation()).Size() <= DigestedProperties[DigestedPropertyIndex].PhobiaRadius) { Elem.Value.RegisterStimulus(hit.GetActor(), FAIStimulus(*this, 5.f, hit.GetActor()->GetActorLocation(), ListenerBodyActor->GetActorLocation())); GLog->Log("registered stimulus!"); } } } } } //Time until next update; in this case we're forcing the update to happen in each frame return 0.f; }
void UAIPerceptionComponent::ProcessStimuli() { if(StimuliToProcess.Num() == 0) { UE_VLOG(GetOwner(), LogAIPerception, Warning, TEXT("UAIPerceptionComponent::ProcessStimuli called without any Stimuli to process")); return; } FStimulusToProcess* SourcedStimulus = StimuliToProcess.GetData(); TArray<AActor*> UpdatedActors; UpdatedActors.Reserve(StimuliToProcess.Num()); for (int32 StimulusIndex = 0; StimulusIndex < StimuliToProcess.Num(); ++StimulusIndex, ++SourcedStimulus) { FActorPerceptionInfo* PerceptualInfo = PerceptualData.Find(SourcedStimulus->Source); if (PerceptualInfo == NULL) { if (SourcedStimulus->Stimulus.WasSuccessfullySensed() == false) { // this means it's a failed perception of an actor our owner is not aware of // at all so there's no point in creating perceptual data for a failed stimulus continue; } else { // create an entry PerceptualInfo = &PerceptualData.Add(SourcedStimulus->Source, FActorPerceptionInfo(SourcedStimulus->Source)); // tell it what's our dominant sense PerceptualInfo->DominantSense = DominantSenseID; PerceptualInfo->bIsHostile = AIOwner != NULL && FGenericTeamId::GetAttitude(AIOwner, SourcedStimulus->Source) == ETeamAttitude::Hostile; } } 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]; // if the new stimulus is "valid" or it's info that "no longer sensed" and it used to be sensed successfully if (SourcedStimulus->Stimulus.WasSuccessfullySensed() || StimulusStore.WasSuccessfullySensed()) { UpdatedActors.AddUnique(SourcedStimulus->Source); } if (SourcedStimulus->Stimulus.WasSuccessfullySensed()) { RefreshStimulus(StimulusStore, SourcedStimulus->Stimulus); } else if (StimulusStore.IsExpired()) { HandleExpiredStimulus(StimulusStore); } else { // @note there some more valid info in SourcedStimulus->Stimulus regarding test that failed // may be useful in future StimulusStore.MarkNoLongerSensed(); } } StimuliToProcess.Reset(); if (UpdatedActors.Num() > 0) { if (AIOwner != NULL) { AIOwner->ActorsPerceptionUpdated(UpdatedActors); } OnPerceptionUpdated.Broadcast(UpdatedActors); } }
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; }