PART 5 - 강의 2/3

동적 Navigation과 Nav Modifier

Dynamic Modifier, NavArea, NavLinkProxy를 활용한 동적 내비게이션 시스템을 구현합니다

01

Dynamic NavMesh Modifier

런타임에 NavMesh를 동적으로 변경

Dynamic Modifier는 런타임에 NavMesh를 수정합니다. 문이 열리고 닫히거나, 장애물이 생기거나, 다리가 파괴되는 등 환경 변화에 따라 AI의 이동 경로가 자동으로 변경됩니다.

C++// Runtime Generation 모드 설정 // Project Settings → Navigation System // Runtime Generation: Dynamic // 또는 DynamicModifiersOnly (Modifier만 동적) // Dynamic Obstacle: NavModifierVolume 사용 // 액터에 NavModifierComponent 추가 UNavModifierComponent* NavModifier = CreateDefaultSubobject<UNavModifierComponent>( TEXT("NavModifier")); NavModifier->SetAreaClass(UNavArea_Null::StaticClass()); // NavArea_Null: 이동 불가 영역 // NavArea_Default: 기본 이동 가능 영역 // NavArea_Obstacle: 장애물 (비용 높음) // 문(Door) 동적 NavMesh 예시 void ADoor::OpenDoor() { bIsOpen = true; // NavModifier 비활성화 → 통과 가능 NavModifier->SetAreaClass(UNavArea_Default::StaticClass()); // NavMesh 업데이트 트리거 UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld()); NavSys->UpdateComponentInNavOctree(*NavModifier); } void ADoor::CloseDoor() { bIsOpen = false; // NavModifier 활성화 → 통과 불가 NavModifier->SetAreaClass(UNavArea_Null::StaticClass()); UNavigationSystemV1::GetCurrent(GetWorld()) ->UpdateComponentInNavOctree(*NavModifier); } // 동적 장애물: 이동하는 오브젝트 // MovableObject에 BoxComponent + NavModifierComponent // DynamicObstacle: true 설정 BoxComponent->SetCanEverAffectNavigation(true); BoxComponent->bDynamicObstacle = true;
주의

Dynamic NavMesh는 타일 단위로 재빌드됩니다. 빈번한 재빌드는 성능에 영향을 미치므로, 작은 오브젝트는 Dynamic Obstacle 대신 RVO Avoidance를 사용하는 것이 좋습니다.

02

NavArea와 비용 시스템

영역별 이동 비용 차등 설정

UNavArea는 NavMesh 영역의 이동 비용을 정의합니다. 물/용암/풀밭 등 다른 지형에 다른 비용을 부여하면, AI가 자연스럽게 비용이 낮은 경로를 선호합니다.

C++// 빌트인 NavArea // UNavArea_Default: 기본 비용 1.0 // UNavArea_Null: 이동 불가 (무한 비용) // UNavArea_Obstacle: 장애물 // 커스텀 NavArea UCLASS() class UNavArea_Water : public UNavArea { GENERATED_BODY() public: UNavArea_Water() { // 이동 비용: 기본의 3배 DefaultCost = 3.0f; // 영역 색상 (에디터 시각화) DrawColor = FColor::Blue; // 진입 비용: 영역 진입 시 추가 비용 FixedAreaEnteringCost = 100.f; } }; UCLASS() class UNavArea_Lava : public UNavArea { GENERATED_BODY() public: UNavArea_Lava() { DefaultCost = 10.0f; // 매우 높은 비용 DrawColor = FColor::Red; // AI가 가능하면 회피하지만, 필요시 통과 } }; // NavModifierVolume에 커스텀 NavArea 적용 // 레벨에 NavModifierVolume 배치 // Details → Area Class: NavArea_Water // 경로 탐색 시 비용 기반 최적 경로 계산 // A* 알고리즘이 NavArea 비용을 고려 // → 약간 돌아가더라도 낮은 비용 경로 선택 // AI별 NavArea 비용 오버라이드 // 특정 AI는 물을 두려워하지 않음 FNavAgentProperties AgentProps; AgentProps.SetNavAreaCostOverride( UNavArea_Water::StaticClass(), 1.0f); // 수중 AI

FixedAreaEnteringCost는 영역 진입 시 한 번만 부과되는 비용입니다. DefaultCost는 영역 내 이동 거리에 비례합니다. 좁은 위험 지역은 EnteringCost를 높이고, 넓은 지형 차이는 DefaultCost를 조절하세요.

03

NavLinkProxy

점프, 낙하 등 특수 이동 연결

ANavLinkProxy는 NavMesh의 두 영역을 연결하는 특수 링크입니다. 점프, 사다리, 낙하 지점 등 일반 이동으로는 연결되지 않는 영역 간 이동을 가능하게 합니다.

