PART 3 - 강의 1/3

AI Perception 시스템 아키텍처

UAIPerceptionSystem의 전체 구조, Listener/Source 모델, Stimulus 처리 파이프라인을 분석합니다

01

UAIPerceptionSystem 아키텍처

월드 레벨의 Perception 관리 시스템

UAIPerceptionSystem은 월드 서브시스템으로, 모든 AI 감각 데이터의 중앙 허브 역할을 합니다. Sense 클래스를 관리하고, Listener(감지자)와 Source(자극원) 간의 매칭을 처리합니다.

Perception 시스템 구조
UAIPerceptionSystem (World) UAISense (Sight/Hearing/...) UAIPerceptionComponent (Listener) FAIStimulus (결과)
C++// UAIPerceptionSystem - 월드 서브시스템 class AIMODULE_API UAIPerceptionSystem : public UAISubsystem { // 등록된 Sense 인스턴스 관리 TArray<UAISense*> Senses; // Listener 등록/해제 void RegisterSource(FAISenseID SenseID, AActor& Source); void UnregisterSource(AActor& Source); // Perception 업데이트 (매 프레임) void Tick(float DeltaTime); // 특정 Sense의 글로벌 설정 static void SetMaxStimulusAge(FAISenseID SenseID, float MaxAge); }; // 접근 방법 UAIPerceptionSystem* PerceptionSystem = UAIPerceptionSystem::GetCurrent(GetWorld()); // 또는 AIController에서 UAIPerceptionComponent* Perception = AIController->GetPerceptionComponent();
구조

UAIPerceptionSystem은 월드당 하나만 존재합니다. 모든 AI의 Perception 요청이 이 시스템을 통해 처리되므로, Sense 설정의 최적화가 전체 AI 성능에 직접적으로 영향을 미칩니다.

02

Listener와 Stimulus Source

감지자와 자극원의 등록 및 매칭

Listener는 UAIPerceptionComponent를 가진 AI로, 감각 입력을 받습니다. Stimulus Source는 AIPerceptionStimuliSourceComponent를 가진 액터로, 감각 자극을 발생시킵니다.

C++// Listener 설정 (AIController) AMyAIController::AMyAIController() { // Perception Component 생성 PerceptionComponent = CreateDefaultSubobject <UAIPerceptionComponent>(TEXT("PerceptionComp")); // Sight Config 추가 UAISenseConfig_Sight* SightConfig = NewObject<UAISenseConfig_Sight>(); SightConfig->SightRadius = 3000.f; SightConfig->LoseSightRadius = 3500.f; SightConfig->PeripheralVisionAngleDegrees = 60.f; SightConfig->SetMaxAge(5.f); SightConfig->DetectionByAffiliation.bDetectEnemies = true; SightConfig->DetectionByAffiliation.bDetectNeutrals = true; SightConfig->DetectionByAffiliation.bDetectFriendlies = false; PerceptionComponent->ConfigureSense(*SightConfig); PerceptionComponent->SetDominantSense( SightConfig->GetSenseImplementation()); } // Stimulus Source 설정 (감지 대상 액터) // 블루프린트: AIPerceptionStimuliSourceComponent 추가 // 또는 C++: UAIPerceptionStimuliSourceComponent* SourceComp = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>( TEXT("StimuliSource")); SourceComp->RegisterForSense( TSubclassOf<UAISense>(UAISense_Sight::StaticClass())); SourceComp->bAutoRegisterAsSource = true;

LoseSightRadius를 SightRadius보다 크게 설정하면 히스테리시스가 생겨, 시야 경계에서 감지/비감지가 반복되는 현상을 방지합니다.

03

Stimulus 처리 파이프라인

자극 생성부터 콜백까지의 전체 흐름

Stimulus는 Source에서 발생하여 Sense에 의해 처리되고, FAIStimulus 구조체로 Listener에 전달됩니다. 이 과정에서 MaxAge, Strength 등의 필터링이 적용됩니다.

