Blackboard 시스템
BlackboardData 에셋, Key 타입, Observer 패턴과 BT 연동을 학습합니다
BlackboardData와 Key 타입
AI의 공유 메모리 시스템
Blackboard는 AI가 사용하는 공유 데이터 저장소입니다. BehaviorTree 노드들은 Blackboard의 Key를 읽고 쓰면서 의사결정에 필요한 정보를 교환합니다. 지원되는 주요 Key 타입은 Bool, Int, Float, Vector, Object, Enum, Name, Class 등입니다.
// Blackboard 값 설정
UBlackboardComponent* BB = GetBlackboardComponent();
BB->SetValueAsBool(TEXT("IsAlert"), true);
BB->SetValueAsVector(TEXT("PatrolLocation"), PatrolPoint);
BB->SetValueAsObject(TEXT("TargetActor"), DetectedEnemy);
BB->SetValueAsFloat(TEXT("ThreatLevel"), 0.8f);
// Blackboard 값 읽기
bool bIsAlert = BB->GetValueAsBool(TEXT("IsAlert"));
AActor* Target = Cast<AActor>(
BB->GetValueAsObject(TEXT("TargetActor")));
FVector Location = BB->GetValueAsVector(TEXT("PatrolLocation"));Key 이름은 FName으로 관리됩니다. 자주 사용하는 Key 이름은 static const FName으로 선언하여 오타를 방지하세요.
Observer 패턴
Blackboard 값 변경을 감시하는 메커니즘
Blackboard는 Observer 패턴을 지원합니다. 특정 Key의 값이 변경되면 등록된 콜백이 호출되어 BT Decorator의 조건 재평가나 즉각적인 반응이 가능합니다.
// Observer 등록 - Key 변경 감지
FOnBlackboardChangeNotification Delegate;
Delegate.BindUObject(this, &AMyAIController::OnBlackboardValueChanged);
BB->RegisterObserver(
BB->GetKeyID(TEXT("TargetActor")),
this, Delegate);
void AMyAIController::OnBlackboardValueChanged(
const UBlackboardComponent& BBComp,
FBlackboard::FKey KeyID)
{
// TargetActor가 변경되면 즉시 반응
UE_LOG(LogAI, Log, TEXT("Target changed!"));
}Blackboard와 BT 연동
BehaviorTree 노드에서 Blackboard 활용
BehaviorTree의 Decorator는 Blackboard 값을 기반으로 분기 조건을 평가합니다. BTDecorator_BlackboardBase를 상속하면 Blackboard 키를 조건으로 사용하는 커스텀 데코레이터를 만들 수 있습니다.
// 커스텀 Decorator: 체력이 일정 이상인지 확인
UCLASS()
class UBTDecorator_CheckHealth : public UBTDecorator_BlackboardBase
{
GENERATED_BODY()
public:
UBTDecorator_CheckHealth();
protected:
UPROPERTY(EditAnywhere, Category = "Condition")
float MinHealthPercent = 0.3f;
virtual bool CalculateRawConditionValue(
UBehaviorTreeComponent& OwnerComp,
uint8* NodeMemory) const override
{
AAIController* Controller = OwnerComp.GetAIOwner();
APawn* Pawn = Controller ? Controller->GetPawn() : nullptr;
if (!Pawn) return false;
// 체력 컴포넌트에서 현재 체력 비율 확인
UHealthComponent* Health =
Pawn->FindComponentByClass<UHealthComponent>();
return Health &&
Health->GetHealthPercent() >= MinHealthPercent;
}
};Decorator에서 bNotifyBecomeRelevant과 bNotifyTick을 설정하면 조건이 변경될 때 BT가 자동으로 재평가합니다. Observer Aborts와 결합하면 즉각적인 행동 전환이 가능합니다.
Blackboard 설계 패턴
확장성 있는 Blackboard 구조 설계
대규모 프로젝트에서는 Blackboard의 Key 이름을 상수로 관리하고, 부모-자식 관계를 활용한 계층적 Blackboard 구조를 설계해야 합니다.
// Blackboard Key 이름 상수 관리
namespace BBKeys
{
static const FName TargetActor = TEXT("TargetActor");
static const FName PatrolLocation = TEXT("PatrolLocation");
static const FName IsAlert = TEXT("IsAlert");
static const FName ThreatLevel = TEXT("ThreatLevel");
static const FName LastKnownLoc = TEXT("LastKnownLocation");
static const FName HomeLocation = TEXT("HomeLocation");
}
// 안전한 Blackboard 접근 헬퍼
template<typename T>
T GetBBValue(const UBlackboardComponent* BB, const FName& Key);
template<>
AActor* GetBBValue<AActor*>(const UBlackboardComponent* BB,
const FName& Key)
{
return Cast<AActor>(BB->GetValueAsObject(Key));
}
// 사용
AActor* Target = GetBBValue<AActor*>(BB, BBKeys::TargetActor);BlackboardData의 Parent 속성을 설정하면 공통 Key(TargetActor, HomeLocation 등)를 부모에, AI 유형별 Key(PatrolPath, SquadRole 등)를 자식에 정의할 수 있습니다. 이를 통해 Key 중복을 방지하고 일관성을 유지합니다.
핵심 요약
- Blackboard는 AI의 공유 메모리로, BT 노드 간 데이터 교환의 핵심이다
- Key 타입으로 Bool, Float, Vector, Object 등 다양한 데이터를 저장한다
- Observer 패턴으로 Key 변경을 감지해 BT가 즉각 반응할 수 있다
- BTDecorator_BlackboardBase로 Blackboard 값 기반 조건 분기를 구현한다
- Key 이름 상수화와 부모 Blackboard로 확장성 있는 구조를 설계한다
도전 과제
배운 내용을 직접 실습해보세요
순찰 AI에 필요한 Blackboard 키(TargetActor, PatrolLocation, IsAlert, ThreatLevel)를 설계하고 BlackboardData 에셋으로 생성하세요. BT 노드에서 각 키를 읽고 쓰는 테스트를 수행합니다.
Blackboard의 RegisterObserver를 사용하여 TargetActor 키가 변경되면 즉시 로그를 출력하는 옵저버를 등록하세요. BT Decorator의 Observer Aborts와 연동하여 실시간 반응을 확인합니다.
UBlackboardKeyType을 상속하여 FGameplayTag 기반의 커스텀 Blackboard Key 타입을 구현하세요. AI의 현재 상태(Patrol, Chase, Attack)를 GameplayTag로 관리하고 BT에서 조건으로 활용합니다.