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

357 lines
8.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BrainComponent.h"
#include "UObject/Package.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "VisualLogger/VisualLoggerTypes.h"
#include "VisualLogger/VisualLogger.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(BrainComponent)
const FName UBrainComponent::AIMessage_MoveFinished = TEXT("MoveFinished");
const FName UBrainComponent::AIMessage_RepathFailed = TEXT("RepathFailed");
const FName UBrainComponent::AIMessage_QueryFinished = TEXT("QueryFinished");
DEFINE_LOG_CATEGORY(LogBrain);
//////////////////////////////////////////////////////////////////////////
// Messages
static UBrainComponent* FindBrainComponentHelper(const AController* Controller)
{
return Controller ? Controller->FindComponentByClass<UBrainComponent>() : NULL;
}
static UBrainComponent* FindBrainComponentHelper(const APawn* Pawn)
{
if (Pawn == NULL)
{
return NULL;
}
UBrainComponent* Comp = NULL;
if (Pawn->GetController())
{
Comp = FindBrainComponentHelper(Pawn->GetController());
}
if (Pawn && Comp == NULL)
{
Comp = Pawn->FindComponentByClass<UBrainComponent>();
}
return Comp;
}
void FAIMessage::Send(AController* Controller, const FAIMessage& Message)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Controller);
Send(BrainComp, Message);
}
void FAIMessage::Send(APawn* Pawn, const FAIMessage& Message)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Pawn);
Send(BrainComp, Message);
}
void FAIMessage::Send(UBrainComponent* BrainComp, const FAIMessage& Message)
{
if (BrainComp)
{
BrainComp->HandleMessage(Message);
}
}
void FAIMessage::Broadcast(UObject* WorldContextObject, const FAIMessage& Message)
{
UWorld* MyWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (MyWorld)
{
for (FConstControllerIterator It = MyWorld->GetControllerIterator(); It; ++It)
{
FAIMessage::Send(It->Get(), Message);
}
}
}
FAIMessageObserver::FAIMessageObserver()
{
}
FAIMessageObserverHandle FAIMessageObserver::Create(AController* Controller, FName MessageType, FOnAIMessage const& Delegate)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Controller);
return Create(BrainComp, MessageType, Delegate);
}
FAIMessageObserverHandle FAIMessageObserver::Create(AController* Controller, FName MessageType, FAIRequestID MessageID, FOnAIMessage const& Delegate)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Controller);
return Create(BrainComp, MessageType, MessageID, Delegate);
}
FAIMessageObserverHandle FAIMessageObserver::Create(APawn* Pawn, FName MessageType, FOnAIMessage const& Delegate)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Pawn);
return Create(BrainComp, MessageType, Delegate);
}
FAIMessageObserverHandle FAIMessageObserver::Create(APawn* Pawn, FName MessageType, FAIRequestID MessageID, FOnAIMessage const& Delegate)
{
UBrainComponent* BrainComp = FindBrainComponentHelper(Pawn);
return Create(BrainComp, MessageType, MessageID, Delegate);
}
FAIMessageObserverHandle FAIMessageObserver::Create(UBrainComponent* BrainComp, FName MessageType, FOnAIMessage const& Delegate)
{
FAIMessageObserverHandle ObserverHandle;
if (BrainComp)
{
FAIMessageObserver* NewObserver = new FAIMessageObserver();
NewObserver->MessageType = MessageType;
NewObserver->bFilterByID = false;
NewObserver->ObserverDelegate = Delegate;
NewObserver->Register(BrainComp);
ObserverHandle = MakeShareable(NewObserver);
}
return ObserverHandle;
}
FAIMessageObserverHandle FAIMessageObserver::Create(UBrainComponent* BrainComp, FName MessageType, FAIRequestID MessageID, FOnAIMessage const& Delegate)
{
FAIMessageObserverHandle ObserverHandle;
if (BrainComp)
{
FAIMessageObserver* NewObserver = new FAIMessageObserver();
NewObserver->MessageType = MessageType;
NewObserver->MessageID = MessageID;
NewObserver->bFilterByID = true;
NewObserver->ObserverDelegate = Delegate;
NewObserver->Register(BrainComp);
ObserverHandle = MakeShareable(NewObserver);
}
return ObserverHandle;
}
FAIMessageObserver::~FAIMessageObserver()
{
Unregister();
}
void FAIMessageObserver::Register(UBrainComponent* OwnerComp)
{
OwnerComp->MessageObservers.Add(this);
Owner = OwnerComp;
}
void FAIMessageObserver::Unregister()
{
UBrainComponent* OwnerComp = Owner.Get();
if (OwnerComp)
{
OwnerComp->MessageObservers.RemoveSingle(this);
}
}
void FAIMessageObserver::OnMessage(const FAIMessage& Message)
{
if (Message.MessageName == MessageType)
{
if (!bFilterByID || Message.RequestID.IsEquivalent(MessageID))
{
ObserverDelegate.ExecuteIfBound(Owner.Get(), Message);
}
}
}
FString FAIMessageObserver::DescribeObservedMessage() const
{
FString Description = MessageType.ToString();
if (bFilterByID)
{
Description.AppendChar(TEXT(':'));
Description.AppendInt(MessageID.GetID());
}
return Description;
}
//////////////////////////////////////////////////////////////////////////
// Brain component
UBrainComponent::UBrainComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
bDoLogicRestartOnUnlock = false;
}
#if ENABLE_VISUAL_LOG
void UBrainComponent::DescribeSelfToVisLog(FVisualLogEntry* Snapshot) const
{
const static UEnum* PriorityEnum = StaticEnum<EAIRequestPriority::Type>();
if (!IsValidChecked(this))
{
return;
}
FVisualLogStatusCategory StatusCategory;
StatusCategory.Category = FString::Printf(TEXT("Resource lock: %s"), *ResourceLock.GetLockPriorityName());
for (int32 LockLevel = 0; LockLevel < int32(EAIRequestPriority::MAX); ++LockLevel)
{
StatusCategory.Add(*PriorityEnum->GetNameStringByValue(LockLevel), ResourceLock.IsLockedBy(EAIRequestPriority::Type(LockLevel)) ? TEXT("Locked") : TEXT("Unlocked"));
}
Snapshot->Status.Add(StatusCategory);
if (BlackboardComp)
{
BlackboardComp->DescribeSelfToVisLog(Snapshot);
}
}
#endif // ENABLE_VISUAL_LOG
void UBrainComponent::LockResource(EAIRequestPriority::Type LockSource)
{
const bool bWasLocked = ResourceLock.IsLocked();
ResourceLock.SetLock(LockSource);
if (bWasLocked == false)
{
PauseLogic(FString::Printf(TEXT("Locking Resource with source %s"), *ResourceLock.GetLockPriorityName()));
}
}
void UBrainComponent::ClearResourceLock(EAIRequestPriority::Type LockSource)
{
ResourceLock.ClearLock(LockSource);
if (ResourceLock.IsLocked() == false)
{
ResumeLogic(TEXT("unlocked"));
}
}
void UBrainComponent::ForceUnlockResource()
{
ResourceLock.ForceClearAllLocks();
ResumeLogic(TEXT("unlocked: forced"));
}
bool UBrainComponent::IsResourceLocked() const
{
return ResourceLock.IsLocked();
}
void UBrainComponent::OnRegister()
{
Super::OnRegister();
AIOwner = Cast<AAIController>(GetOwner());
}
void UBrainComponent::InitializeComponent()
{
Super::InitializeComponent();
// cache blackboard component if owner has one
// note that it's a valid scenario for this component to not have an owner at all (at least in terms of unittesting)
AActor* Owner = GetOwner();
if (Owner)
{
BlackboardComp = GetOwner()->FindComponentByClass<UBlackboardComponent>();
if (BlackboardComp)
{
BlackboardComp->CacheBrainComponent(*this);
}
}
}
void UBrainComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (MessagesToProcess.Num() > 0)
{
const int32 NumMessages = MessagesToProcess.Num();
for (int32 Idx = 0; Idx < NumMessages; Idx++)
{
// create a copy of message in case MessagesToProcess is changed during loop
const FAIMessage MessageCopy(MessagesToProcess[Idx]);
for (int32 ObserverIndex = 0; ObserverIndex < MessageObservers.Num(); ObserverIndex++)
{
MessageObservers[ObserverIndex]->OnMessage(MessageCopy);
}
}
MessagesToProcess.RemoveAt(0, NumMessages, EAllowShrinking::No);
}
}
void UBrainComponent::CacheBlackboardComponent(UBlackboardComponent* BBComp)
{
if (BBComp)
{
BlackboardComp = BBComp;
}
}
void UBrainComponent::HandleMessage(const FAIMessage& Message)
{
MessagesToProcess.Add(Message);
}
void UBrainComponent::RequestLogicRestartOnUnlock()
{
if (IsResourceLocked())
{
UE_VLOG(GetOwner(), LogBrain, Log, TEXT("Scheduling Logic Restart on next braing unlocking"));
bDoLogicRestartOnUnlock = true;
}
}
EAILogicResuming::Type UBrainComponent::ResumeLogic(const FString& Reason)
{
UE_VLOG(GetOwner(), LogBrain, Log, TEXT("Execution updates: RESUMED (%s)"), *Reason);
if (bDoLogicRestartOnUnlock == true)
{
bDoLogicRestartOnUnlock = false;
RestartLogic();
// let child implementations not to continue
return EAILogicResuming::RestartedInstead;
}
return EAILogicResuming::Continue;
}
void UBrainComponent::StartLogic()
{
}
void UBrainComponent::RestartLogic()
{
}
void UBrainComponent::StopLogic(const FString& Reason)
{
}
bool UBrainComponent::IsRunning() const
{
return false;
}
bool UBrainComponent::IsPaused() const
{
return false;
}