Download

Load asset at runtime - 2020

Hello guys,
I need to load at runtime a set of asset that will be downloaded from a distant server (a kind of DLC), and I’m stuck.

My strategy is to use the Editor for creating a package with my assets for a chosen platform, than use the .pak file in a second project (where it will be loaded at runtime).

**1. It is the right way to do ? **

For the moment, I can only mount the .pak file, and get the list of files in the pack:



    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
    FPakPlatformFile* PakPlatformFile = **new** FPakPlatformFile();

    PakPlatformFile->Initialize(&PlatformFile, TEXT(""));

    FPlatformFileManager::Get().SetPlatformFile(*PakPlatformFile);


    **const** FString PakFilename = FPaths::ProjectContentDir() + TEXT("PakFiles/FirstDLC-MacNoEditor.pak");

    **const** FString MountPoint = FPaths::EngineContentDir();

    
    FPakFile pak_file{PakPlatformFile->GetLowerLevel(), *PakFilename, **false**};

    pak_file.SetMountPoint(*MountPoint);


    PakPlatformFile->Mount(*PakFilename, 0, *MountPoint)


2. How to load theses assets into memory ?

From what I saw, the most recent solution is to use the AssetManager object:

→ Create a primary asset object in the project, and link it with all asset that I want to export, with the same bundle tag for each asset.
→ Export the package
→ Try to import the primary asset, what goes load all secondary assets linked.




        UAssetManager &asset_manager = UAssetManager::Get();
        FPrimaryAssetId package_id = asset_manager.GetPrimaryAssetIdForPackage(package_name);

        FPrimaryAssetId primary_asset{"DLCObjects:FirstPack"};
        FAssetBundleData bundle_data;
        bundle_data.AddBundleAsset("AllTextures", soft_path);
        asset_manager.RecursivelyExpandBundleData(bundle_data);

    
        asset_manager.AddDynamicAsset(primary_asset, FStringAssetReference{}, bundle_data)

        /*
        TArray<FName> bundle_names;
         bundle_names.Add(FName{"AllTextures"});
          //Then call *asset_manager.GetPrimaryAssetObject(primary_asset);
*/*


But I never successfully get my UObject. I’m missing something ?

In an other hand, I find there is an AssetRegistry.bin file in the .pak file. I saw that when we mount a package, the file is loaded in memory and get references to the assets, but here too a got nothing from my file. Is this method still working ?

I also tried some methods with ObjectLibrary, but if I well understood the Asset Manager doc, this is an ancient method and the Asset manager is preferred.

Which method should I prefer today ?

hi, im also interested on this,
I dont have the solution to your problem just wanted to share what i could find

i was able to load assets as dlcs using only blueprints(i did this like one year ago so i dont remember all the details),
the pak file and other files should be uploaded to your server,
be sure to use a valid structure and dont override those of your game
for example if you have /game/shooter/bp_hero
and you want to add an skeletal mesh i would recommend to use a completely different path
/games/shooterMounted/skeletalMesh

you define this structure from the editor when you create your new pak
so i created two projects one for my game and one for the pak assets

there are a lot of options when creating the pak, make sure you export only
the assets you want and not the entire game
also i think i had issues with some materials not loading when using skeletal meshes

i think the pak exporter is actually a command line (its show in the logs after exporting)
so in theory you could do it from windows console
and maybe with python ?

Same here, I just wish there was more documentation on this. Ideally I’d really want to give players an SDK to create their own paks to then upload to an http server for players clients to download and import to useable objects at runtime but the few examples I’ve seen tend to be extremely outdated. Any assistance here would help a ton!

I’ll try to write my progression on this work when I made good advance, in the hope we got more documentation infos/ Unreal dev help.

For the moment, I can create .pak file from assets, and load them at runtime.

Create .PAK:

I use command-line tool provided by Unreal for creating paks files from a second project. First, I cook my content (I’m on macOS):

./UE4Editor-Cmd UPROJECT -run=cook -targetplatform=MacNoEditor

Where UPROJECT is the path to the .uproject file where are yours assets. Then:

./UnrealPak /path/where/your/file/will/be/created/file.pak -create=/path/to/your/cooked/content/folder/ -compress

The cooked asset can be found in the Saved/Cooked/ folder of your project.

Load .PAK:

I use this code to load at runtime:




      IPlatformFile& platform_file = FPlatformFileManager::Get().GetPlatformFile();
      pak_platform_file = **new** FPakPlatformFile();
      TArray<FAssetData> asset_datas;

      **if**(pak_platform_file->Initialize(&platform_file, TEXT("")))  {

            pak_platform_file->InitializeNewAsyncIO();

            FPlatformFileManager::Get().SetPlatformFile(*pak_platform_file);
        

            **const** FString pak_path{FPaths::ProjectContentDir() + TEXT("PakFiles/AssetPak-MacNoEditor.pak")};

            **const** FString mount_point{"/MountPoint/"}; //Where datas are after extraction

            **bool** is_mounted{pak_platform_file->Mount(*pak_path, 0, *mount_point)};

            **if** (is_mounted) {

                FPackageName::RegisterMountPoint("/ABC/", *mount_point);

                FAssetRegistryModule& registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");

                registry.Get().ScanPathsSynchronous({ "/ABC/" });
                registry.Get().GetAssetsByPath("/ABC/", asset_datas, **true**, **false**);

                //for(FAssetData &current_data: asset_datas) { current_data.GetAsset(); }
            } 
     }


Then you can get UObject from the GetAsset() function that will load it.

Important things:

  • It’s look like they are a bug when we try to use a folder that is a root path for Unreal ("/Game/", “/Engine/”, …), which make files will not be find by the scan. So just don’t use them.
  • mount_point must be an existing file on disk.
  • I** have no idea if it’s a good way to do the things or not.**
  • The load of an object will make a warning in logs: ‘asset’ has been saved with empty engine version. The asset will be loaded but may be incompatible.
    I think they are something i do wrong when creating the .pak file, but I don’t know what. Assets are still loaded.

This code works for me … halfway. Indeed, if you try to load simple assets likes Texture2D, you shouldn’t have problems. But when I try to load a Static Mesh for exemple, the reference to his Materials will be missing because Unreal search it at the same place they was in the project where I had my assets, and the log produce this type of warning:
‘…/…/…/…/…/…/…/MountPoint/Content/scene_sphere_binary.uasset’ failed to load ‘/Game/scene_sphere_binary/Wood’: Can’t find file.
And the same is true for Materials → Texture2D.
I’m searching for a way to change the references before load (or load a first time with fail, get references and reload the object), but for the moment I didn’t find anything really conclusive.

Thanks a ton for the info! While I found it very informative, I wasn’t able to get anything to load. The pak will mount but GetAssetsByPath returns zero results for contents of a pak that I know contains valid objects. I’ve also noticed running in standalone mode gives a fatal error. God, I wish there was some more documentation for this kind of stuff!