C++// FAIStimulus 구조체 - 감각 정보의 단위 struct FAIStimulus { float Age; // 자극 나이 (초) float ExpirationAge; // 만료 시간 float Strength; // 자극 강도 FVector StimulusLocation; // 자극 발생 위치 FVector ReceiverLocation; // 수신자 위치 FName Tag; // 자극 태그 bool bSuccessfullySensed; // 감지 성공 여부 // ... }; // Perception 업데이트 콜백 PerceptionComponent->OnPerceptionUpdated.AddDynamic( this, &AMyAIController::OnPerceptionUpdated); PerceptionComponent->OnTargetPerceptionUpdated.AddDynamic( this, &AMyAIController::OnTargetPerceptionUpdated); // 개별 타겟 감지/상실 void AMyAIController::OnTargetPerceptionUpdated( AActor* Actor, FAIStimulus Stimulus) { if (Stimulus.WasSuccessfullySensed()) { // 감지됨 → Blackboard에 타겟 등록 GetBlackboardComponent()->SetValueAsObject( TEXT("TargetActor"), Actor); } else { // 감지 상실 → 마지막 위치 기록 GetBlackboardComponent()->SetValueAsVector( TEXT("LastKnownLocation"), Stimulus.StimulusLocation); GetBlackboardComponent()->ClearValue( TEXT("TargetActor")); } }
주의

OnPerceptionUpdated는 모든 감지 결과를 한 번에 전달하고, OnTargetPerceptionUpdated는 개별 액터별로 호출됩니다. 대부분의 경우 개별 콜백이 더 효율적입니다.

04

Perception 쿼리와 필터링

감지된 액터를 조건별로 필터링

UAIPerceptionComponent는 다양한 쿼리 메서드를 제공하여 감지된 액터를 Sense 종류, 현재 감지 상태 등으로 필터링할 수 있습니다.

C++// 현재 감지 중인 모든 액터 TArray<AActor*> PerceivedActors; PerceptionComponent->GetCurrentlyPerceivedActors( nullptr, PerceivedActors); // nullptr = 모든 Sense // 특정 Sense로 감지된 액터만 TArray<AActor*> SeenActors; PerceptionComponent->GetCurrentlyPerceivedActors( UAISense_Sight::StaticClass(), SeenActors); // 특정 액터에 대한 상세 감지 정보 FActorPerceptionBlueprintInfo Info; PerceptionComponent->GetActorsPerception(TargetActor, Info); for (const FAIStimulus& Stimulus : Info.LastSensedStimuli) { if (Stimulus.Type == UAISense::GetSenseID<UAISense_Sight>()) { UE_LOG(LogAI, Log, TEXT("Sight: Age=%.1f, Strength=%.2f"), Stimulus.Age, Stimulus.Strength); } } // 감지 이력이 있는 모든 액터 (만료 포함) TArray<AActor*> KnownActors; PerceptionComponent->GetKnownPerceivedActors( UAISense_Sight::StaticClass(), KnownActors);

GetKnownPerceivedActors()는 MaxAge 이내의 모든 액터를 반환합니다. 현재 시야에 없지만 최근에 본 적을 "기억"하는 AI를 구현할 때 유용합니다.

SUMMARY

핵심 요약

  • UAIPerceptionSystem은 월드 서브시스템으로, 모든 AI 감각의 중앙 허브 역할을 한다
  • Listener(UAIPerceptionComponent)와 Source(StimuliSourceComponent)의 분리 구조로 감지 시스템이 동작한다
  • FAIStimulus가 자극 정보의 단위이며, Age/Strength로 자극의 유효성을 관리한다
  • OnTargetPerceptionUpdated 콜백으로 개별 액터의 감지/상실을 처리하는 것이 효율적이다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Perception 시스템 구조 탐색

UAIPerceptionSystem::GetCurrent()로 월드의 Perception 시스템에 접근하세요. 등록된 Listener 수와 Stimulus Source 수를 로그로 출력하고, 시스템의 업데이트 주기를 확인합니다.

실습 2: 감각 소스 등록

UAIPerceptionStimuliSourceComponent를 Actor에 부착하여 자동으로 감각 소스로 등록되도록 하세요. RegisterForSense()로 시각 자극을 생성합니다.

심화 과제

UAISense를 상속하여 '지진 감지(Seismic Sense)' 커스텀 감각을 구현하세요. 폭발이나 대형 몬스터 이동 시 지면 진동을 발생시키고, 범위 내 AI가 이를 감지하도록 합니다.