
BTTask_FindPlayerLocation.h
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
#include "BTTask_FindPlayerLocation.generated.h"
UCLASS()
class TEST_PROJECT_PLAYER_API UBTTask_FindPlayerLocation : public UBTTask_BlackboardBase
{
GENERATED_BODY()
public:
explicit UBTTask_FindPlayerLocation(FObjectInitializer const& ObjectInitializer);
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Search", meta = (AllowPrivateAccess = "true"))
bool SearchRandom = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Search", meta = (AllowPrivateAccess = "true"))
float SearchRadius = 250.0f;
};
explicit UBTTask_FindPlayerLocation(FObjectInitializer const& ObjectInitializer);
- 생성자 선언입니다. FObjectInitializer를 매개변수로 받아, 부모 클래스의 초기화 로직을 전달.
- explicit 키워드는 암시적 변환을 방지하여, 인스턴스를 생성할 때 명시적으로 호출되도록 합니다.
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
- AI의 동작을 정의하는 로직을 여기에 작성
BTTask_FindPlayerLocation.cpp
#include "BTTask_FindPlayerLocation.h"
#include "NavigationSystem.h"
#include "BehaviorTree\BlackboardComponent.h"
#include "GameFramework\Character.h"
#include "Kismet\GameplayStatics.h"
UBTTask_FindPlayerLocation::UBTTask_FindPlayerLocation(FObjectInitializer const& ObjectInitializer)
{
NodeName = TEXT("Find Player Location");
}
EBTNodeResult::Type UBTTask_FindPlayerLocation::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
// get player character
if (auto* const Player = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))
{
// get player location to use as an origin
auto const PlayerLocation = Player->GetActorLocation();
if (SearchRandom)
{
FNavLocation Loc;
// get the navigation system and generate a random location near the player
if (auto* const Navsys = UNavigationSystemV1::GetCurrent(GetWorld()))
{
if (Navsys->GetRandomPointInNavigableRadius(PlayerLocation, SearchRadius, Loc))
{
OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), Loc.Location);
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
return EBTNodeResult::Succeeded;
}
}
}
else
{
OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), PlayerLocation);
return EBTNodeResult::Succeeded;
}
}
return EBTNodeResult::Failed;
}
UBTTask_FindPlayerLocation::UBTTask_FindPlayerLocation(FObjectInitializer const& ObjectInitializer) { NodeName = TEXT("Find Player Location"); }
- Behavior Tree Task의 이름을 설정
if (auto* const Player = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))
- 게임 월드에서 인덱스 0에 해당하는 플레이어 캐릭터를 가져옵니다.
FNavLocation Loc; // get the navigation system and generate a random location near the player if (auto* const Navsys = UNavigationSystemV1::GetCurrent(GetWorld())) { if (Navsys->GetRandomPointInNavigableRadius(PlayerLocation, SearchRadius, Loc)) { OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), Loc.Location); FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); return EBTNodeResult::Succeeded; } }
Navsys->GetRandomPointInNavigableRadius: 내비게이션 시스템을 사용해, 플레이어의 위치를 중심으로 SearchRadius 반경 내에서 이동 가능한 무작위 위치를 생성합니다. 이 위치는 Loc 변수에 저장됩니다.OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), Loc.Location): 생성된 무작위 위치를 블랙보드에 저장합니다.GetSelectedBlackboardKey()는 블랙보드에서 설정된 키를 반환하고,SetValueAsVector()는 해당 키에 좌표 값을 할당
OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), PlayerLocation); return EBTNodeResult::Succeeded;
- 플레이어의 위치를 블랙보드에 저장
ACAIControllerEnemy.h
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AIPerceptionTypes.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "CAIControllerEnemy.generated.h"
UCLASS()
class TEST_PROJECT_PLAYER_API ACAIControllerEnemy : public AAIController
{
GENERATED_BODY()
public:
explicit ACAIControllerEnemy(FObjectInitializer const& objectInitializer);
protected:
virtual void OnPossess(APawn* Inpawn) override;
private :
class UAISenseConfig_Sight* SightConfig;
void SetupPerceptionSystem();
UFUNCTION()
void OnTargetDetected(AActor* Actor, FAIStimulus const Stimulus);
};
ACAIControllerEnemy.cpp
#include "CAIControllerEnemy.h"
#include "CEnemy.h"
#include "Perception\AIPerceptionComponent.h"
#include "Perception\AISenseConfig_Sight.h"
#include "CTPSPlayer.h"
ACAIControllerEnemy::ACAIControllerEnemy(FObjectInitializer const& objectInitializer)
{
SetupPerceptionSystem();
}
void ACAIControllerEnemy::OnPossess(APawn* Inpawn)
{
... 생략
}
void ACAIControllerEnemy::SetupPerceptionSystem()
{
SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("Sight Config"));
if (SightConfig)
{
SetPerceptionComponent(*CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("Perception Comp")));
SightConfig->SightRadius = 500.0f;
SightConfig->LoseSightRadius = SightConfig->SightRadius + 25.0f;
SightConfig->PeripheralVisionAngleDegrees = 90.0f;
SightConfig->SetMaxAge(5.0f);
SightConfig->AutoSuccessRangeFromLastSeenLocation = 520.0f;
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
GetPerceptionComponent()->SetDominantSense(*SightConfig->GetSenseImplementation());
GetPerceptionComponent()->OnTargetPerceptionUpdated.AddDynamic(this, &ACAIControllerEnemy::OnTargetDetected);
GetPerceptionComponent()->ConfigureSense((*SightConfig));
}
}
void ACAIControllerEnemy::OnTargetDetected(AActor* Actor, FAIStimulus const Stimulus)
{
if (auto* const ch = Cast<ACTPSPlayer>(Actor))
{
GetBlackboardComponent()->SetValueAsBool("CanSeePlayer", Stimulus.WasSuccessfullySensed());
}
}
SetPerceptionComponent(*CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("Perception Comp")));
- I 지각 컴포넌트를 생성하고, AI 컨트롤러에 설정
- 이 컴포넌트는 AI의 모든 감각을 통합하고 관리하는 역할
SightConfig->SightRadius = 500.0f; SightConfig->LoseSightRadius = SightConfig->SightRadius + 25.0f; SightConfig->PeripheralVisionAngleDegrees = 90.0f; SightConfig->SetMaxAge(5.0f); SightConfig->AutoSuccessRangeFromLastSeenLocation = 520.0f;
- SightRadius
- AI가 감지할 수 있는 시각적 범위(반지름)입니다. 이 값은 500.0f로 설정되어 있어, AI는 이 범위 내에서 객체를 감지할 수 있습니다.
- LoseSightRadius
- AI가 시야에서 대상이 벗어나는 반경입니다. 이 값은 SightRadius에 25.0f를 더한 값으로, 대상이 시야에서 완전히 벗어나기 전까지 AI가 인식할 수 있는 최대 범위입니다.
- PeripheralVisionAngleDegrees
- AI의 주변 시야 각도입니다. 이 값은 90도이며, AI는 자신의 정면을 중심으로 90도 범위 내에서만 시각적으로 인식할 수 있습니다.
- SetMaxAge
- AI가 마지막으로 본 대상을 기억하는 시간(5초)입니다. 이 시간이 지나면 AI는 대상을 기억하지 못하게 됩니다.
- AutoSuccessRangeFromLastSeenLocation
- AI가 마지막으로 본 위치로부터 520.0f 범위 내에서는 대상이 시야에서 벗어나도 여전히 인식 성공으로 간주됩니다.
ACTPSPlayer.h
private:
class UAIPerceptionStimuliSourceComponent* StimulusSource;
void SetupStimulusSource();
ACTPSPlayer.cpp
#include "Perception\AIPerceptionStimuliSourceComponent.h"
#include "Perception\AISense_Sight.h"
ACTPSPlayer::ACTPSPlayer()
{
... 생략
SetupStimulusSource();
}
... 생략
void ACTPSPlayer::SetupStimulusSource()
{
StimulusSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimulus"));
if (StimulusSource)
{
StimulusSource->RegisterForSense(TSubclassOf<UAISense_Sight>());
StimulusSource->RegisterWithPerceptionSystem();
}
}
StimulusSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimulus"));
- StimulusSource는 UAIPerceptionStimuliSourceComponent 타입의 컴포넌트로, AI가 감지할 수 있는 자극(Stimuli)을 제공하는 역할
- CreateDefaultSubobject는 이 자극 소스를 생성하고, "Stimulus"라는 이름을 부여
- 이렇게 생성된 자극 소스 컴포넌트는 나중에 플레이어의 행동을 AI가 감지할 수 있도록 하는 중요한 역할
StimulusSource->RegisterForSense(TSubclassOf<UAISense_Sight>()); StimulusSource->RegisterWithPerceptionSystem();
- RegisterForSense는 자극 소스를 AI의 특정 감각(AISense)에 등록하는 함수
- 이 코드에서는 UAISense_Sight, 즉 시각 감각에 플레이어를 등록하고 있습니다. 이를 통해 AI는 플레이어를 시각적으로 감지할 수 있게 됩니다.
- RegisterWithPerceptionSystem은 자극 소스를 언리얼 엔진의 AI 지각 시스템(Perception System)에 등록하는 함수
- 이를 통해 플레이어 캐릭터가 AI에게 인식될 수 있는 자극 소스로 시스템에 추가






