게임 개발/언리얼 5

언리얼5 - 행동트리 모델의 이해

싹난 감자 2024. 12. 15. 19:46

 

행동 트리 모델 개요

행동 트리(Behavior Tree)의 역사

2004년 개발사 번지의 헤일로2에서 인공지능을 설계하는데 사용됨.

2005 GDC에서 발표 : "Handling Complexity in the Halo 2 AI"

우선순위와 트리 구조를 사용해 인공지능을 설계하는 기법

게임 산업에서 개량해 널리 사용되고 있음.

 

행동 트리 모델의 장점

FSM이 가지고 있던 문제를 해결하는 새로운 게임 인공지능 모델의 수립.

행동 크리 모델의 장점

  • 모듈화가 잘 되어 있어 확장이 자유로움
  • 트리를 기반으로 계층화가 잘 되어있어, 복잡한 인공지능 모델을 쉽게 설계할 수 있음
  • 다이어그램으로 인공지능 모델을 효과적으로 표현할 수 있음
  • 제공되는 여러 편리한 부가 기능을 활용해 다양한 상황에 대해 손쉽게 제어할 수 있음.

 

행동 트리 모델의 구성 요소

트리에서 항상 왼쪽에 있는 노드에 우선 순위를 부여.

시작 상태를 설정할 필요 없이 왼쪽에서부터 깊이 우선 탐색을 시작.

 

 

행동 트리 모델의 구성 요소

행동을 중심으로 설계.

단, 부모 노드에서 다수의 행동을 컨트롤하며, 이를 컴포짓(Composite)이라 함

  • 셀렉터 (여러 행동 중 하나의 행동을 지정)
  • 시퀀스 (여러 행동을 모두 수행)
  • 패러렐 (여러 행동을 함께 수행)

 

행동에 대한 다양한 결과

  • 성공(Succeeded) : 행동의 성공
  • 실패(Failed) : 행동의 실패
  • 중지(Aborted) : 외부 요인으로 인한 행동의 실패
  • 진행 중(InProgress) : 행동 결과를 홀딩

컴포짓 노드마다 다른 행동 결과 처리

 

컴포짓 노드에 부착하는 다양한 추가 기능

  • 데코레이터(Decorator) : 컴포짓 노드가 실행되는 조건을 지정
  • 서비스(Service) : 컴포짓 노드가 활성화될 때 주기적으로 실행하는 부가 명령
  • 관찰자 중단(Abort) : 데코레이터 조건에 부합되면 컴포짓 내 활동을 모두 중단

 

 

행동 트리 모델의 예시

퇴근하고 집에 가는 행동 트리 모델의 설정

  • 지하철 역까지 이동한 후, 지하철을 타고, 버스를 환승한 후, 문 앞에 도착한다.
  • 시퀀스 컴포짓을 사용해 이들을 묶는다.

 

 

트리가 가진 깊이를 활용해 단계별로 세부적인 행동 설계

퇴근하고 집에 가기

  • 1단계 : 지하철 역까지 이동한 후, 지하철을 타고, 버스를 환승한 후, 문 앞에 도착
  • 2단계 : 두 가지 행동 중 하나만 실행하도록 셀렉터 컴포짓을 사용해 지하철 역까지 걷거나 버스를 타거나 둘 중의 하나를 선택.

 

 

컴포짓의 데코레이터 설정

컴포짓에 조건을 걸어 선택의 폭을 넓힐 수 있도록 확장.

우선 순위가 높은 컴포짓을 왼쪽에 배치하고 데코레이터를 설정

 

 

컴포짓에 관찰자 중단(Abort) 설정

비상 상황에 대한 처리. 예) 친구로부터 함께 놀자는 연락이 왔다.

조건을 추가하고 관찰자 중단 기능을 설정, 비상 상황이 발생하면 현재 진행 중인 컴포짓을 즉시 취소하고 루트에서부터 다시 점검.

 

 

 

NPC 전투를 위한 행동 트리 모델 예시

행동 트리 모델의 구현 예시

전형적인 RPG 게임 NPC에 대한 행동 트리 모델 예시

쉬기(IDLE)와 정찰(PATROL) 상태에 대한 행동트리

 

 

추가적으로 추격(CHASE)과 공격(ATTACK) 상태에 대한 행동 트리

 

 


 

 

Auto Prossess AI에 월드에 배치된 Pawn에 어떤 AI를 적용할 것인지와, AI Controller를 선택

 

AIController를 상속받은 ABAIController 클래스 생성

 

