반응형
졸업작품을 진행하면서 언리얼 엔진에서 게임에서 사용하는게 아닌 개발 단계에서 사용하기 위해서
플러그인 제작이 필요했다.
NavMesh 맵을 추출해야하는데, 이때 인스턴스에서 추출하는 코드를 만들어야하나? 하면서 고민하고 있었는데
플러그인으로 만들어서 레벨에 있는 NavMesh를 추출하고 해당 파일을 서버에 따로 보내서 적용하는 것이다.
플러그인을 어떻게 만드냐?
1. 언리얼 상단에 edit - plugin 을 선택한다.

2. 좌측 상단 위 +Add 버튼을 눌러 플러그인을 생성한다.

3. 자신에게 필요한 플러그인 템플릿을 선택한다. (여기서는 Editor Toolbar Button을 선택했는데 Blank 선택해도 됨)

4. 그럼 다음과 같이 plugin 폴더가 생성된다.

5. 반드시 해야하는 Build.cs에 다음과 같이 추가한다.
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"UnrealEd",
"NavigationSystem",
"Navmesh",
"Projects"
}
);
6. NavMesh를 export하기 위한 클래스를 생성한다.
#pragma once
#include "CoreMinimal.h"
class FNavMeshExporter
{
public:
static void ExportCurrentWorldNavMeshCommand(const TArray<FString>& Args);
private:
static bool ExportCurrentWorldNavMeshToFile(const FString& AbsPath);
};
#include "NavMeshExporter.h"
#include "Editor.h"
#include "Engine/World.h"
#include "NavigationSystem.h"
#include "NavMesh/RecastNavMesh.h"
#include "Misc/FileHelper.h"
#include "Serialization/BufferArchive.h"
#include "Detour/DetourNavMesh.h"
static constexpr uint32 NAV_MAGIC = 0x4D56414E; // 'NAVM'
static constexpr uint32 NAV_VERSION = 1;
void FNavMeshExporter::ExportCurrentWorldNavMeshCommand(const TArray<FString>& Args)
{
if (Args.Num() < 1)
{
UE_LOG(LogTemp, Error, TEXT("Usage: Readapt.ExportNavMesh <AbsolutePath>"));
UE_LOG(LogTemp, Error, TEXT("Example: Readapt.ExportNavMesh D:/NavExport/MyMap.navbin"));
return;
}
const FString AbsPath = Args[0];
if (ExportCurrentWorldNavMeshToFile(AbsPath))
{
UE_LOG(LogTemp, Display, TEXT("[NavExport] OK: %s"), *AbsPath);
}
else
{
UE_LOG(LogTemp, Error, TEXT("[NavExport] FAILED"));
}
}
bool FNavMeshExporter::ExportCurrentWorldNavMeshToFile(const FString& AbsPath)
{
if (!GEditor)
{
UE_LOG(LogTemp, Error, TEXT("GEditor is null."));
return false;
}
UWorld* World = GEditor->GetEditorWorldContext().World();
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("Editor World is null. Open a level first."));
return false;
}
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (!NavSys)
{
UE_LOG(LogTemp, Error, TEXT("NavigationSystem is null. Check Project Settings -> Navigation System."));
return false;
}
ANavigationData* NavData = NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
ARecastNavMesh* RecastNav = Cast<ARecastNavMesh>(NavData);
if (!RecastNav)
{
UE_LOG(LogTemp, Error, TEXT("Default NavData is not ARecastNavMesh."));
UE_LOG(LogTemp, Error, TEXT("Did you add NavMeshBoundsVolume and Build Paths?"));
return false;
}
const dtNavMesh* DetourMesh = RecastNav->GetRecastMesh();
if (!DetourMesh)
{
UE_LOG(LogTemp, Error, TEXT("Detour dtNavMesh is null. NavMesh may not be generated yet."));
return false;
}
// --- 파일에 쓸 바이너리 만들기 ---
FBufferArchive Ar;
uint32 Magic = NAV_MAGIC;
uint32 Ver = NAV_VERSION;
Ar << Magic;
Ar << Ver;
// dtNavMeshParams 저장 (서버 init에 필요)
dtNavMeshParams Params = *DetourMesh->getParams();
Ar.Serialize(&Params, sizeof(dtNavMeshParams));
// 타일 개수 세기
uint32 TileCount = 0;
const int MaxTiles = DetourMesh->getMaxTiles();
for (int i = 0; i < MaxTiles; ++i)
{
const dtMeshTile* Tile = DetourMesh->getTile(i);
if (!Tile || !Tile->header || !Tile->data || Tile->dataSize <= 0)
continue;
TileCount++;
}
Ar << TileCount;
// 타일 데이터 저장
for (int i = 0; i < MaxTiles; ++i)
{
const dtMeshTile* Tile = DetourMesh->getTile(i);
if (!Tile || !Tile->header || !Tile->data || Tile->dataSize <= 0)
continue;
int32 TileIndex = i;
int32 DataSize = Tile->dataSize;
Ar << TileIndex;
Ar << DataSize;
Ar.Serialize((void*)Tile->data, DataSize);
}
// 저장
if (!FFileHelper::SaveArrayToFile(Ar, *AbsPath))
{
UE_LOG(LogTemp, Error, TEXT("Failed to write file: %s"), *AbsPath);
return false;
}
Ar.FlushCache();
Ar.Empty();
return true;
}
7. ExportEditor.cpp에 가서 콘솔 커멘드를 추가해야 한다.
#include "NavMeshExporter.h"
#include "HAL/IConsoleManager.h"
void FReadaptNavExportEditorModule::StartupModule()
{
// (아래만 추가)
IConsoleManager::Get().RegisterConsoleCommand(
TEXT("Readapt.ExportNavMesh"),
TEXT("Export current level RecastNavMesh tiles to file. Usage: ...파일이름 <AbsolutePath>"),
FConsoleCommandWithArgsDelegate::CreateStatic(&FNavMeshExporter::ExportCurrentWorldNavMeshCommand),
ECVF_Default
);
}
8. 언리얼 OutPut Log에 다음과 같이 입력해보자.

반응형