Loading Sub-Level multiple times, possible?

I want to load a sublevel multiple times (load multiple instances of one level). I dont want to copy the level file and rename it e.g. “house_1.umap, house_2.umap, house_3.umap, …”, because then i define the max number of instances and if i want to change something i need to do it in all levels or recopy them, its realy dirty (it should be consistent). I know that the level-umap-name is unique to address the sublevel inside the streaming, thats an obstacle.
Someone has an idea how to solve this problem? :confused:

1 Like

Using streaming levels would be difficult to load them multiple times, but you could just spawn the actors contained in the level multiple times :smiley:

I did such a test and it works kind a nice, though you need a lot of pre-setup and logic for it. But lets go with the step by step idea:

The way I did it was creating levels that are used as a template levels, each level must have its center in the same place so will be able to offset it between levels and with the actors it contains. This is useful once you spawn all actors of that level.

A template level contains only actors that implement an interface I defined (I called it ICloneAble :D), every Actor of an ICloneAble as a method which takes in in parameter to copy over all it values, this seams easy at first but can be very hard to do for each value you want. You could use reflection if you want all or even add a new property flag which dictates if it will be cloned or not.

Another difficult task is references in the template level, I resolved it by creating my new clones following the reference graph of the actors, I did it the simple way by just adding a list in each template actor to maintain references, again this can be automated using reflection.

When cloning the actor within the template level make sure to disable physics (set them all to phys_none and then again to the correct one once you are done with all of’em). Knowing the offset of each level and that the center you can set the translation of the cloned actor easily buy maintaining the same relationship with the new center point where you would like to place your copy level.

While you are doing all your stuff make sure to cleanup any references that you might hold to any actor of your template level, if not you will get a leak in a shipping build or a crash in a development build, the good think is that the crash will tell you where you are leaking ^^.

To be able to optimize memory consumption I always loaded the template level, did the copy/clone process and unloaded it, this way you will only hold it in memory while you are cloning it’s content. If your are reusing that level again and again you could hold them in memory. Make sure to not make your template level visible, you do not want any BeginPlay event to get called on it ^^.

Btw, I used this little system to play around and build a ‘prefab’ like system.

Cheers,
Moss

Just as a side node, the idea in doing in runtime is not very nice performance wise. Doing such a thing in editor time though is similar to what the guys a Yager might be doing with their AssetGroups, in editor time you also have access to every level and you do not need to stream it in.

It can be done, this is how Fortnite loads in some of its dynamically generated level layout, however, it is a bit manual.

This is roughly the Fortnite code for doing so, you may well have to fiddle around with it to get it to work the way you want, I’m afraid there isn’t really a document on how this all works at the moment so your mileage may vary and it may take some fiddling to get it right, but here’s where you’d start with it at any rate.



	ULevelStreamingKismet* StreamingLevel = static_cast<ULevelStreamingKismet*>(StaticConstructObject(ULevelStreamingKismet::StaticClass(), GetWorld(), NAME_None, RF_NoFlags, NULL));

	// Associate a package name.
	StreamingLevel->SetWorldAssetByPackageName(LevelToStream);
	if (GetWorld()->IsPlayInEditor())
	{
		FWorldContext WorldContext = GEngine->GetWorldContextFromWorldChecked(GetWorld());
		StreamingLevel->RenameForPIE(WorldContext.PIEInstance);
	}

	StreamingLevel->LevelColor = FColor::MakeRandomColor();
	StreamingLevel->bShouldBeLoaded	= true;
	StreamingLevel->bShouldBeVisible = true;
	StreamingLevel->bShouldBlockOnLoad = false;
	StreamingLevel->bInitiallyLoaded = true;
	StreamingLevel->bInitiallyVisible = true;

	StreamingLevel->LevelTransform = // where to put it

	StreamingLevel->PackageNameToLoad = // PackageName containing level to load

	FString PackageFileName;
	if( !FPackageName::DoesPackageExist( StreamingLevel->PackageNameToLoad.ToString(), NULL, &PackageFileName ) )
	{
		UE_LOG(LogStreamingLevel, Error, TEXT("trying to load invalid level %s"), *StreamingLevel->PackageNameToLoad.ToString());
		return false;
	}

	StreamingLevel->PackageNameToLoad = FName(*FPackageName::FilenameToLongPackageName(PackageFileName));

	// Add the new level to world.
	GetWorld()->StreamingLevels.Add( StreamingLevel );


1 Like

Wow, Epic employees sure are wizards!

Wow nice! Are there any drawback using the steaming level approach? The only thing that I worry about it is how you position the levels content.

Wow :smiley: so many good answers, big Thanks! I realy like the streaming level approach and i want to implement it. But i need to lern more about, how this hole streaming process works internally on the c++ side. I only find explanations how to use it in the editor but i want to know how it works under the hood.

About Marc’s code: I think i got a rough idea how this code works. It will be great if you guys can help me to understand the details:


ULevelStreamingKismet* StreamingLevel = static_cast<ULevelStreamingKismet*>(StaticConstructObject(ULevelStreamingKismet::StaticClass(), GetWorld(), NAME_None, RF_NoFlags, NULL));

Here we get a basic Level named “StreamingLevel”, which we want to configure and load later?


StreamingLevel->SetWorldAssetByPackageName(LevelToStream);

“LevelToStream” is the name of the level we want to load into the persistent level. What exactly does this set function do? Is this the part where we declare which “LevelToStream”.umap we want to load? The “StreamingLevel” gets a reference to the umap file? Does PackageName means umap level name?


if (GetWorld()->IsPlayInEditor())
{
	FWorldContext WorldContext = GEngine->GetWorldContextFromWorldChecked(GetWorld());
	StreamingLevel->RenameForPIE(WorldContext.PIEInstance);
}

What does this part do?


StreamingLevel->PackageNameToLoad = // PackageName containing level to load

FString PackageFileName;
if( !FPackageName::DoesPackageExist( StreamingLevel->PackageNameToLoad.ToString(), NULL, &PackageFileName ) )
{
	UE_LOG(LogStreamingLevel, Error, TEXT("trying to load invalid level %s"), *StreamingLevel->PackageNameToLoad.ToString());
	return false;
}

StreamingLevel->PackageNameToLoad = FName(*FPackageName::FilenameToLongPackageName(PackageFileName))

What is a package for level streaming? PackageName ?


// Add the new level to world.
GetWorld()->StreamingLevels.Add( StreamingLevel );

“StreamingLevels” is the persistent level, where we want to load our new “StreamingLevel” ? how do i get the persistent level?

How do i address the different levels? Normally the level name is unique and i can get a reference by the name. Where do i make sure the name is unique and how do i know the unique level name, if i want to unload a level? Or is there a better way to address a level?

My plan is too make a custom ALevelScriptActor class with a new load function which implements this code. This way i can use the function in code, but also in the level blueprint.

Can someone help me with my questions? I want to understand this. Maybe Marc or Moss? :wink:

Basically. It is something that can be loaded in and out. You can see how it is set up to immediately start loading, though it could be set up in such a way that you dynamically toggle it in later. You could also potentially use ULevelStreamingAlwaysLoaded if there is no bringing in and out, but I will caveat that with I’m not clear on why Fortnite would want the ability to stream them in and out, so it may be necessary to use the Kismet version for this dynamic purposes, I’m not sure.

I looked at the internals of that function, without a lot of investigation I can’t say immediately what it accomplishes, but I do believe that this is your map file name

When you play a level in the editor (PIE) we have to put some prefixes on the map name so that they don’t conflict with the loaded editor name and so we can have multiples of them for the multiplayer PIE feature. This just sets up the streaming level so that it is using the correctly prefixed name.

This is probably your map name again.

StreamingLevels is an array on the persistent level (i.e. the level in the main map that has the rest of the sublevels streamed in to it) that manages which levels are in memory and unloaded at a given time. Normally it is manipulated from the Levels panel in the editor, but here you are dynamically adding entries to it.

Probably need to hang on to this StreamingLevel pointer. From there you can specify for it to be unloaded by setting the bShouldBeLoaded to false, I believe you can also get a direct reference to the ULevel from the ULevelStreaming object, if you need to look at its specific Actors or some such thing.

As I said, this isn’t necessarily a direct roadmap on getting this working, but hopefully it will point you in the right direction. If I find some time I’ll start up fortnite (though in debug that can be painfully slow) and drop a breakpoint in this function to see what the values that are being fed to the different pieces are.

Good luck.

Marc, is there a simple way to populate the list of levels of a persistent level auto-magically in run-time? I’m thinking about the ‘Asset Groups’ feature the guys at Yager did, using streaming levels as the base template for prefabs would be really nice but adding all levels in a persistent level could be a hell to maintain.

That is roughly how the World Composition feature works. It uses a folder and will pull in each of those levels as sublevels.

I got curious, so I pulled it up:


StreamingLevel->SetWorldAssetByPackageName(LevelToStream);

This sets up a unique name to use for the streaming level. This lets the LevelStreaming object have a name that points to a unique versino of the map that you’ll specify in PackageNameToLoad. It can probably be anything unique, but Fortnite uses the name of the level with some unique identifier at the end so as to make debugging easier.


StreamingLevel->PackageNameToLoad = ...

Set this to the full path to the map package without .umap … so for example /Game/Maps/MySubLevel

Alright, I think that’s as much time as I can spend digging in to it … I hope you can use this information to get the results you want.

So ‘SetWorldAssetByPackageName’ seams to be the right way to begin with.

Thanks Marc ^^

Cheers,
Moss

Thanks Marc for your time! Now i have hope to make this work.
I find it really exciting to see implementation-ideas which are used in big productions. I want to learn more and more about this. :wink:

I got a question if i put my houses and my houses are individual levels where are setup like old school prefabs and i place that in the main world all from the editor always loaded and for example i got around 1.000 houses in the main world this will give me problems of performance or something ?

Would this work if you were to dynamically load up the sub-levels at runtime? (Provided they were relatively simple). My use case is to have the internals of ship variants implemented as individual levels and loaded up as they enter the session. So far I’ve been implementing the ship variants as blueprints though the lighting looks terrible compared to what they could look like with baked lighting.

Is it possible to implement a custom Actor that could be placed in the editor where you could specify the Level Name to be loaded and Vector at which to load it?

PS: For anyone interested, I just came across another thread discussing this topic and the same solution as above: Streaming levels with an offset. - Feedback for Unreal Engine team - Unreal Engine Forums

1 Like

I’ve managed to use the “Create Instance” Blueprint node to spawn 3 instances of my level. I was then able to set the level transform to a specified position. They also appear to be working on client sessions.

I have no idea what the limitations are but will continue testing.

Because the old school prefabs are 3D models (with different models), audio, BSP, Volumes of collisions, triggers and things like Blueprints you can’t use that like a instance system xD
Its a totally different thing. (Are like Blueprints but bigger and with sub blueprints and collisions, triggers and bsp)

BP Node Created For Community

Dear Everyone,

Thanks for this code Marc Audy!

I’ve created a BP node for the community to use which has a special solution for ensuring unique instance names to guarantee that multiple instances of level can be spawned.

65784d2e1f959b9e9b4ea8e4d04f78a466d2e655.jpeg

Victory BP Library Thread

My entire modified C++ code at link above

Enjoy!

Rama

1 Like

Thank you Rama! You are a beautiful person!

This is really awesome… thanks for the input everyone! Some valuable stuff here!