C++// NavLinkProxy 종류 // Simple Link: 시작점→끝점 직선 연결 // Smart Link: C++/BP로 커스텀 이동 로직 // Simple Link 설정 // 1. 레벨에 NavLinkProxy 배치 // 2. Point Links 배열에 시작/끝 위치 설정 // 3. Direction: LeftToRight / RightToLeft / BothWays // 4. NavArea: 링크의 이동 비용 // Smart Link - 커스텀 이동 로직 UCLASS() class AMySmartNavLink : public ANavLinkProxy { GENERATED_BODY() public: AMySmartNavLink(); // Smart Link 이동 시작 콜백 UFUNCTION() void OnSmartLinkReached( AActor* MovingActor, const FVector& DestinationPoint); }; AMySmartNavLink::AMySmartNavLink() { // Smart Link 활성화 SetSmartLinkEnabled(true); // 콜백 바인딩 OnSmartLinkReached.AddDynamic( this, &AMySmartNavLink::OnSmartLinkReached); } void AMySmartNavLink::OnSmartLinkReached( AActor* MovingActor, const FVector& DestinationPoint) { // 점프 이동 구현 ACharacter* Character = Cast<ACharacter>(MovingActor); if (Character) { FVector LaunchVelocity; // 포물선 궤도 계산 UGameplayStatics::SuggestProjectileVelocity_CustomArc( this, LaunchVelocity, Character->GetActorLocation(), DestinationPoint, 0.f, // Override Gravity 0.5f // Arc 파라미터 ); Character->LaunchCharacter(LaunchVelocity, true, true); // 착지 후 경로 재개 FTimerHandle TimerHandle; GetWorldTimerManager().SetTimer(TimerHandle, [this]() { ResumePathFollowing(); }, 1.0f, false); } } // 런타임에 NavLink 활성화/비활성화 // 다리 파괴 시: NavLinkProxy->SetSmartLinkEnabled(false); // → AI가 더 이상 이 링크를 사용하지 않음
참고

Smart Link의 OnSmartLinkReached에서 AI의 이동을 직접 제어합니다. 점프, 사다리 오르기, 텔레포트 등 다양한 특수 이동을 구현할 수 있으며, 완료 후 ResumePathFollowing()으로 경로를 재개합니다.

04

Navigation Query Filter

AI별 맞춤 경로 탐색 조건

FNavigationQueryFilter를 사용하면 AI마다 다른 경로 탐색 조건을 적용할 수 있습니다. 특정 AI는 물을 건널 수 있고, 다른 AI는 건널 수 없는 등의 차별화가 가능합니다.

C++// Navigation Query Filter UCLASS() class UNavQueryFilter_WaterAverse : public UNavigationQueryFilter { GENERATED_BODY() public: UNavQueryFilter_WaterAverse() { // 물 영역 비용을 극단적으로 높임 // → 사실상 회피 SetAreaCost(UNavArea_Water::StaticClass(), 100.f); // Null 영역은 완전 차단 SetExcludedArea(UNavArea_Null::StaticClass()); } }; UCLASS() class UNavQueryFilter_Amphibian : public UNavigationQueryFilter { GENERATED_BODY() public: UNavQueryFilter_Amphibian() { // 물 영역 비용을 낮춤 → 물 경로 선호 SetAreaCost(UNavArea_Water::StaticClass(), 0.5f); SetAreaCost(UNavArea_Default::StaticClass(), 1.0f); } }; // AI Controller에서 필터 적용 void AMyAIController::SetupNavigationFilter() { // MoveTo 시 필터 적용 FAIMoveRequest MoveReq; MoveReq.SetGoalLocation(Destination); MoveReq.SetNavigationFilter( UNavQueryFilter_WaterAverse::StaticClass()); MoveTo(MoveReq); } // BT에서 필터 사용 // BTTask_MoveTo → Filter Class 프로퍼티 설정 // 각 AI 클래스별로 다른 필터 지정 가능

Navigation Filter를 AI 유형별로 분류하면, 같은 맵에서 육상 AI는 다리를 건너고, 수상 AI는 물길을 따라가는 자연스러운 행동이 가능합니다.

SUMMARY

핵심 요약

  • Dynamic Modifier로 런타임에 NavMesh를 변경하며, NavArea_Null로 이동 불가, 커스텀 NavArea로 비용 차등 설정한다
  • NavArea의 DefaultCost와 FixedAreaEnteringCost로 지형별 이동 비용을 세밀하게 제어할 수 있다
  • NavLinkProxy의 Smart Link로 점프, 사다리 등 특수 이동을 구현하고, 런타임에 활성화/비활성화가 가능하다
  • Navigation Query Filter로 AI별 맞춤 경로 탐색 조건을 적용하여 다양한 이동 특성을 부여한다
PRACTICE

도전 과제

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

실습 1: Nav Modifier Volume 활용

NavModifierVolume을 배치하여 특정 영역의 이동 비용을 높이거나(독가스 지역) 이동 불가로 설정하세요. AI가 해당 영역을 우회하여 이동하는지 확인합니다.

실습 2: 동적 NavMesh 갱신

런타임에 장애물을 스폰/제거하고 NavMesh가 동적으로 갱신되는지 확인하세요. Navigation Invoker를 사용하여 플레이어 주변의 NavMesh만 동적으로 빌드하도록 설정합니다.

심화 과제

UNavigationQueryFilter를 상속하여 AI 유형별 이동 제한을 구현하세요. 비행 AI는 모든 영역 통과 가능, 지상 AI는 물 영역 우회, 대형 AI는 좁은 통로 불가 등의 규칙을 필터로 정의합니다.