
좌측의 키는 변수 같은것으로 원하는 키(변수)를 저장
각 키에 대해 우측 디테일 패널에서 설정
// MyAIController.h
UCLASS()
class MYAI_API AMyAIControllerAIController : public AAIController
{
GENERATED_BODY()
public:
FORCEINLINE UBlackboardComponent* GetBlackboardComp() const
{ return BlackboardComp; }
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
UBlackboardComponent* BlackboardComp;
};
// MyAIController.cpp
#include "BehaviorTree/BlackboardComponent.h"
AMyAIController::AMyAIController()
{
// ... //
BlackboardComp = CreateDefaultSubobject<UBlackboardComponent>(TEXT("BB"));
}
void AMyAIController::BeginPlay()
{
Super::BeginPlay();
if (BlackboardComp)
{
BlackboardComp->SetValueAsBool(TEXT("CanSeeTarget"), false);
BlackboardComp->SetValueAsBool(TEXT("IsInvestigating"), false);
}
}
다른 컴포넌트처럼 속성으로 가지고, 생성자에서 생성
SetValueAs~(키이름, 값) : 블랙보드에서 만든 키에 값 설정하는 함수
컨트롤러 블루프린트에서 BlackBoard 연결

void AMyAIController::OnPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
if (Actor->IsA<AMyCharacter>() == false || !BlackboardComp)
return;
if (Stimulus.WasSuccessfullySensed())
{
BlackboardComp->SetValueAsObject(TEXT("TargetActor"), Actor);
BlackboardComp->SetValueAsBool(TEXT("CanSeeTarget"), true);
BlackboardComp->SetValueAsVector(TEXT("TargetLastKnownLocation"),
Actor->GetActorLocation());
BlackboardComp->SetValueAsBool(TEXT("IsInvestigating"), false);
}
else
{
BlackboardComp->SetValueAsBool(TEXT("CanSeeTarget"), false);
BlackboardComp->SetValueAsBool(TEXT("IsInvestigating"), true);
}
}
OnPerceptionUpdated함수를 통해 블랙보드에서 값을 수정
Selector, Sequence, Simple Parallel이 존재해당 자식 노드들을 왼쪽에서부터 오른쪽으로 실행
if-else문처럼 첫 번재 자식 노드가 실패하면 오른쪽 노드로 넘어감
자식 노드 실행 중 성공하면 오른쪽으로 더 안가고 다시 자기자신으로 돌아옴
자식 노드 중 하나라도 성공하면 selector도 성공한거고, 자식들이 다 실패하면 본인도 실패한 것
마찬가지로 자식노드를 왼쪽에서 오른쪽으로 실행
자식 노드가 성공하면 그 다음 오른쪽 자식도 실행함
실행하다 실패하면, 오른쪽으로 더 안가고 자기 자신으로 돌아옴
모든 자식들이 다 성공해야 본인도 성공인거고, 하나라도 실패하면 실패

왼쪽 보라색이 main task, 오른쪽 회색이 background
main task를 실행시키면서 동시에 background 브랜치를 동시에 실행
디테일 창에 Finish Mode관련하여 2가지 옵션이 존재
Intermediate : 메인 성공 시 즉시 백그라운드도 종료Delayed : 메인 성공해도 백그라운드가 끝날때까지 기다림![]() | ![]() |
|---|
이 Decorator가 조건을 걸어주는 역할. 여러 개도 가능
디테일 패널

On Result Change로 하면, 값이 바뀔 때 Notify됨.On Value Change이면, 동일한 값으로 바뀌어도 Notify 됨

Is Set이면 블랙보드 키에 값이 설정되어 있어야 참을 반환.Is Not Set이면 값이 없거나 false이거나 초기화 되어 있으면 참 반환
// MyController.h
UPROPERTY(EditDefaultsOnly, Category = "AI")
UBehaviorTree* BehaviorTreeAsset;
// MyController.cpp
void AMyAIController::BeginPlay()
{
Super::BeginPlay();
RunBehaviorTree(BehaviorTreeAsset);
}
Behavior Tree를 처음 시작해줄 함수가 필요함. RunBehaviorTree함수를 통해 시작
컨트롤러에 연결

아직 플레이어 감지 못 해 ROOT에 위치![]() | 플레이어 감지 하여 이후 노드들 실행![]() |
|---|

// header
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "BTTask_FindRandomLocation.generated.h"
UCLASS()
class SPARTAAI_API UBTTask_FindRandomLocation : public UBTTaskNode
{
GENERATED_BODY()
public:
UBTTask_FindRandomLocation();
protected:
virtual EBTNodeResult::Type ExecuteTask
(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
// 결과를 저장할 Blackboard 키
UPROPERTY(EditAnywhere, Category = "Blackboard")
struct FBlackboardKeySelector LocationKey;
UPROPERTY(EditAnywhere, Category = "Search", meta = (ClampMin = "100.0"))
float SearchRadius = 1000.0f;
};
![]() | 디테일 창에 구현한 속성들 존재.![]() |
|---|
// cpp
#include "Course/BTTask_FindRandomLocation.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "AIController.h"
#include "NavigationSystem.h"
UBTTask_FindRandomLocation::UBTTask_FindRandomLocation()
{
// 해당 Node 이름 설정
NodeName = TEXT("Find Random Location");
// 벡터값을 저장가능한 블랙보드의 키들만 Location Key에 필터링됨
LocationKey.AddVectorFilter(this,
GET_MEMBER_NAME_CHECKED(UBTTask_FindRandomLocation,
LocationKey));
}
EBTNodeResult::Type UBTTask_FindRandomLocation::ExecuteTask(/**/)
{
// 컨트롤러, Pawn, 네비게이션 시스템 가져오기
AAIController* AIController = OwnerComp.GetAIOwner();
if (!AIController) return EBTNodeResult::Failed;
APawn* MyPawn = AIController->GetPawn();
if (!MyPawn) return EBTNodeResult::Failed;
UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetCurrent(GetWorld());
if (!NavSystem) return EBTNodeResult::Failed;
// 랜덤 위치 찾기
FNavLocation RandomLocation;
bool bFound = NavSystem->GetRandomReachablePointInRadius(
MyPawn->GetActorLocation(),
SearchRadius,
RandomLocation // 결과를 여기에 저장
);
// Blackboard 키에 저장
UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
if (BlackboardComp)
{
// 디테일 창에서 설정한 Location Key 이름을 이용해 저장
BlackboardComp->SetValueAsVector(LocationKey.SelectedKeyName,
RandomLocation.Location);
return EBTNodeResult::Succeeded;
}
return EBTNodeResult::Failed;
}
