AIController를 이용해서 AI 형태의 캐릭터를 만들수 있다
가장 먼저 Character C++ 클래스를 만들겠습니다.

BehaviorTree AI를 사용하기 위해 헤더와 class를 선언한다
CEnemy.h
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BehaviorTree.h"
#include "GameFramework/Character.h"
#include "CEnemy.generated.h"
UCLASS()
class TEST_PROJECT_PLAYER_API ACEnemy : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
ACEnemy();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UBehaviorTree* GetBeHaviorTree() const;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
class UBehaviorTree* Tree;
};
CEnemy.cpp
#include "CEnemy.h"
// Sets default values
ACEnemy::ACEnemy()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
}
// Called when the game starts or when spawned
void ACEnemy::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ACEnemy::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void ACEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
UBehaviorTree* ACEnemy::GetBeHaviorTree() const
{
return Tree;
}
~.Bulid.cs
using UnrealBuildTool;
public class TEST_Project_Player : ModuleRules
{
public TEST_Project_Player(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"EnhancedInput",
"AIModule",
"GameplayTasks",
"NavigationSystem"
});
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}

CAIControllerEnemy.h
#pragma once
#include "CoreMinimal.h"
#include "AIController.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;
};
CAIControllerEnemy.cpp
#include "CAIControllerEnemy.h"
#include "CEnemy.h"
ACAIControllerEnemy::ACAIControllerEnemy(FObjectInitializer const& objectInitializer)
{
}
void ACAIControllerEnemy::OnPossess(APawn* Inpawn)
{
Super::OnPossess(Inpawn);
if (ACEnemy* const enemy = Cast<ACEnemy>(Inpawn))
{
if (UBehaviorTree* const tree = enemy->GetBeHaviorTree())
{
UBlackboardComponent* b;
UseBlackboard(tree->BlackboardAsset, b);
Blackboard = b;
RunBehaviorTree(tree);
}
}
}
OnPossess 함수
void ACAIControllerEnemy::OnPossess(APawn* Inpawn)
{
Super::OnPossess(Inpawn);
}
AI가 적 캐릭터를 제어하기 시작하는 시점입니다. ACEnemy로 캐스팅
if (ACEnemy* const enemy = Cast<ACEnemy>(Inpawn))
Behavior Tree와 Blackboard 설정
if (UBehaviorTree* const tree = enemy->GetBeHaviorTree())
{
UBlackboardComponent* b;
UseBlackboard(tree->BlackboardAsset, b);
Blackboard = b;
RunBehaviorTree(tree);
}






UBTTask_BlackboardBase형태의 C++ 클래스를 하나 만든다
이 코드는 BTTask_FindRandomLocation이라는 클래스를 구현한 것으로, 언리얼 엔진에서 AI 행동 트리(Task) 노드로 사용됩니다.
이 클래스는 UBTTask_BlackboardBase를 상속받으며, AI가 네비게이션 메쉬(NavMesh) 내에서 무작위 위치를 찾아 블랙보드에 저장하는 역할
BTTask_FindRandomLocation.h
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
#include "BTTask_FindRandomLocation.generated.h"
/**
*
*/
UCLASS()
class TEST_PROJECT_PLAYER_API UBTTask_FindRandomLocation : public UBTTask_BlackboardBase
{
GENERATED_BODY()
public:
explicit UBTTask_FindRandomLocation(FObjectInitializer const& ObjectInitializer);
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI", meta = (AllowPrivateAccess="true"))
float SearchRadius = 1500.0f;
};
BTTask_FindRandomLocation.cpp
#include "BTTask_FindRandomLocation.h"
#include "NavigationSystem.h"
#include "CAIControllerEnemy.h"
#include "BehaviorTree/BlackboardComponent.h"
UBTTask_FindRandomLocation::UBTTask_FindRandomLocation(FObjectInitializer const& ObjectInitializer)
{
NodeName = "Find Random Location In NavMesh";
}
EBTNodeResult::Type UBTTask_FindRandomLocation::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
// get AI Controller and its enemy
if (auto* const cont = Cast<ACAIControllerEnemy>(OwnerComp.GetAIOwner()))
{
if (auto* const enemy = cont->GetPawn())
{
// obtain enemy location to use as an origin
auto const Origin = enemy->GetActorLocation();
// get the navigation system and generate a random locaiton
if (auto* const NavSys = UNavigationSystemV1::GetCurrent(GetWorld()))
{
FNavLocation Loc;
if (NavSys->GetRandomPointInNavigableRadius(Origin, SearchRadius, Loc))
{
OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), Loc.Location);
}
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
return EBTNodeResult::Succeeded;
}
}
}
return Super::ExecuteTask(OwnerComp, NodeMemory);
}
UBTTask_FindRandomLocation 생성자
NodeName = "Find Random Location In NavMesh";
생성자에서 Task의 이름을 "Find Random Location In NavMesh"로 설정합니다. 이 이름은 Behavior Tree 에디터에서 노드를 시각적으로 구분하는 데 사용됩니다.
ExecuteTask 함수
Behavior Tree에서 이 Task가 실행될 때 호출되는 함수
EBTNodeResult::Type UBTTask_FindRandomLocation::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
AI 컨트롤러 캐스팅if (auto* const cont = Cast<ACAIControllerEnemy>(OwnerComp.GetAIOwner()))적(Pawn) 가져오기if (auto* const enemy = cont->GetPawn())네비게이션 시스템에서 무작위 위치 생성if (auto* const NavSys = UNavigationSystemV1::GetCurrent(GetWorld()))블랙보드에 위치 저장OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), Loc.Location);작업 완료 처리FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
return EBTNodeResult::Succeeded; - 작업이 성공적으로 완료되었음을 알리고, EBTNodeResult::Succeeded를 반환하여 Behavior Tree에서 다음 노드로 이동하도록 합니다.