// Fill out your copyright notice in the Description page of Project Settings.


#include "Character/ABCharacterNonPlayer.h"
#include "Engine/AssetManager.h"
#include "AI/ABAIController.h"

AABCharacterNonPlayer::AABCharacterNonPlayer()
{
	GetMesh()->SetHiddenInGame(true);

	AIControllerClass = AABAIController::StaticClass();
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
}

void AABCharacterNonPlayer::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	ensure(NPCMeshes.Num() > 0);
	int32 RandIndex = FMath::RandRange(0, NPCMeshes.Num() - 1);
	NPCMeshHandle = UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(NPCMeshes[RandIndex], FStreamableDelegate::CreateUObject(this, &AABCharacterNonPlayer::NPCMeshLoadCompleted));
}

void AABCharacterNonPlayer::SetDead()
{
	Super::SetDead();

	FTimerHandle DeadTimerHandle;
	GetWorld()->GetTimerManager().SetTimer(DeadTimerHandle, FTimerDelegate::CreateLambda(
		[&]()
		{
			Destroy();
		}
	), DeadEventDelayTime, false);
}

void AABCharacterNonPlayer::NPCMeshLoadCompleted()
{
	if (NPCMeshHandle.IsValid())
	{
		USkeletalMesh* NPCMesh = Cast<USkeletalMesh>(NPCMeshHandle->GetLoadedAsset());
		if (NPCMesh)
		{
			GetMesh()->SetSkeletalMesh(NPCMesh);
			GetMesh()->SetHiddenInGame(false);
		}
	}

	NPCMeshHandle->ReleaseHandle();
}

ABCharacterNonPlayer.cpp

 

데이터 저장소인 BlackBoard와 Behavior Tree 두 가지를 만들어줘야함.

 

먼저 BlackBoard, 안에 저장된 값을 가지고 의사 결정을 함.

 

Behavior Tree, 행동 트리에 노드를 추가해 수행할 행동을 지정

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "ABAIController.generated.h"

/**
 * 
 */
UCLASS()
class ARENABATTLE_API AABAIController : public AAIController
{
	GENERATED_BODY()
	
public:
	AABAIController();

	void RunAI();
	void StopAI();

protected:
	virtual void OnPossess(APawn* InPawn) override;

private:
	UPROPERTY()
	TObjectPtr<class UBlackboardData> BBAsset;
	
	UPROPERTY()
	TObjectPtr<class UBehaviorTree> BTAsset;
};

ABAIController.h

// Fill out your copyright notice in the Description page of Project Settings.


#include "AI/ABAIController.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardData.h"
#include "BehaviorTree/BlackboardComponent.h"

AABAIController::AABAIController()
{
	static ConstructorHelpers::FObjectFinder<UBlackboardData> BBAssetRef(TEXT("/Script/AIModule.BlackboardData'/Game/ArenaBattle/AI/BB_ABCharacter.BB_ABCharacter'"));
	if (nullptr != BBAssetRef.Object)
	{
		BBAsset = BBAssetRef.Object;
	}

	static ConstructorHelpers::FObjectFinder<UBehaviorTree> BTAssetRef(TEXT("/Script/AIModule.BehaviorTree'/Game/ArenaBattle/AI/BT_ABCharacter.BT_ABCharacter'"));
	if (nullptr != BTAssetRef.Object)
	{
		BTAsset = BTAssetRef.Object;
	}
}

void AABAIController::RunAI()
{
	UBlackboardComponent* BlackboardPtr = Blackboard.Get();
	if (UseBlackboard(BBAsset, BlackboardPtr))
	{
		bool RunResult = RunBehaviorTree(BTAsset);
		ensure(RunResult);
	}
}

void AABAIController::StopAI()
{
	UBehaviorTreeComponent* BTComponent = Cast<UBehaviorTreeComponent>(BrainComponent);
	if (BTComponent)
	{
		BTComponent->StopTree();
	}
}

void AABAIController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);

	RunAI();
}

ABAIController.cpp

 

 


https://www.inflearn.com/course/%EC%9D%B4%EB%93%9D%EC%9A%B0-%EC%96%B8%EB%A6%AC%EC%96%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-part-2/dashboard

 

이득우의 언리얼 프로그래밍 Part2 - 언리얼 게임 프레임웍의 이해 강의 | 이득우 - 인프런

이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 C++ 프로그래밍을 사용해 핵&슬래시 로그라이크 게임 예제를 처음부터 끝까지 체계적으로 제작하는 방법을

www.inflearn.com