11.2 비헤이비어 트리 시스템
비에어 비어 트리
AI 컨트롤러가 수행 해야 하는 행동 패턴을 트리 구조로 설계 하는 기법
비헤어비어 트리와 블랙보드 애셋 생성
블랙 보드 : 판단에 사용하는 데이터 집합, NPC가 의사 결정 시 데이터 기반으로 진행
비헤어비어 트리 : 블랙보드 데이터 기반으로 트리 정보를 저장한 애셋
Wait 태스트 생성
Task 그룹에서 Wait 태스크 생성
연결 할려면 컴포짓 노드를 사용 하고 셀렉터와 시퀀스가 존재
시퀀스는 false 결과 값이 나올 때가지 테스크를 계속 실행
5초동안 대기 ▶ false 리턴 ▶ 루트로 이동
ABAIController 코드
비헤이비어 트리 애셋, 블랙 보드 사용 할 수 있게 기존 코드 수정
AIModule을 빌드에 포함
public class ArenaBattle : ModuleRules
{
public ArenaBattle(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{ "Core", "CoreUObject", "Engine", "InputCore","UMG", "AIModule"});
}
}
ABAIController.h, cpp
wait 태스크 동작 테스트
비헤어 비어 애셋을 뜨위고 Art + p 하면 뷰포트로 실행 디버그에 활용
UCLASS()
class ARENABATTLE_API AABAIController : public AAIController
{
GENERATED_BODY()
private:
UPROPERTY()
class UBehaviorTree* BTAsset;
UPROPERTY()
class UBlackboardData* BBAsset;
};
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardData.h"
AABAIController::AABAIController()
{
static ConstructorHelpers::FObjectFinder<UBlackboardData> BBObject(TEXT("/Game/Book/AI/BB_ABCharacter.BB_ABCharacter"));
if (BBObject.Succeeded())
{
BBAsset = BBObject.Object;
}
static ConstructorHelpers::FObjectFinder<UBehaviorTree> BTObject(TEXT("/Game/Book/AI/BT_ABCharacter.BT_ABCharacter"));
if (BTObject.Succeeded())
{
BTAsset = BTObject.Object;
}
}
void AABAIController::Possess(APawn* InPawn)
{
Super::Possess(InPawn);
//Blackboard 맞는 지 확인
if (UseBlackboard(BBAsset, Blackboard))
{
//비헤어비어 트리 실행
if (!RunBehaviorTree(BTAsset))
{
ABLOG(Error, TEXT("AIController couldn't run behavior tree!"));
}
}
}
NPC가 스테이지 임의 위치로 순찰 하는 기능 제작
AIController 와 Blackboard 데이터 연결
1. 블랙 보드에 데이터 추가
HomePos : NPC 가 생성 됐을 떄의 위치 값
PatrolPos : 순찰할 위치 정보 보관
2. AI 컨트롤러에서 HomePos 키 및 PatrolPoskey 선언
Black board data는 static 으로 선언 해 참조
Blackboard 에 있는 HomePos 값을 초기화
UCLASS()
class ARENABATTLE_API AABAIController : public AAIController
{
public:
static const FName HomePosKey;
static const FName PatrolPosKey;
}
//...CPP
const FName AABAIController::HomePosKey(TEXT("HomePos"));
const FName AABAIController::PatrolPosKey(TEXT("PatrolPos"));
void AABAIController::Possess(APawn* InPawn)
{
Super::Possess(InPawn);
//Blackboard 맞는 지 확인
if (UseBlackboard(BBAsset, Blackboard))
{
//블랙 보드의 위치 값 갱신
Blackboard->SetValueAsVector(HomePosKey, InPawn->GetActorLocation());
//비헤어비어 트리 실행
if (!RunBehaviorTree(BTAsset))
{
ABLOG(Error, TEXT("AIController couldn't run behavior tree!"));
}
}
}
Patrol 동작을 하는 태스크 제작
BTTaskNode 상속 받는 BTTask_FindPatrolPos 생성 ( BTTask_ 접두사 부분은 자동 제거)
관련 된 모듈을 참조 할 수 있게 Bulid.cs 파일에 모듈 추가
PublicDependencyModuleNames.AddRange(new string[]
{ "Core", "CoreUObject", "Engine", "InputCore","UMG", "AIModule", "GameplayTasks"});
태스크 실행 시 태스크 클래스의 ExcuteTask 함수 실행
무조건 하나의 반환 값을 리턴 해야 된다
Aborted : 실행 중에 중단, Failed : 실행 했지만 실패, Succeeded : 성공 , Inprogress : 실행 중
시퀀스 컴포짓은 자신이 속한 태스크가 실패 할때 까지 계속 실행
로직을 구현하고 실행 결과를 바로 반환 할 수 있게 구현
UCLASS()
class ARENABATTLE_API UBTTask_FindPatrolPos : public UBTTaskNode
{
GENERATED_BODY()
public:
UBTTask_FindPatrolPos();
//태스트 실행 시 동작하는 함수 오버로드 해서 사용
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp,
uint8* NodeMemory) override;
};
#include "BTTask_FindPatrolPos.h"
#include "ABAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
UBTTask_FindPatrolPos::UBTTask_FindPatrolPos()
{
//태스트 이름 지정
NodeName = TEXT("FindPatrolPos");
}
EBTNodeResult::Type UBTTask_FindPatrolPos::ExecuteTask(UBehaviorTreeComponent & OwnerComp,
uint8 * NodeMemory)
{
EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory);
//현재 동작 중인 폰 참조
auto ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
if (nullptr == ControllingPawn)
return EBTNodeResult::Failed;
//내비게이션 클래스 참조
UNavigationSystem* NavSystem = UNavigationSystem::GetNavigationSystem(
ControllingPawn->GetWorld());
if (nullptr == NavSystem)
return EBTNodeResult::Failed;
//블랙 보드의 HomePos 데이터 로드
FVector Origin = OwnerComp.GetBlackboardComponent()->GetValueAsVector(
AABAIController::HomePosKey);
FNavLocation NextPatrol;
//내비게이션 시스템이서 랜덤 위치값을 호출
if (NavSystem->GetRandomPointInNavigableRadius(Origin, 500.0f, NextPatrol))
{
//블랙 보드의 Patrolpos 값 갱신
OwnerComp.GetBlackboardComponent()->SetValueAsVector(
AABAIController::PatrolPosKey, NextPatrol.Location);
return EBTNodeResult::Succeeded;//성공 리턴
}
return EBTNodeResult::Failed;
}
비헤이비어 트리에 태스크 추가
스퀀스에 FindPatrolPos 및 MoveTo 추가
Wait ▶ FindPatrolPos ▶ MoveTo 순으로 동작