[DAY51] Shooter Project(2) : AI perception, Attack

베리투스·2025년 10월 22일
0

Shooter Project

목록 보기
2/10

AI 개발 로드맵에 따라 구현에 돌입했다. 오늘은 C++로 몬스터의 기본 뼈대를 만들고, AI가 플레이어를 '보고' '생각'하게 만드는 핵심 로직을 완성하는 데 집중했다. AI 컨트롤러, 인지(Perception), 행동 트리(BT), 블랙보드(BB)라는 네 가지 개념이 어떻게 유기적으로 맞물려 돌아가는지 몸으로 부딪히며 배웠다.😅 수많은 오류와 씨름한 끝에, 마침내 AI가 나를 발견하고 쫓아와 공격 애니메이션을 재생했을 때의 성취감이 있었다.


📌 목표

  • C++로 AIMonsterBase 클래스를 구현하여 AI의 기본 데이터 구조를 확립한다.
  • PawnSensing를 넘어 AIPerceptionComponent를 사용하여 정교한 시각 인지 시스템을 구축한다.
  • 행동 트리(BT)블랙보드(BB)를 연결하고, '플레이어 추적 및 공격' 로직을 구현한다.
  • 애님 몽타주BTTask를 연동하여, AI가 특정 상황에 맞는 공격 애니메이션을 재생하도록 한다.

📖 이론

1. AI의 뇌 구조: 컨트롤러, BT, BB의 삼위일체

AI가 움직이기 위해서는 이 세 가지 요소가 긴밀하게 협력해야 한다.

  • AI 컨트롤러: AI 캐릭터의 '뇌'. 어떤 행동 트리와 블랙보드를 사용할지 결정하고, Perception 컴포넌트를 통해 외부 세계의 정보를 받아들인다.
  • 블랙보드: AI의 '단기 기억 메모장'. 컨트롤러가 받아들인 정보(예: "타겟은 플레이어다")를 기록하는 공간이다.
  • 행동 트리: AI의 '의사결정 순서도'. 블랙보드에 기록된 정보를 보고 "타겟이 있다면, 추적하고 공격해라. 없다면, 순찰해라" 와 같은 실제 행동을 결정하고 지시한다.

💻 코드

AIC_Monster.h

AI의 두뇌인 AI 컨트롤러다. 여기서 AIPerceptionComponent를 이용해 시각 센서를 만들고, 플레이어를 감지했을 때와 시야에서 놓쳤을 때의 로직을 처리했다. 특히 감지된 대상이 진짜 플레이어인지 Cast로 확인하는 과정이 핵심이었다. 다른 AI나 중립 NPC를 공격하는 문제를 막아주기 때문이다.

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AIPerceptionTypes.h"
#include "AIC_Monster.generated.h"

UCLASS()
class SPARTA_TPROJECT_02_API AAIC_Monster : public AAIController
{
    GENERATED_BODY()

public:
    AAIC_Monster();

private:
    UPROPERTY(VisibleAnywhere)
    class UAIPerceptionComponent* AIPerceptionComponent; // AI의 감각(시각, 청각 등)을 담당

    UPROPERTY(VisibleAnywhere)
    class UAISenseConfig_Sight* SightConfig; // 시각 센서의 상세 설정 (반경, 각도 등)

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

AIC_Monster.cpp

// AIC_Monster.cpp
#include "AIC_Monster.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Sight.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Sparta_TProject_02/Sparta_TProject_02Character.h" // 플레이어 클래스 헤더

AAIC_Monster::AAIC_Monster()
{
    // Perception Component와 Sight Config 생성
    AIPerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerceptionComponent"));
    SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));

    if (SightConfig)
    {
        // 시각 센서의 상세 설정
        SightConfig->SightRadius = 1500.0f;
        SightConfig->LoseSightRadius = 2000.0f;
        SightConfig->PeripheralVisionAngleDegrees = 90.0f;
        SightConfig->DetectionByAffiliation.bDetectEnemies = true;
        SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
        SightConfig->DetectionByAffiliation.bDetectFriendlies = true;

        // 설정한 센서를 Perception Component에 등록
        AIPerceptionComponent->ConfigureSense(*SightConfig);
        AIPerceptionComponent->SetDominantSense(SightConfig->GetSenseImplementation());
    }
    // 감지 이벤트가 발생하면 OnTargetPerceptionUpdated 함수를 호출하도록 연결
    AIPerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &AAIC_Monster::OnTargetPerceptionUpdated);
}

void AAIC_Monster::OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
    // 감지된 대상이 플레이어인지 '반드시' 확인!
    ASparta_TProject_02Character* PlayerCharacter = Cast<ASparta_TProject_02Character>(Actor);

    if (Stimulus.WasSuccessfullySensed())
    {
        if (PlayerCharacter) // 플레이어가 맞을 때만 아래 로직 실행
        {
            // 블랙보드의 TargetActor 키에 플레이어 정보를 기록
            GetBlackboardComponent()->SetValueAsObject(TEXT("TargetActor"), Actor);
        }
    }
    else
    {
        // 시야에서 사라지면 블랙보드의 TargetActor 키를 비움
        GetBlackboardComponent()->ClearValue(TEXT("TargetActor"));
    }
}

⚠️ 실수

  • AI가 꿈쩍도 하지 않는 문제 😱
    분명 로그는 뜨는데 AI가 제자리에 서 있는 문제가 있었다. 원인은 행동 트리와 블랙보드의 연결 고리가 끊어져 있었기 때문이었다. 행동 트리 에디터의 디테일 패널에서 Blackboard Asset을 지정해주지 않아, Move To 태스크가 TargetActor라는 기억을 읽어오지 못하고 있었다. AI의 뇌와 기억을 연결하는 이 작은 설정 하나가 모든 것을 멈추게 했다.

  • AI가 아군 좀비에게 돌진하는 문제
    플레이어뿐만 아니라 다른 좀비에게도 이동하고 공격하는 문제가 발생했다. OnTargetPerceptionUpdated 함수에서 감지된 Actor플레이어 클래스인지 Cast로 확인하는 필터링 로직을 추가하여 해결했다. AI에게 '적'이 누구인지 명확히 알려주는 과정이 필수적이었다.

  • 공격 애니메이션이 뚝 끊기는 문제
    BTT_Attack 태스크에서 Montage Play 노드를 일반 실행 핀으로 연결했더니, 애니메이션이 시작되자마자 태스크가 종료되어 움직임이 꼬였다. Montage Play 노드의 On Completed 델리게이트 핀Finish Execute에 연결하여, 애니메이션이 모두 끝나야 태스크가 종료되도록 비동기적으로 처리해야 한다는 것을 배웠다.


✅ 핵심 요약

개념설명비고
AI 컨트롤러AI 캐릭터의 행동을 결정하는 '뇌' 역할을 하는 클래스. Perception, 행동 트리 실행 등을 담당한다.🧠 AI의 뇌
AIPerception시각, 청각 등 AI의 '감각'을 담당하는 컴포넌트. 특정 대상을 감지하고 정보를 컨트롤러에 전달한다.👀 AI의 눈과 귀
블랙보드AI가 기억해야 할 데이터를 저장하는 '메모리' 또는 '메모장'. (예: TargetActor, LastKnownLocation)📝 AI의 기억
행동 트리블랙보드의 데이터를 바탕으로 AI가 어떤 행동을 할지 결정하는 '의사결정 순서도'.🗺️ AI의 생각 흐름
애님 몽타주코드나 블루프린트에서 직접 제어할 수 있는 특수 애니메이션. 공격, 스킬 사용 등 이벤트 기반 동작에 필수적이다.🎬 특별 행동 스크립트
profile
Shin Ji Yong // Unreal Engine 5 공부중입니다~

0개의 댓글