[UE5 KDT2024] 35. 비헤이비어트리, 블랙보드
▶️ 비헤이비어
언리얼에서 지원하는 트리기반의 노드시스템
트리구조를 통해 ai를 컨트롤한다.
▶️루트 노드
: 가장 최상위 노드. 시작점. 자식 노드로 하나의 노드만 가질 수 있다.
▶️컴포짓 노드 : 트리 구조에서 여러 자식 노드를 가질 수 있는 노드.
- 자식 노드들의 어떤 순서와 조건으로 실행될 지 결정
- 노드에 데코레이터, 서비스를 부착할 수 있다.
- 컴포짓 노드의 종류 :
1) 시퀀스 노드 (Sequence) : 왼쪽 -> 오른쪽 실행.
- 자식 노드 중 하나라도 실패한다면 실행을 중단. 시퀀스도 실패
2) 셀렉터 (Selector) : 왼쪽 -> 오른쪽 실행
- 자식 노드 중 하나라도 성공하면 실행을 중단. 셀렉터도 성공
3) 심플페러럴 : 메인테스크와 보조트리를 동시에 실행.
- 메인 테스크의 겨로가가 전체 노드를 결정.
테스크노드 : 진짜 작업을 하는 노드
데코레이터 : 조건을 확인하여 성공, 실패를 리턴.
- 컴포짓이나 테스크노드에 부착 가능.
서비스 노드 : 컴포짓 또는 테스크 노드에 부착된다. 설정된 시간 간격으로 반복 실행된다.
▶️블랙보드 : 데이터를 저장하고 관리하는 에셋 (객체)
- 언리얼 비헤이비어트리 -> 실행 -> 데이터
1. 블랙보드 생성
1) 블루프린트로 생성하기
(1)블루프린트 폴더에서 ai폴더 생성
(2) 인공지능 - 블랙보드 - BD_MonsterDefault 생성
(3) 인공지능 - 비헤이비어트리 - BT_MonsterTree 생성
(4) BD_MonsterDefault 블랙보드에서
[1] 새 키 -> 오브젝트형 -> Target
[2] Target선택 -> 키타입 열기 -> 베이스 클래스를 Actor로 지정.
▶️ ‘인스턴트 동기화’를 체크 하게되면 해당 블랙보드를 사용하는 모든 클래스들에게 이 데이터가 동기화된다.
2) BT_MonsterTree 에디터
(1) selector 추가, sequence 추가
(2) 시퀀스 노드 선택 후 데코레이터 추가 - 블랙보드 선택
(3) 블랙보드 선택 후 디테일창
블랙보드키는 Target으로 설정
관찰자 노티파이는 OnValueChange
▶️ 관찰자 노티파이 :
On Value Change : Target(블랙보드)의 값이 변할때 들어옴
On Result Change : 상태가 변경되는 경우 재평가 (키쿼리가 is Set 일때 사용되나봄?)
▶️관찰자 중단 :
None : 중단하지 않음
LowerPriority : 해당 데코레이터가 붙은 노드의 우측 노드들을 중단
Self : 해당 데코레이터가 붙은 노드의 하위 노드들을 중단
Both : LowPriority + Self
(4) 테스트용으로 비헤이비어 트리 만들고 사운드 넣기
(5) 만든걸 설정해봅시다.
[1] 비헤이비어 트리, 블랙보드 프로퍼티 추가
AMonsterAIControllerKDT 헤더에 비헤이비어 트리, 블랙보드 프로퍼티 추가
UPROPERTY( EditAnywhere, BlueprintReadWrite )
class UBehaviorTree* mATITree;
UPROPERTY( EditAnywhere, BlueprintReadWrite )
class UBlackboardData* mBlackBoard;
cpp에 헤더 추가
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardData.h"
#include "BehaviorTree/BlackboardComponent.h"
AMonsterAIController::AMonsterAIController()
{
...
// 에셋 가져오기
static ConstructorHelpers::FObjectFinder<UBehaviorTree> tree( L"/Script/AIModule.BehaviorTree'/Game/Blueprint/AI/BT_MonsterTree.BT_MonsterTree'" );
if ( tree.Succeeded() )
mATITree = tree.Object;
static ConstructorHelpers::FObjectFinder<UBlackboardData> board( L"/Script/AIModule.BlackboardData'/Game/Blueprint/AI/BD_MonsterDefault.BD_MonsterDefault'" );
if ( board.Succeeded() )
mBlackBoard = board.Object;
}
[2] MonsterAIController에서 상속받은 AIController 의
protected:
virtual void OnPossess( APawn* InPawn ) override; // aiController가 폰에 빙의 될 때
virtual void OnUnPossess() override; // aiController가 폰에 빙의 해제될 때
가져와서 오버라이드 해주기
void AMonsterAIControllerKDT::OnPossess( APawn* InPawn )
{
Super::OnPossess( InPawn );
if ( IsValid( mATITree ) && IsValid( mBlackBoard ) )
{
// UseBlackboard()에서 컴포넌트 비어있으면 만들어줌
UBlackboardComponent* comp = nullptr;
if ( UseBlackboard( mBlackBoard, comp ) )
{
// 비헤이비어 트리를 실행시킴
RunBehaviorTree( mATITree );
}
}
}
▶️UseBlackboard() : 컨트롤러에 블랙보드 설정
- UseBlackboard() 인자로 들어온 BlackboardComponent가 nullptr일 경우 내부에서 새로 생성한다. (참조로 만들어진 블랙보드컴포넌트를 넘겨준다)
▶️RunBehaviorTree() : 비헤이비어 트리 실행
void AMonsterAIController::OnUnPossess()
{
Super::OnUnPossess();
}
=> 실행하면 블랙트리에서 설정한 사운드가 정상적으로 출력된다…
✔️ 활용방법 : 전투 / 비전투 상태 나눠서 게이지를 다르게 채운다던지
페이즈 보스 등으로 활용할 수 있다.
2. 새로운 테스크를 만들어보자
1) 블랙트리 에디터에서 새 테스크 추가 - ~_Print 저장
2) 생성된 BTTask_BlueprintBase_Print 에디터에서 변수 추가 - Message - 스트링링형 - 눈모양 눌러서 눈뜬상태로 변경
3) 함수 - Receive Execute 함수 만들어보자
▶️테스크 함수 오버라이드
abort : 해당 태스크 중지
execute : 실행했을 때
Tick : 매 틱
4) Excute에서 PrintString 추가 - 변수 Message가져와서 GetMessage 형태로 Print String의 InString과 연결
5) 비헤이비어 트리에 추가한 태스크 연결 (두개)
6) 오른쪽~_Print 선택 후 디테일창 - Message에 Not Found Target 추가
7) 아래 ~_Print 선택 -> Message -> Found Target 추가
▶️ 오른쪽 위 숫자가 실행 순서.
=>실행하면 오른쪽으로만 실행. 아직 입력한 메세지가 출력되진 않는다.
=> OnTargetFound()에서 액터 감지 후, 블랙보드에 데이터 저장을 해야한다.