[Unreal/C++] 인공지능 AIController 구성하기
언리얼 엔진에서 AIController를 구성하는 방법을 소개합니다.
Unreal 엔진 공식 문서에서는 AIController를 다음과 같이 소개합니다.
PlayerController 가 사람 플레이어의 의사 결정을 통해 할 일을 결정하는 반면, AIController 는 환경과 게임 월드에서의 입력에 반응하는 데 초점을 맞추고 있습니다. AIController 의 임무는 사람 플레이어의 명시적인 입력 없이 주변 월드를 관찰하고 의사를 결정한 뒤 알맞게 반응하는 것입니다.
언리얼 엔진에서 소개된 것처럼 플레이어의 명시적인 입력 없이 주변 월드를 관찰하고 의사를 결정한 뒤 반응하게 만들 수 있습니다. 하지만 구체적이지 않고 추상적인 설명은 직접 구현해보고 나서야 이해하게 됩니다.
대부분 AIController를 구현하는 이유는 언리얼 엔진에서 구현되어 있는 시스템을 이용하기 위함입니다.
AIController와 함께 자주 사용되는 것들은 다음과 같습니다.
1. Perception
2. AI Sense
3. Behavior Tree
4. Blackboard
Perception(지각)은 AI Sense(감각)을 사용합니다.
우리가 현실에서 사물을 보고 소리를 들을 수 있는 것처럼 언리얼 엔진에서는 시각, 청각 기능을 AI Sense로 구현해놨습니다. 감각을 통해 전달받은 정보는 지각을 통해 처리되고 있습니다.
처리된 정보는 Blackboard에 기억해놓고 Behavior Tree를 통해 어떤 행동을 할지 정의할 수 있습니다.
개념적인 부분은 여기서 마치고 AIController의 처리를 확인해 보며 클래스를 작성하겠습니다.
AIController를 상속받아 구현하기 전에 어떻게 구현되어 있는지 확인할 필요가 있습니다.
우리가 대체로 필요로 하는 PerceptionComponent와 Blackboard가 선언되어 있고
Actor의 초기화?가 마무리된 시점 PostInitializeComponents와 PostRegisterAllComponents 함수 안에서는 생성이 안되어 있을 경우에 기본 클래스를 지정해줍니다.
AI Controller가 배치, 생성된 폰에 빙의 되었을 때나 빙의가 해제되었을 때 또한 구현되어 있습니다.
그리고 Behavior Tree를 동작할 수 있는 함수도 포함되어 있습니다.
RunBehaviorTree함수는 BehaviorTree를 받아 등록된 Blackboard데이터를 사용하고 행동 트리를 시작합니다.
기본 AI Controller 에서는 필요한 동작을 대부분 구현해 놓았습니다.
하지만 Perception에 감각을 등록하고 정보를 받아오는 동작은 구현할 필요가 있습니다.
UAIPerceptionComponent 클래스를 살펴보면 ProcessStimuli 함수에서 감각들을 체크하고 Delegate로 뿌려주는 것을 볼 수 있습니다.
이제 설명한 내용을 토대로 AIController를 구현할 수 있습니다.
AI Controller를 상속받아 AIControllerBase를 생성합니다.
기본적으로 오버라이딩 하여 구현해야할 함수와 생성할 변수는 다음과 같습니다.
1. OnPossess(APawn* InPawn);
2. OnUnPossess();
3. OnPerceptionUpdate(const TArray<AActor*>& UpdateActors); // (OnPerceptionUpdated Delegate)
----------------------------------------------------------------------------------
4. TObjectPtr<UAISenseConfig_Sight> Sight; //(시야 감각)
// AAIControllerBase.h
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "AIControllerBase.generated.h"
class UBehaviorTreeComponent;
class UAISenseConfig_Sight;
class AEnemyBase;
UCLASS()
class MULTIPLAYER_API AAIControllerBase : public AAIController
{
GENERATED_BODY()
public :
AAIControllerBase();
protected :
virtual void BeginPlay() override; // 사용하지 않음
virtual void Tick(float DeltaTime) override; // 사용하지 않음
protected:
//~ Begin AController Interface
/* Pawn 에 빙의 되었을 경우 */
virtual void OnPossess(APawn* InPawn) override;
/* 빙의가 해제 되었을 경우 */
virtual void OnUnPossess() override;
//~ End AController Interface
private:
/*
* PerceptionComponent -> OnPerceptionUpdated Delegate
* AI Sense에 감지되거나 감지 되지 않을 때 발생
* 감지된 오브젝트를 추가
*/
UFUNCTION()
void OnPerceptionUpdated(const TArray<AActor*>& UpdateActors);
protected:
/* AISenseConfig_Sight 감지 클래스 */
TObjectPtr<UAISenseConfig_Sight> Sight;
};
헤더 파일이 준비되면 cpp 파일을 작성합니다.
이해를 돕기 위해 분해하여 설명합니다.
Line별 설명
14 : UAIPerceptionComponent를 생성합니다. AIController::PostRegisterAllComponents 함수에서 기본 클래스를 지정해주지만 감각을 등록하는 과정에서 필요하기 때문에 생성해주었습니다. 생성자에서 설정하는 것이 아니라 PostRegisterAllComponents를 오버라이딩하여 설정한다면 미리 생성해줄 필요는 없습니다. (Perception을 따로 구현한 것이 아니라면)
15 : Perception에 등록할 시야 감각을 생성합니다.
17 : 감지 범위를 지정합니다.
18 : 감지된 대상을 잃어버리는 범위를 지정합니다.
19 : 시야각을 설정합니다.
22~24 : 아군, 적군, 중립 대상을 감지할지 설정합니다. TeamID에 관한 것은 글 하단 링크를 참조바랍니다.
26 : 감각을 등록합니다. 감각은 TArray로 관리되고 있기 때문에 추가적인 감각 등록이 가능합니다.
28 : 주요감각을 설정합니다. 이는 다른 감각들 보다 우선적으로 처리됩니다.
다음은 OnPossess와 OnUnPossess입니다.
56 : OwnerEnemy의 경우 빙의될 Pawn 개체입니다. 이는 개별적으로 대상 클래스를 생성해야 합니다.
60 : OwnerEnemy에는 TObjectPtr<UBehaviorTree> 를 가지고 있어야 합니다.
58 : PerceptionComponent의 Perception이 업데이트 되었을 때 수신받을 대상을 지정합니다. (Delegate)
마지막 처리 부분입니다.
Perception 업데이트에서는 시야나 청각에 의해 감지된 Actor들을 받아 처리할 수 있습니다.
예를 들어 플레이어나 다른 개체들이 탐지되었을 때 플레이어만 뽑아내어 Blackboard에 설정하거나 Target 설정 등을 할 수 있습니다. 대부분 Cast<PlayerCharacter>를 통하여 구별해내고 Blackboard->SetValueAsObject 등으로 블랙보드에 설정할 수 있습니다.
// AIControllerBase.cpp
#include "GameSetting/Controllers/AIControllerBase.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Sight.h"
#include "Characters/Enemies/EnemyBase.h"
AAIControllerBase::AAIControllerBase()
{
PerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("Perception"));
Sight = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("Sight"));
Sight->SightRadius = 600; // 감지 범위
Sight->LoseSightRadius = 800; // 벗어난 범위
Sight->PeripheralVisionAngleDegrees = 90; //시야각
// TeamID에 의해 결정
Sight->DetectionByAffiliation.bDetectEnemies = true; // 적
Sight->DetectionByAffiliation.bDetectNeutrals = false; // 중립
Sight->DetectionByAffiliation.bDetectFriendlies = true; // 아군
PerceptionComponent->ConfigureSense(*Sight); // 감지
// AI 지각 컴포넌트에 시각 감각을 주요 감각으로 설정
PerceptionComponent->SetDominantSense(*Sight->GetSenseImplementation());
}
void AAIControllerBase::BeginPlay()
{
Super::BeginPlay();
}
void AAIControllerBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AAIControllerBase::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
OwnerEnemy = Cast<AEnemyBase>(InPawn);
RunBehaviorTree(OwnerEnemy->GetBehaviorTree());
PerceptionComponent->OnPerceptionUpdated.AddDynamic(this, &AAIControllerBase::OnPerceptionUpdated);
}
void AAIControllerBase::OnUnPossess()
{
Super::OnUnPossess();
PerceptionComponent->OnPerceptionUpdated.Clear();
}
void AAIControllerBase::OnPerceptionUpdated(const TArray<AActor*>& UpdateActors)
{
// 처리
}
콘텐츠 폴더에서 비헤이비어 트리와 블랙보드를 생성하고 블랙보드를 설정합니다.
Enemy = AIController가 빙의될 대상은 BehaviorTree를 가지고 있어야 합니다.
'Unreal > Manual' 카테고리의 다른 글
Unreal BehaviorTree에서 Random Selector Composite 만들기 (0) | 2024.02.14 |
---|---|
Unreal Animation Instance, 캐릭터의 속도와 방향 구하기 (1) | 2024.02.08 |
Unreal 아군, 적군, 중립 구별 TeamID 설정하기 (0) | 2024.02.06 |
Unreal EQS(Environment Query System) 사용하기 (1) | 2024.02.06 |
Unreal meta = (AllowPrivateAccess = "true") 의미 (0) | 2024.02.05 |