Create a new Level from C++ code

Greetings,

Is it possible to create a new Level (so a new .umap file) directly from C++ code, for example from a plugin’s code ? I would like to automate the level creation (not having to go through the editor => new Level menu) then populate it directly from code, without having to interact with the editor whatsoever.

How can I do that ?

3 Likes

Yes! I had to do this as part of my plugin (although I create a new UWorld not a ULevel).
The way I did it was by creating a template world in my plugin’s Content folder (with basic things like a Light Source and Sky Sphere) and then copy that for each new level. Here’s the rough code I used:

auto WorldTemplateObj = StaticLoadObject(UWorld::StaticClass(), NULL, TEXT("World'/MyPlugin/LevelTemplate.LevelTemplate'"));
ObjectTools::FPackageGroupName PGN;
pgn.ObjectName = MyNewLevelName;
pgn.PackageName = MyNewPackageName;
TSet<UPackage*> ObjectsUserRefusedToFullyLoad;

auto World = CastChecked<UWorld>(ObjectTools::DuplicateSingleObject(WorldTemplateObj, PGN, ObjectsUserRefusedToFullyLoad));

Alternately, you can instead use UWorld::CreateNewWorld, which will give you a truly clean world.

2 Likes

Thank you, worked perfectly !
Any idea how I could go about opening the level in the editor via the C++ code though ? The level properly appears in the content browser (as unsaved though) but my editor still has the previous level opened. Do you know how I could open the new one that I just created without having to double click on it in the assets browser ?

I too am unclear on how to open a World without user interaction. It looks like you might have to use FEditorFileUtils::LoadMap, but the code within looks very thorny.

That looked promising, but I tried this :

FEditorFileUtils::LoadMap(TEXT("World'/Game/Scenes/Shot_42.Shot_42'"));

And it’s not working. I suspect it’s because the Shot_42 map (that I created thanks to your technique !) hasn’t been savec/serialized to a file yet. So, I looked further and found this :

FEditorFileUtils::SaveMap(World, TEXT("World'/Game/Scenes/Shot_42.Shot_42'"));

However, I’m getting a compilation error :

Module.ImportIntoLevel.cpp.obj : error LNK2019: unresolved external symbol "public: static bool __cdecl FEditorFileUtils::SaveMap(class UWorld *,class FString const &)" (?SaveMap@FEditorFileUtils@@SA_NPEAVUWorld@@AEBVFString@@@Z) referenced in function "public: void __cdecl FImportIntoLevelModule::PluginButtonClicked(void)" (?PluginButtonClicked@FImportIntoLevelModule@@QEAAXXZ)
1>D:\Unreal Projects\PythonPluginTest\Plugins\ImportIntoLevel\Binaries\Win64\UE4Editor-ImportIntoLevel.dll : fatal error LNK1120: 1 unresolved externals

I don’t understand why I’m getting this error while FEditorFileUtils::LoadMap works perfectly. Any idea ? And thanks for your help :slight_smile:

That’s a linker error, so usually, it would mean you forgot a module in your build file but it doesn’t here. In this case, looking at FileHelpers.h, it looks like SaveMap is missing its API Specifier UNREALED_API. Compare the following declarations:

static UNREALED_API void LoadMap(const FString& Filename, bool LoadAsTemplate = false, const bool bShowProgress=true);

static bool SaveMap(UWorld* World, const FString& Filename );

I think this is a mistake by the Unreal dev team. A possible messy workaround would be to copy the implementation from FileHelpers.cpp into your own file but this may be infeasible, depending on how many private properties it uses.

An easier route (if it works, I don’t know) would be to save the Persistent Level, FEditorFileUtils::SaveLevel(World->PersistentLevel,FileName), which should create a

1 Like

Sorry for the late answer ! Perfect, I used your easier route and it worked just fine. I called the method without a FileName (because it would crash, for some reason, or not save the map at the right place) and it saved it with the right name where I wanted, which is great. I could then LoadMap and it worked !

Thank you again :slight_smile: