Files
UnrealEngine/Engine/Source/Runtime/AIModule/Private/Perception/AISense_Hearing.cpp
2025-05-18 13:04:45 +08:00

199 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Perception/AISense_Hearing.h"
#include "Perception/AIPerceptionSystem.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Hearing.h"
#include "Perception/AISenseEvent_Hearing.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AISense_Hearing)
//----------------------------------------------------------------------//
// FAINoiseEvent
//----------------------------------------------------------------------//
FAINoiseEvent::FAINoiseEvent()
: Age(0.f), NoiseLocation(FAISystem::InvalidLocation), Loudness(1.f), MaxRange(0.f)
, Instigator(nullptr), Tag(NAME_None), TeamIdentifier(FGenericTeamId::NoTeam)
{
}
FAINoiseEvent::FAINoiseEvent(AActor* InInstigator, const FVector& InNoiseLocation, float InLoudness, float InMaxRange, FName InTag)
: Age(0.f), NoiseLocation(InNoiseLocation), Loudness(InLoudness), MaxRange(InMaxRange)
, Instigator(InInstigator), Tag(InTag), TeamIdentifier(FGenericTeamId::NoTeam)
{
Compile();
}
void FAINoiseEvent::Compile()
{
TeamIdentifier = FGenericTeamId::GetTeamIdentifier(Instigator);
if (FAISystem::IsValidLocation(NoiseLocation) == false && Instigator != nullptr)
{
NoiseLocation = Instigator->GetActorLocation();
}
}
//----------------------------------------------------------------------//
// FDigestedHearingProperties
//----------------------------------------------------------------------//
UAISense_Hearing::FDigestedHearingProperties::FDigestedHearingProperties(const UAISenseConfig_Hearing& SenseConfig)
{
HearingRangeSq = FMath::Square(SenseConfig.HearingRange);
AffiliationFlags = SenseConfig.DetectionByAffiliation.GetAsFlags();
}
UAISense_Hearing::FDigestedHearingProperties::FDigestedHearingProperties()
: HearingRangeSq(-1.f)
{
AffiliationFlags = FAISenseAffiliationFilter::DetectAllFlags();
}
//----------------------------------------------------------------------//
// UAISense_Hearing
//----------------------------------------------------------------------//
UAISense_Hearing::UAISense_Hearing(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
OnNewListenerDelegate.BindUObject(this, &UAISense_Hearing::OnNewListenerImpl);
OnListenerUpdateDelegate.BindUObject(this, &UAISense_Hearing::OnListenerUpdateImpl);
OnListenerRemovedDelegate.BindUObject(this, &UAISense_Hearing::OnListenerRemovedImpl);
}
}
void UAISense_Hearing::PostInitProperties()
{
Super::PostInitProperties();
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
RegisterMakeNoiseDelegate();
}
}
void UAISense_Hearing::RegisterMakeNoiseDelegate()
{
AActor::SetMakeNoiseDelegate(FMakeNoiseDelegate::CreateStatic(&UAIPerceptionSystem::MakeNoiseImpl));
}
void UAISense_Hearing::ReportNoiseEvent(UObject* WorldContextObject, FVector NoiseLocation, float Loudness, AActor* Instigator, float MaxRange, FName Tag)
{
UAIPerceptionSystem* PerceptionSystem = UAIPerceptionSystem::GetCurrent(WorldContextObject);
if (PerceptionSystem)
{
FAINoiseEvent Event(Instigator, NoiseLocation, Loudness, MaxRange, Tag);
PerceptionSystem->OnEvent(Event);
}
}
void UAISense_Hearing::OnNewListenerImpl(const FPerceptionListener& NewListener)
{
UAIPerceptionComponent* ListenerPtr = NewListener.Listener.Get();
check(ListenerPtr);
const UAISenseConfig_Hearing* SenseConfig = Cast<const UAISenseConfig_Hearing>(ListenerPtr->GetSenseConfig(GetSenseID()));
check(SenseConfig);
const FDigestedHearingProperties PropertyDigest(*SenseConfig);
DigestedProperties.Add(NewListener.GetListenerID(), PropertyDigest);
}
void UAISense_Hearing::OnListenerUpdateImpl(const FPerceptionListener& UpdatedListener)
{
// @todo add updating code here
const FPerceptionListenerID ListenerID = UpdatedListener.GetListenerID();
if (UpdatedListener.HasSense(GetSenseID()))
{
const UAISenseConfig_Hearing* SenseConfig = Cast<const UAISenseConfig_Hearing>(UpdatedListener.Listener->GetSenseConfig(GetSenseID()));
check(SenseConfig);
FDigestedHearingProperties& PropertiesDigest = DigestedProperties.FindOrAdd(ListenerID);
PropertiesDigest = FDigestedHearingProperties(*SenseConfig);
}
else
{
DigestedProperties.Remove(ListenerID);
}
}
void UAISense_Hearing::OnListenerRemovedImpl(const FPerceptionListener& RemovedListener)
{
DigestedProperties.FindAndRemoveChecked(RemovedListener.GetListenerID());
}
float UAISense_Hearing::Update()
{
AIPerception::FListenerMap& ListenersMap = *GetListeners();
UAIPerceptionSystem* PerceptionSys = GetPerceptionSystem();
const float SpeedOfSoundSqScalar = SpeedOfSoundSq > 0.f ? 1.f / SpeedOfSoundSq : 0.f;
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;
}
const FDigestedHearingProperties& PropDigest = DigestedProperties[Listener.GetListenerID()];
for (const FAINoiseEvent& Event : NoiseEvents)
{
const float ClampedLoudness = FMath::Max(0.f, Event.Loudness);
const FVector::FReal DistToSoundSquared = FVector::DistSquared(Event.NoiseLocation, Listener.CachedLocation);
// Limit by loudness modified squared range (this is the old behavior)
if (DistToSoundSquared > PropDigest.HearingRangeSq * FMath::Square(ClampedLoudness))
{
continue;
}
// Limit by max range
else if (Event.MaxRange > 0.f && DistToSoundSquared > FMath::Square(Event.MaxRange * ClampedLoudness))
{
continue;
}
if (FAISenseAffiliationFilter::ShouldSenseTeam(Listener.TeamIdentifier, Event.TeamIdentifier, PropDigest.AffiliationFlags) == false)
{
continue;
}
// calculate delay and fake it with Age, Delay should be pretty small so a static_cast is safe enough here.
const float Delay = FloatCastChecked<float>(FMath::Sqrt(DistToSoundSquared * SpeedOfSoundSqScalar), UE::LWC::DefaultFloatPrecision);
// pass over to listener to process
PerceptionSys->RegisterDelayedStimulus(Listener.GetListenerID(), Delay, Event.Instigator
, FAIStimulus(*this, ClampedLoudness, Event.NoiseLocation, Listener.CachedLocation, FAIStimulus::SensingSucceeded, Event.Tag) );
}
}
NoiseEvents.Reset();
// return decides when next tick is going to happen
return SuspendNextUpdate;
}
void UAISense_Hearing::RegisterEvent(const FAINoiseEvent& Event)
{
NoiseEvents.Add(Event);
RequestImmediateUpdate();
}
void UAISense_Hearing::RegisterEventsBatch(const TArray<FAINoiseEvent>& Events)
{
NoiseEvents.Append(Events);
RequestImmediateUpdate();
}
void UAISense_Hearing::RegisterWrappedEvent(UAISenseEvent& PerceptionEvent)
{
UAISenseEvent_Hearing* HearingEvent = Cast<UAISenseEvent_Hearing>(&PerceptionEvent);
ensure(HearingEvent);
if (HearingEvent)
{
RegisterEvent(HearingEvent->GetNoiseEvent());
}
}