[DAY46] Wandering/Chashing AI(2)

베리투스·2025년 10월 15일

TIL: Today I Learned

목록 보기
47/93

오늘은 AI를 한 단계 더 똑똑하게 만드는 행동 트리(Behavior Tree)블랙보드(Blackboard)에 대해 공부했다. 단순히 타이머로 정해진 행동만 반복하는 게 아니라, AI가 스스로 상황을 '기억'하고 그 기억을 토대로 '판단'해서 행동하게 만드는 방식이다. AI의 감각(Perception)으로 얻은 정보를 블랙보드라는 데이터베이스에 저장하고, 행동 트리가 이 정보를 읽어 추격, 조사, 순찰 등 다양한 행동을 결정하는 흐름을 직접 구현해봤다. 코드가 복잡해 보였지만, 역할을 분리하니 오히려 구조가 더 명확해지는 느낌이었다. 🧠


📌 목표

  • Behavior Tree와 Blackboard의 개념과 역할을 이해한다.
  • AI Perception(감각) 시스템이 감지한 정보를 Blackboard에 실시간으로 업데이트한다.
  • Behavior Tree에서 사용할 커스텀 Task(행동 단위)를 직접 코드로 작성한다.
  • 추격, 조사, 순찰의 우선순위를 가진 AI 의사결정 구조를 Behavior Tree로 완성한다.

📖 이론

1. 핵심 개념

  • 블랙보드 (Blackboard): AI의 "기억 저장소" 또는 "메모리"이다. AI가 보고 들은 정보들을 Key-Value 형태로 저장하는 데이터베이스 역할을 한다. (예: CanSeeTarget = true)
  • 행동 트리 (Behavior Tree): AI의 "의사결정 로직"이다. 블랙보드에 저장된 데이터를 조건으로 "지금 무엇을 할까?"를 결정하는 규칙들의 집합이다. 시각적인 트리 구조로 되어 있어 로직을 파악하기 쉽다.

2. AI 작동 흐름

AI의 각 컴포넌트는 다음과 같은 순서로 유기적으로 동작한다.

Perception (감각)AI Controller (정보 처리)Blackboard (기억)Behavior Tree (판단)Character (행동)


💻 코드

SpartaAIController.h


#pragma once

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

class UBehaviorTree;
class UBlackboardComponent;

UCLASS()
class SPARTAPROJECT_API ASpartaAIController : public AAIController
{
    GENERATED_BODY()

public:
    ASpartaAIController();
    void StartBehaviorTree();

protected:
    virtual void BeginPlay() override;

    // AI의 기억 장치
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
    UBlackboardComponent* BlackboardComp;

    // AI의 의사결정 로직 에셋
    UPROPERTY(EditDefaultsOnly, Category = "AI")
    UBehaviorTree* BehaviorTreeAsset;

private:
    // 감각 정보가 업데이트될 때 호출될 함수
    UFUNCTION()
    void OnPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);
};

SpartaAIController.cpp


#include "SpartaAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BehaviorTree.h"
#include "Perception/AIPerceptionComponent.h"
#include "Kismet/GameplayStatics.h"

// BeginPlay: 게임 시작 시 블랙보드 초기화 및 행동 트리 실행
void ASpartaAIController::BeginPlay()
{
    Super::BeginPlay();

    if (BlackboardComp)
    {
        // 블랙보드 값 초기화
        BlackboardComp->SetValueAsBool(TEXT("CanSeeTarget"), false);
        BlackboardComp->SetValueAsBool(TEXT("IsInvestigating"), false);
    }

    // 행동 트리 실행
    StartBehaviorTree();
}

// OnPerceptionUpdated: 감각 시스템이 정보를 감지했을 때 블랙보드를 업데이트
void ASpartaAIController::OnPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
    if (Actor != PlayerPawn || !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);
    }
}

// StartBehaviorTree: 지정된 행동 트리 에셋을 실행
void ASpartaAIController::StartBehaviorTree()
{
    if (BehaviorTreeAsset)
    {
        RunBehaviorTree(BehaviorTreeAsset);
    }
}

⚠️ 실수

  • StartBehaviorTree() 함수만 작성하고 호출하지 않음: SpartaAIController 클래스에 StartBehaviorTree() 함수를 멋지게 구현했지만, 정작 BeginPlay() 함수에서 이 함수를 호출하는 코드를 빼먹었다. 그래서 AI가 게임 시작 시 아무런 행동도 하지 않고 가만히 서 있었다. 코드가 아무리 잘 짜여 있어도 실제로 호출해주지 않으면 아무 소용이 없다는 것을 뼈저리게 느꼈다. 😅
  • 행동 트리 노드 선택 오류 (Simple Parallel 대신 다른 것 사용): AI가 플레이어를 감지했을 때, 플레이어를 추격하면서 동시에 다른 행동(예: 주변 경계)도 수행하게 하고 싶었다. 이를 위해 Simple Parallel 노드를 사용해야 했는데, 실수로 Selector 노드를 사용했다. 그 결과, 플레이어를 감지하면 추격 행동만 하고 다른 행동은 시도조차 하지 않는 문제가 발생했다. 각 노드의 정확한 역할과 사용법을 숙지하는 것이 얼마나 중요한지 다시 한번 깨달았다.
  • Abort Mode 설정 오류: 행동 트리에서 특정 조건이 만족되면 현재 진행 중인 행동을 중단하고 새로운 행동으로 전환해야 할 때가 있다. 예를 들어, 플레이어를 발견하면 순찰을 멈추고 바로 추격해야 한다. 이때 Abort Mode 설정을 잘못하면, 플레이어를 발견해도 기존의 순찰 행동을 계속하거나, 예상치 못한 시점에 행동이 중단되는 문제가 발생할 수 있다. 각 Abort Mode (Self, Lower Priority, Both)의 의미를 정확히 이해하고 상황에 맞게 설정하는 것이 중요하다는 것을 배웠다.

✅ 핵심 요약

개념설명비고
BlackboardAI의 기억 저장소. 모든 상황 판단의 근거가 되는 데이터를 저장한다.Key-Value 방식
Behavior TreeAI의 의사결정 로직. 블랙보드의 데이터를 보고 어떤 행동을 할지 결정한다.우선순위에 따라 행동 결정
PerceptionAI의 감각 기관. 플레이어를 보거나 소리를 듣는 등 외부 정보를 감지한다.이벤트 기반으로 컨트롤러에 알림
BT Task행동 트리를 구성하는 가장 작은 행동 단위. (예: 이동, 공격, 대기)C++ 코드로 직접 기능 확장 가능
Decorator행동 트리의 조건문. 블랙보드의 값을 체크하여 자식 노드의 실행 여부를 결정한다.'If'문과 비슷한 역할 (CanSeeTarget?)
profile
Shin Ji Yong // Unreal Engine 5 공부중입니다~

0개의 댓글