🎮 AEnemyAIController.h
class UAIPerceptionComponent; class UAISenseConfig_Sight; UCLASS() class BOSSFIGHT_API AEnemyAIController : public AAIController { GENERATED_BODY() public: AEnemyAIController(); protected: virtual void BeginPlay() override; UFUNCTION() virtual void OnEnemyPerceptionUpdate(AActor* Actor, FAIStimulus Stimulus); UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UAIPerceptionComponent* EnemyPerceptionComponent; UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UAISenseConfig_Sight* AISenseConfig_Sight; };🎮 AEnemyAIController.cpp
AEnemyAIController::AEnemyAIController() { AISenseConfig_Sight = CreateDefaultSubobject<UAISenseConfig_Sight>("EnemySenseConfig_Sight"); AISenseConfig_Sight->DetectionByAffiliation.bDetectEnemies = true; AISenseConfig_Sight->DetectionByAffiliation.bDetectFriendlies = false; AISenseConfig_Sight->DetectionByAffiliation.bDetectNeutrals = false; AISenseConfig_Sight->SightRadius = 5000.0f; AISenseConfig_Sight->LoseSightRadius = 0.0f; AISenseConfig_Sight->PeripheralVisionAngleDegrees = 360.0f; EnemyPerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>("EnemyPerceptionComponent"); EnemyPerceptionComponent->ConfigureSense(*AISenseConfig_Sight); EnemyPerceptionComponent->SetDominantSense(UAISenseConfig_Sight::StaticClass()); EnemyPerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &ThisClass::OnEnemyPerceptionUpdate); } void AEnemyAIController::BeginPlay() { Super::BeginPlay(); } void AEnemyAIController::OnEnemyPerceptionUpdate(AActor* Actor, FAIStimulus Stimulus) { //TODO 플레이어 인식 }위와 같이 UAIPerceptionComponent을 정의 하고 필요한 정보를 기입 해주었다. 이를통해 플레이어를 인식 시킬려고 한다.
그러나 지금은 플레이어를 인식 하지 않는데, 왜냐하면 귀속 탐지 옵션이 현재는 "적대적" 인 객체만 인식 하는데, 플레이어가 현재 적대적인지 아닌지 확인하지 못하기 때문이다.
🎮 AEnemyAIController.h
UCLASS() class BOSSFIGHT_API AEnemyAIController : public AAIController { GENERATED_BODY() public: AEnemyAIController(); //GetTeamAttitudeTowards() 함수 추가 virtual ETeamAttitude::Type GetTeamAttitudeTowards(const AActor& Other) const override; // 중략 };🎮 AEnemyAIController.cpp
ETeamAttitude::Type AEnemyAIController::GetTeamAttitudeTowards(const AActor& Other) const { const APawn* PawnToCheck = Cast<const APawn>(&Other); const IGenericTeamAgentInterface* OtherTeamAgent = Cast<const IGenericTeamAgentInterface>(PawnToCheck->GetController()); if (OtherTeamAgent && OtherTeamAgent->GetGenericTeamId() < GetGenericTeamId()) { return ETeamAttitude::Hostile; } return ETeamAttitude::Friendly; }GetTeamAttitudeTowards()함수를 통해 AIController를 소유한 Pawn이 다른 Pawn과 적대적인지 확인 할 수 있게 되었다. 허나 이 기능을 사용하기 위에서는 PlayerController에도 GenericTeamId를 설정 해주어야 한다.
🎮 ABFPlayerController.h
UCLASS() class BOSSFIGHT_API ABFPlayerController : public APlayerController, public IGenericTeamAgentInterface { GENERATED_BODY() public: ABFPlayerController(); //~Begin IGenericTeamAgentInterface virtual FGenericTeamId GetGenericTeamId() const override; //~End IGenericTeamAgentInterface private: FGenericTeamId TeamID; };🎮 ABFPlayerController.cpp
ABFPlayerController::ABFPlayerController() { TeamID = FGenericTeamId(0); } FGenericTeamId ABFPlayerController::GetGenericTeamId() const { return TeamID; }PlayerController에 TeamID를 추가하고 생성자에서FGenericTeamId(0)로 초기화, GetGenericTeamId()함수에서 TeamID 를 반환 하도록 구현 하였다.

🎮 AEnemyAIController.cpp
void AEnemyAIController::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); if (IsValid(BehaviorTree)) { RunBehaviorTree(BehaviorTree); } } void AEnemyAIController::OnEnemyPerceptionUpdate(AActor* Actor, FAIStimulus Stimulus) { if (UBlackboardComponent* BlackboardComponent = GetBlackboardComponent()) { if (!BlackboardComponent->GetValueAsObject(TargetActor)) { if (Stimulus.WasSuccessfullySensed() && Actor) { BlackboardComponent->SetValueAsObject(TargetActor, Actor); } } } }OnPossess()함수를 오버라이드 하여 비헤이비어 트리를 작동 시키고, OnEnemyPerceptionUpdate를통해 Perception이 업데이트가 되면, 블랙보드의TargetActor값을 확인, nullptr이면 감지된 Actor를 TargetActor에 저장하도록 하였다.

🎮 AEnemyAIController.cpp
EBFEnemyAIState AEnemyAIController::GetAIState() { if (UBlackboardComponent* BlackboardComponent = GetBlackboardComponent()) { return EBFEnemyAIState(GetBlackboardComponent()->GetValueAsEnum(AIState)); } else { EBFEnemyAIState::None; } }
🎮 UBFEnemyAnimInstance.cpp
void UBFEnemyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds) { Super::NativeThreadSafeUpdateAnimation(DeltaSeconds); if (OwningEnemyCharacter) { bIsStrafing = (OwningAIController->GetAIState() == EBFEnemyAIState::Strafing); } }
