Unreal: Dynamic load map from Pak file

皮斯卡略夫發表於2021-05-21

Unreal: Dynamic load map from Pak file

目標:在程式執行時載入自定義 Pak 檔案,並開啟指定關卡,顯示其中的完整 map 內容

Unreal 的 Pak 檔案內包括了物體,材質,blueprint,map等等。Level 以 map 的形式儲存。

Firsr of all, Pak 相關的除錯需要 Package Project 執行,不能在 Editor 裡面直接 Launch,否則 FCoreDelegates::MountPak IsBound 為 false , 則無法執行後續操作。

Code

在 GameMode 的 InitGame() 內呼叫如下程式碼。

void MyMainClass::MyMainClass::Exe(UWorld *world) {
    UE_LOG(LogTemp, Warning, TEXT("Init Main class"));
    if (FCoreDelegates::MountPak.IsBound())
    {
        UE_LOG(LogTemp, Warning, TEXT("LoadPakDelegate(): OnMountPak.IsBound()"));
        FString pakPath = TEXT("../../../MyProject3/Content/Paks/MyProject1-MacNoEditor.pak");
        IPakFile *pakFile = FCoreDelegates::MountPak.Execute(pakPath, 4);
      if (pakFile)
      {
        const auto& mountPoint = pakFile->PakGetMountPoint();
              UE_LOG(LogTemp, Warning, TEXT("LoadPakDelegate(): MountPoint %s"), *mountPoint);
        FString pakContentPath = mountPoint + "MyProject1/Content/";
      // FPackageName::RegisterMountPoint("/Game/", "../../../MyProject1/Content/");
      FPackageName::RegisterMountPoint("/Game/", pakContentPath);

      UGameplayStatics::OpenLevel(world, FName(TEXT("Map2")) );
      }
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("LoadPakDelegate(): OnMountPak.IsBound() Falied"));
    }
}

RegisterMountPoint 的分析

static void  RegisterMountPoint
(
    const FString  & RootPath,
    const FString  & ContentPath
)

This will insert a mount point at the head of the search chain (so it can overlap an existing mount point and win).

引數解釋:

RootPath: Logical Root Path.

ContentPath: Content Path on disk.

Logical Path 是程式執行時會去找的 path 並非實際路徑,根據反覆嘗試,只有在這個引數寫成 “/Game/” 的時候才能正確載入到所有資源,若隨意命名可能只能載入到 map 資源,連 map_builtdata 都找不到。會有如下報錯:
LogStreaming: Error: Couldn't find file for package /Game/StarterContent/Maps/Map2_BuiltData requested by async loading code. NameToLoad: /Game/StarterContent/Maps/Map2_BuiltData

我的猜測是當前執行的程式的預設載入路徑是 Game 所以這樣寫可以成功,應該也有方法自定義載入的路徑,但是我暫時不知道。

Content Path 是指需要 mount 到前面這個目錄的內容的父路徑,這個路徑取決於載入的Pak 的當前掛載點和 Pak 內部的檔案路徑。

通過 PakGetMountPoint() 函式得到掛載點為 "../../../"
而且 Pak 內部檔案如下

MyProject1/Content/StarterContent/Blueprints/...
MyProject1/Content/StarterContent/Maps/...
MyProject1/Content/StarterContent/Materials/...
MyProject1/Content/StarterContent/Shapes/...
...

故最終路徑為 “../../../MyProject1/Content/”

Missing shader resource

成功載入 map 但是模型材質和 shader 丟失,log 如下:

[UE4] [2021.05.17-03.57.53:734][  0]LogShaders: Error: Missing shader resource for hash '589973CAE03D7F0ECFEC6B825B774136FF9FCB9D' for shader platform 16 in the shader library
LogMaterial: Error: Tried to access an uncooked shader map ID in a cooked application
[UE4] [2021.05.17-08.02.25:186][  0]LogMaterial: Can't compile BasicShapeMaterial with cooked content, will use default material instead

這個問題原因是在要載入 Pak 的工程設定裡面啟用了 Share Material Shader Code,啟用這個選項會 “Save shader only once” 這樣的優化選項導致了外部的 shader 無法被找到。
在工程中關閉此開關即可。

Ref:

https://answers.unrealengine.com/questions/363767/how-to-load-a-map-from-a-dynamic-level.html 參見 TestyRabbit May 03 '18 at 4:07 PM 的評論

sample code: https://pastebin.com/ZWAPtynK

https://answers.unrealengine.com/questions/258386/loading-map-from-pak-at-runtime.html top 回答

https://answers.unrealengine.com/questions/963414/view.html

相關文章