왜 넣었나?

단순 시야 추적만으로는 연출 다양성이 부족함 : 시야 밖에서도 "소리 듣고 갸우뚱 -> 확인 하러감 ->발견 시 전투 전환" 같은 연출이 필요

전투 리액션 강화 : 피격(촉각/데미지)시 즉시 타겟 전환, 어그로 재설정 등,

유지보수/확장성 : 시각/청각/피해를 표준 이벤트(Simuls)로 통합 관리 -> BT/BB와 둥글게 연결


구성(설계 요점) 리스트

1. AIController에 PerceptionComponent 추가

2. SenseConfig 3 종 생성/등록

3. StimuliSource 부착&등록

4. 블랙보드/BT 키,상태 정의

5. 소리/피해 이벤트 발생 경로 연결

6. 테스트

구현부 주요 내용

몹 AIController에 정의

PerceptionComponent->SetDominantSense();

=> 동시에 들어왔을때 우선순위, 

PerceptionComponent->OnPerceptionUpdated.AddDynamic(this, &ThisClass::OnPerceptionUpdate);

=> 인지가 될때 업데이트 되는 함수(델리게이트로 바인딩)

SenseConfig

아래는 내 코드에서 SenseConfig를 이렇게 정의했다.

void APC_AIController::SetupSenseConfig()
{
	const FPC_EnemyTableRow* EnemyTableRow = GetEnemyData();
	ensure(EnemyTableRow);

	SightSense->SightRadius = EnemyTableRow->SightRadius;
	SightSense->LoseSightRadius = EnemyTableRow->LoseSightRadius;
	SightSense->PeripheralVisionAngleDegrees = EnemyTableRow->SightAngle;
	//얼마나 기억할건지
	SightSense->SetMaxAge(5.f);
	SightSense->DetectionByAffiliation.bDetectEnemies = true;

	HearingSense->HearingRange = 1500.f;
	HearingSense->DetectionByAffiliation.bDetectEnemies = true;
	HearingSense->SetMaxAge(5.f);

	DamageSense->SetMaxAge(2.f);

	//
	PerceptionComponent->ConfigureSense(*SightSense);
	PerceptionComponent->ConfigureSense(*HearingSense);
	PerceptionComponent->ConfigureSense(*DamageSense);
}

Sight (시각)

  • SightRadius / LoseSightRadius
    • SightRadius: 최초 발견 거리
    • LoseSightRadius: 시야를 잃는 거리
  • PeripheralVisionAngleDegrees
  • MaxAge(=SetMaxAge)
    • 마지막으로 본 자극을 얼마나 기억할지(초).
  • DetectionByAffiliation
    • bDetectEnemies = true만 두면, 팀 시스템(IGenericTeamAgentInterface) 기준 적군만 감지.

Hearing (청각)

  • HearingRange
    • 소리를 들을 수 있는 기본 범위. (예: 1500.f)
    • 실제 감지는 발생한 노이즈의 Loudness/MaxRange에도 영향.
    •  
  • MaxAge
    • “조금 전 들은 소리”를 얼마나 의심 상태로 유지할지.
    • 3~6초가 무난. 그 사이에 도착해서 확인 → 못 찾으면 Patrol 복귀.
  • 의사결정
    • 발견 아님 → Investigating 상태 전환 + InvestigatingPos에 위치 저장.
    • 갸우뚱/정찰 애님, 느린 Move, 도착 후 재탐색(EQS/시야 재확인) 추천.
    • MakeNoise
void APC_PlayableCharaceter::Jump(const FInputActionValue& Value)
{
	Super::Jump();

	MakeNoise(1, this, GetActorLocation());
	UGameplayStatics::SpawnSoundAtLocation(GetWorld(), PlayerData->JumpSound, GetActorLocation());
}

Damage (피해)

  • MaxAge
    • 피격 후 전투 모드 유지 시간 쪽 의미로 받아들여도 됨.
    • 너무 길면 “맞고 오래 집착”, 너무 짧으면 “금방 잊음”.
  • 정책
    • 보통 가장 강한 우선순위.
    • 즉시 Battle 전환 + 타겟 교체/어그로 갱신.
    • ReportDamgeEvent
float APC_NonPlayableCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
	AController* EventInstigator, AActor* DamageCauser)
{
	UAISense_Damage::ReportDamageEvent(
	GetWorld(), 
	this,
	DamageCauser,
	DamageAmount,
	DamageCauser->GetActorLocation(),
	GetActorLocation());
	
	return Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

 

타겟 UAIPerceptionStimuliSourceComponent 부착

StimulusSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimulus"));
StimulusSource->RegisterForSense(TSubclassOf<UAISense>(UAISense_Sight::StaticClass()));
StimulusSource->RegisterWithPerceptionSystem();

 


동작 방식

1. Perception 업데이트 발생 → UpdatedActors 전달

2. 죽은 대상/군중제어 상태 등 즉시 제외됨

3. 한 액터에 대해 Sight → Hearing → Damage 순으로 체크

4. 시야,데미지는 바로 전투 상태 전환(블랙보드 타겟 액터 저장), 청각은 갸우뚱 이동/애님(블랙보드에 위치저장)

 

추가로 감각을 스코어링해서 우선순위를 처리하도록 해야 할듯함


구현 결과

시각
청각
피해


마무리

UAIPerceptionComponent는 처음 보면 복잡해 보이지만, 한 번 구조를 잡아두면 표준화된 방식으로 처리 할 수 있다. 이번 포스팅에서 설명한 3요소만 적용해도 꽤 감각있는 몹AI처리가 된다.

+ Recent posts