How do I use PrepareMapChange/CommitMapChange?

I am currently loading a level with
UGameplayStatics::OpenLevel((), mapToLoad, 1, “”);

however, this unloads my whole game and reloads it again. I need the game to simply keep my current data but move into a new map/location. I have had a look around and the PrepareMapChange states that “… GameModes where the game state should be preserved through a transition.” which seems to be what I need.

However, I cannot seem to get this working as I get an error (“LogEngine:Warning: PREPAREMAPCHANGE: Couldn’t find package for level ‘Room01’”) when trying to give the array my map names: Room01, Room02… with each of them as a map file in the content/maps. I feel I am missing something so can anyone give me any pointers of how I should be using this correctly?

Anybody used this method at all? Tried to find more information about it but have been struggling. Currently use OpenLevel(…) but it freezes my game (Could do with a loading screen that has some animating icon on) and also re-initializes all my game mode and variables.

You tried with extension *.umap

Yeah, I tried the full file path and it does seem to find it and being loading it with the prepare map change. I put a few checks in and it seems to constantly be loading the maps data up in the background while I can still do whatever I need to (animating loading screens), but when I call CommitMapChange, it seems to do nothing at all and there is simply no output to find out why it doesn’t work.

Still having some trouble with getting this concept working. I am still trying to find a way to load/unload different maps but I am currently still using OpenLevel - Which does work, however the game freezes and the takes sometimes what seems forever to load a level which isn’t suitable really. Is there a specific approach I need to be looking at which suits my needs?

Hi!
Same problem here. I do:
TArray* levels = new TArray < FName >;
levels->Add(“/Game/Maps/RoomNight”);
()->PrepareMapChange(*levels);
which seems to work (with a wrong path there is an error in log). But when I call ()->CommitMapChange(); the game freezes although IsMapChangeReady() is true.
Does anyone see my fault?

I know this is old, but I’ll answer for any future people who
have the same question.

Using PrepareMapChange/CommitChangeMap:

Call ()->PrepareMapChange and give it a list of maps (FName list) starting
with the main (persistent level) first, followed by any sublevels that need to
be loaded at the spawn point. Example:

TArray<FName> Levels;
Levels.Add(MainLevel);
Levels.Add(Sublevel1);
Levels.Add(Sublevel2);

The FNames must be in the form:

FName MainLevel = "/Game/Maps/MyLevelName";

Note that the extension is not being used, and we use “/Game” rather than
“/Content”. If you have a filepath, you can covert it to the needed format,
called a “Long Package Name”, by calling:

FPackageName::FilenameToLongPackageName(MyFilePath)

If you neglect to add a needed sublevel to the list sent to PrepareMapChange,
the call to CommitMapChange will block for however long it takes to load and
set up the neglected level.

To check to see if the maps are done preloading use:

if(()->IsMapChangeReady && !IsAsyncLoading())

That second part is important because IsMapChangeReady tells you if the levels
sent in the list to PrepareMapChange are ready, but not if any sublevels or
assets those maps reference are ready.

When done call:

()->CommitMapChange();

It’s important NOT to call the GEngine version of the same file. The UWorld
version makes sure the call is done the right way.

When CommitMapChange is done, there will be a callback to
GameMode::PostCommitMapChange(). Teleport the player and do any setup
necessary for the new level there. Note that TeleportTo will not change the
orientation of the Player, so you need to set that manually by calling

MyPlayerController->SetControlRotation(LevelPlayerStart->GetActorRotation()); 

How long the switchover takes after calling CommitMapChange depends on how
long the garbage collection takes when clearing the old level. If you’re
exiting a small level, then the transition is very small. On my tests with
large levels, the switch over took about a second and a half. Usually a quick
fade-out, CommitMapChange, fade-in can easily hide the transition.

This method will NOT work in “Play In Editor” mode (PIE). You must run it
in Standalone mode to see it.

Hope this helps.

EDIT: To make this pipeline work in PIE mode:

  1. In EditorEngine.cpp (Engine\Source\Editor\UnrealEd\Private):

In UEditorEngine::Tick:

Look for the first inner loop that contains the line:

FWorldContext &PieContext = *ContextIt;

Near the end of that loop add:

 ConditionalCommitMapChange(PieContext.World());
  1. In UnrealEngine.cpp (Engine\Source\Runtime\Engine\Private):

In UEngine::CommitMapChange:

After:

if(bWasFound)
{

Add:

 if (Context.WorldType == EWorldType::PIE)
      StreamingLevel->RenameForPIE(Context.PIEInstance);

Hi rantrod.

Thanks for this detailed explanation, I’ve made some progress, but I still have some issues.

First of all, after adding the couple of code lines you suggested, I wasn’t able to make this technique work in PIE. By the way, they are 2 inner loop that contains the line FWorldContext PieContext = *ContextIt;

Second problem: The steps you explain successfully loads the Persistent Map i want to load, but not the sub-levels.

Third Problem: When I Load the Map, my old Map is not unloaded. So I have 2 Level Blueprints that are running at the same time. How should I unload the old Map?

Thanks for you help!

The first occurrence of “FWorldContext PieContext = *Contextit;” is the correct one.

The only reason a sub-level would not show up even if it’s added into the array you send to PrepareMapChange, is if there is no reference to that sub-level in the segment you’re loading into (i.e. they get garbage collected).

The old levels get garbage collected when you no longer reference them.

If you need more of a “clean slate” approach where everything you used to have is deleted, replaced by a new level, and you’re put into the new level’s game start, then I would recommend a different approach.

Great tutorial but i would love to see some full code so its easier for others just to copy paste this or something :slight_smile:

Thanks anyways really appriciate it.

Also, if you want a nice UI too selelect your levels instead of write them as strings you can use:

TAssetPtr<UWorld> LevelAsset;

And get the package name like this:

FString LevelPath = LevelAsset.GetLongPackageName();

And add it to the levels array:

Levels.Add(*LevelPath);

Sorry to dig this post 3years later but this is really interesting for me. Everything seems to be working just fine except for one thing ! The
virtual void PostCommitMapChange();
That I included in my game mode is never called. It doesnt seem to be overridable too so I guess something changed in the use of this whole system. Could someone help me out figuring this out ?
Else I guess I will simply call the gamemode within the class and tell me to go on.