추가한 Blackboardkey의 값을 Blackboard에서 선언한 bool 값으로 구분 짓는다
Patrolling = Is Not Set

Chasing Player = Is Set


BTTask_ChasePlayer.h
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
#include "BTTask_ChasePlayer.generated.h"
UCLASS()
class TEST_PROJECT_PLAYER_API UBTTask_ChasePlayer : public UBTTask_BlackboardBase
{
GENERATED_BODY()
public:
explicit UBTTask_ChasePlayer(FObjectInitializer const& ObjectInitializer);
EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
};
BTTask_ChasePlayer.cpp
#include "BTTask_ChasePlayer.h"
#include "CAIControllerEnemy.h"
#include "BehaviorTree\BlackboardComponent.h"
#include "Blueprint\AIBlueprintHelperLibrary.h"
UBTTask_ChasePlayer::UBTTask_ChasePlayer(FObjectInitializer const& ObjectInitializer)
{
NodeName = TEXT("Chase Player");
}
EBTNodeResult::Type UBTTask_ChasePlayer::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
// get target location from blackboard via the Enemy Controller
if (auto* const cont = Cast<ACAIControllerEnemy>(OwnerComp.GetAIOwner()))
{
auto const PlayerLocation = OwnerComp.GetBlackboardComponent()->GetValueAsVector(GetSelectedBlackboardKey());
// move to the Player's location
UAIBlueprintHelperLibrary::SimpleMoveToLocation(cont, PlayerLocation);
// finish with success
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
return EBTNodeResult::Succeeded;
}
return EBTNodeResult::Failed;
}

