내비게이션 메시와 경로 찾기
언리얼 엔진의 내비게이션 시스템은 AI 캐릭터의 이동과 경로 찾기를 위한 강력한 도구를 제공합니다.
이 절에서는 C++ 관점에서 내비게이션 시스템을 활용하는 방법을 살펴보겠습니다.
내비게이션 메시의 개념과 생성
내비게이션 메시(NavMesh)는 AI 캐릭터가 이동할 수 있는 영역을 표현하는 3D 구조입니다.
// NavMesh 생성 및 설정
UCLASS()
class AMyNavMeshBoundsVolume : public AVolume
{
GENERATED_BODY()
public:
AMyNavMeshBoundsVolume()
{
GetBrushComponent()->SetCollisionProfileName(TEXT("NoCollision"));
GetBrushComponent()->SetCanEverAffectNavigation(true);
}
};
// 레벨에 NavMesh 자동 생성 설정
void AMyGameMode::BeginPlay()
{
Super::BeginPlay();
UNavigationSystemV1::SetNavigationAutoUpdateEnabled(true);
}
NavMesh 쿼리 및 경로 찾기
NavMesh를 사용하여 유효한 위치를 찾거나 경로를 계산할 수 있습니다.
bool AMyAIController::FindRandomReachablePoint(FVector& OutResult)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
FNavLocation RandomPoint;
if (NavSys->GetRandomReachablePointInRadius(GetPawn()->GetActorLocation(), 1000.0f, RandomPoint))
{
OutResult = RandomPoint.Location;
return true;
}
}
return false;
}
bool AMyAIController::FindPathToLocation(const FVector& TargetLocation)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
FPathFindingQuery Query;
Query.StartLocation = GetPawn()->GetActorLocation();
Query.EndLocation = TargetLocation;
Query.NavData = NavSys->GetDefaultNavDataInstance();
Query.SetAllowPartialPaths(true);
FPathFindingResult Result = NavSys->FindPathSync(Query);
if (Result.IsSuccessful())
{
// 경로 사용
return true;
}
}
return false;
}
동적 장애물 처리
동적으로 변화하는 환경에 대응하기 위해 동적 장애물을 처리할 수 있습니다.
UCLASS()
class AMyDynamicObstacle : public AActor
{
GENERATED_BODY()
public:
AMyDynamicObstacle()
{
PrimaryActorTick.bCanEverTick = true;
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
}
virtual void Tick(float DeltaTime) override
{
Super::Tick(DeltaTime);
UpdateNavOctree();
}
private:
void UpdateNavOctree()
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
NavSys->UpdateNavOctree(this);
}
}
};
AI 캐릭터의 경로 따라가기
계산된 경로를 따라 AI 캐릭터를 이동시킬 수 있습니다.
void AMyAIController::FollowPath(const TArray<FNavPathPoint>& PathPoints)
{
if (PathPoints.Num() > 0)
{
CurrentPathIndex = 0;
MoveToLocation(PathPoints[CurrentPathIndex].Location);
}
}
void AMyAIController::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
Super::OnMoveCompleted(RequestID, Result);
if (Result.IsSuccess() && ++CurrentPathIndex < PathPoints.Num())
{
MoveToLocation(PathPoints[CurrentPathIndex].Location);
}
}
경로 재계산 트리거 설정
환경 변화에 따라 경로를 재계산하는 트리거를 설정할 수 있습니다.
UCLASS()
class AMyAIController : public AAIController
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
private:
FVector TargetLocation;
float PathUpdateInterval = 1.0f;
float TimeSinceLastPathUpdate = 0.0f;
void UpdatePath();
};
void AMyAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
TimeSinceLastPathUpdate += DeltaTime;
if (TimeSinceLastPathUpdate >= PathUpdateInterval)
{
UpdatePath();
TimeSinceLastPathUpdate = 0.0f;
}
}
void AMyAIController::UpdatePath()
{
if (FindPathToLocation(TargetLocation))
{
// 새 경로 사용
}
}
효율적인 내비게이션 전략
복잡한 환경에서는 계층적 내비게이션 시스템을 구현할 수 있습니다.
UCLASS()
class UHierarchicalNavigation : public UObject
{
GENERATED_BODY()
public:
FVector FindPathInComplexEnvironment(const FVector& Start, const FVector& End);
private:
TArray<FVector> FindHighLevelPath(const FVector& Start, const FVector& End);
TArray<FVector> RefinePathSegment(const FVector& SegmentStart, const FVector& SegmentEnd);
};
FVector UHierarchicalNavigation::FindPathInComplexEnvironment(const FVector& Start, const FVector& End)
{
TArray<FVector> HighLevelPath = FindHighLevelPath(Start, End);
TArray<FVector> DetailedPath;
for (int32 i = 0; i < HighLevelPath.Num() - 1; ++i)
{
TArray<FVector> SegmentPath = RefinePathSegment(HighLevelPath[i], HighLevelPath[i + 1]);
DetailedPath.Append(SegmentPath);
}
return DetailedPath[0]; // 다음 목적지 반환
}
내비게이션 시스템의 성능 최적화 기법
- NavMesh 타일 크기 최적화
void AMyGameMode::OptimizeNavMesh()
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
ARecastNavMesh* NavMesh = Cast<ARecastNavMesh>(NavSys->GetDefaultNavDataInstance());
if (NavMesh)
{
NavMesh->CellSize = 30.0f; // 타일 크기 조정
NavMesh->TileSizeUU = 1000.0f; // 타일 크기 단위 조정
NavMesh->RebuildAll();
}
}
}
- 동적 NavMesh 업데이트 최적화
void AMyGameMode::OptimizeDynamicNavMeshUpdates()
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
NavSys->SetMaxSimultaneousTileGenerationJobsCount(4);
NavSys->SetDirtyAreaWarningSizeThreshold(500.0f);
}
}
대규모 월드에서의 내비게이션 관리 전략
대규모 월드에서는 스트리밍 레벨과 함께 내비게이션 데이터를 관리해야 합니다.
UCLASS()
class AMyLevelStreamingVolume : public AVolume
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Streaming")
FName LevelToStream;
virtual void ActorEnteredVolume(AActor* Other) override;
virtual void ActorLeavingVolume(AActor* Other) override;
};
void AMyLevelStreamingVolume::ActorEnteredVolume(AActor* Other)
{
ULevelStreamingDynamic::LoadLevelInstance(GetWorld(), LevelToStream, FVector::ZeroVector, FRotator::ZeroRotator);
}
void AMyLevelStreamingVolume::ActorLeavingVolume(AActor* Other)
{
ULevelStreamingDynamic::UnloadStreamedLevel(GetWorld(), LevelToStream);
}
동적 환경 변화에 대응
동적으로 변화하는 환경에 대응하기 위해 NavMesh를 실시간으로 업데이트할 수 있습니다.
UCLASS()
class AMyDynamicEnvironmentManager : public AActor
{
GENERATED_BODY()
public:
void UpdateEnvironment(const TArray<AActor*>& ChangedActors);
private:
void UpdateNavMeshForActors(const TArray<AActor*>& Actors);
};
void AMyDynamicEnvironmentManager::UpdateEnvironment(const TArray<AActor*>& ChangedActors)
{
UpdateNavMeshForActors(ChangedActors);
}
void AMyDynamicEnvironmentManager::UpdateNavMeshForActors(const TArray<AActor*>& Actors)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
for (AActor* Actor : Actors)
{
NavSys->UpdateNavOctree(Actor);
}
}
}
언리얼 엔진의 내비게이션 시스템은 AI 캐릭터의 이동과 경로 찾기를 위한 강력한 도구를 제공합니다.
NavMesh를 활용하여 복잡한 환경에서도 효율적인 경로 찾기가 가능하며, 동적 장애물 처리를 통해 변화하는 환경에도 대응할 수 있습니다.
성능 최적화를 위해서는 NavMesh의 타일 크기와 업데이트 빈도를 적절히 조정해야 합니다.
대규모 월드에서는 스트리밍 레벨과 함께 내비게이션 데이터를 관리하여 메모리 사용을 최적화할 수 있습니다.
동적 환경 변화에 대응하기 위해서는 실시간 NavMesh 업데이트 시스템을 구현해야 합니다.
이를 통해 이동 가능 영역의 변화를 즉시 반영하여 AI 캐릭터의 정확한 경로 찾기를 보장할 수 있습니다.