Any way to get hierarchy of Levels/Sublevels from code/blueprints?

In my level I have several folders with sublevels like:
Persistent level
_Place1
___Floor1
___Floor2
_Place2
___Floor1

And for these sublevels I want to get full logical path like “Place1/Floor2”, but so far all I found is either how to get logical name (Floor1) or actual path (/Game/Maps/Place1_Floor2).

So is there any way of getting that subfolders hierarchy of Level?

I would create a chain of GetOuter()'s until you hit the PersistentLevel, using each ULevel to get further out (GetOuter Of ULevel)

Here’s the relevant code from my RamaSaveSystem Plugin (paid plugin on marketplace) to get you started!

You can pass in an actor that is inside your innermost level to initiate the chain, going outward, then switch to using each ULevel’s GetOuter and listing each package name along the way!

FString URamaSaveComponent::GetActorStreamingLevelPackageName()
{
	AActor* Owner = GetOwner();
	if(!Owner) return "No Owning Actor";
	  
	ULevel* Level = Owner->GetLevel();
	if(!Level) 
	{
		VSCREENMSG("Rama Save System, Level PTR BAD");
		return "No level for Actor apparently! (should not ever see this unless actor pending delete perhaps)";
	}
	
	//Consistency if it is the persistent level
	//! Causes crash if called right as a level is being hidden
	if(Level->IsPersistentLevel())
	{
		//if(WITH_EDITOR) RS_LOG2(RamaSave,"Saved actor in", "PersistentLevel");
		 
		return "PersistentLevel";
	}
	
	//Rama Victory!!!
	UObject* Package = Level->GetOuter(); 
	if(!Package) 
	{ 
		return "PersistentLevel";
	}
	 
	//if(WITH_EDITOR) RS_LOG2(RamaSave,"Saved actor in", *Package->GetName());
	 
	return Package->GetName();
}

so my psuedo code idea:

  1. Obtain an actor inside the innermost level
  2. Use my code above to Actor->GetLevel()
  3. Level->GetOuter() = Result
  4. Result->GetOuter()->GetName (Display it), Result->GetOuter() (recursion)
  5. until Level->IsPersistentLevel()

:sparkling_heart:

Rama

Hi! Thanks for the reply, but that’s not what I’m looking for.

Let’s say I have HierarchyTest level with this hierarchy:
2022-02-22_11-25-39_UE4Editor_22

So for any actor from HierarchyTest_Sublevel1 I want to get path like

HierarchyTest/SubFolder1/HierarchyTest_Sublevel1

ooooh, so you’re saying you want the subfolders which you are creating in the Levels window?

very interesting! I wonder where that info is stored, if the actual levels have any association with that.

You might succeed at getting that info in an editor build, but I am not confident, yet, that it would ship with a packaged game.

If you really want this information in-game, I suggest you extend the Level Blueprint, create a custom variable, and track the folder that “Self” (each level) is in Yourself.

something like this:

/*
	By Rama
*/
 
#pragma once
       
#include "Engine/LevelScriptActor.h"
#include "JoyLevel.generated.h"
  
UCLASS()
class AJoyLevel : public ALevelScriptActor
{
	GENERATED_BODY()
public:
	     
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Joy")
	FString JoyfulFileName = "";
	   
	/** Set this to true when you want to use the copy from umap to data asset option */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Joy")
	bool UseUMapWorldEnvSettings = false;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Joy")
	FString WorldEnv;
	 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Joy")
	float LevelSettings_MagneticSparklesBrightnessMult = 1;
	
public:
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category="Joy Level")
	void SetNewSkyActive(bool NewSky);
	 
};
  1. Open level blueprint of each sublevel
  2. File → reparent → your custom class
  3. Edit Class Defaults toolbar button (along top)
  4. Enter the folder name for the sublevel

That will give you maximum control over this process you are doing, and give you Additional Benefits, because now you can store ANY INFO YOU WANT, per sublevel in your Levels window, and get that info ingame

:sparkles::sparkles::sparkles: annnnd :sparkles::sparkles::sparkles:

get that info in a packaged game with ease!

:heart:

Rama

Some interesting ideas there, thanks for the suggestions!

But actually I don’t care about packaged game, I just wanted to use this to clean up World Outliner while working in editor (put each actor to subfolder with name of the sublevel that actor is stored in).

So following the example from previous message result should look like this.

And this is actually a result of a working script.
So in case anyone stumbles upon this thread in future - here’s how I did it.

Added WorldBrowser to PrivateDependencyModuleNames in my MyProjectEditor.Build.cs

PrivateDependencyModuleNames.AddRange(new string[] { "WorldBrowser" });

Then in one of BPL classes

// Necessary includes
#include "WorldBrowserModule.h"
#include "Engine/WorldComposition.h"
#include "WorldBrowser/Private/LevelCollectionModel.h"

...

// Getting model which contains list of all SubLevels
FWorldBrowserModule& WorldBrowserModule = FModuleManager::GetModuleChecked<FWorldBrowserModule>("WorldBrowser");
TSharedPtr<FLevelCollectionModel> SharedWorldModel = WorldBrowserModule.SharedWorldModel(WorldContextObject->GetWorld());

...

// Now iterating by all SubLevels
for (TSharedPtr<FLevelModel> Child : SharedWorldModel->GetAllLevels())
{
	if (Child.Get()->GetDisplayName() == "Persistent Level")
	{
        // Will return the name of the level - HierarchyTest from the example above
	    FString LevelName = WorldContextObject->GetWorld()->GetMapName();
	}
	else
	{
        // Will return HierarchyTest_Sublevel1 from the example above
		FString SubLevelName = Child.Get()->GetDisplayName();
        // Will return SubFolder1 from the example above
		FName SubLevelPath = Child.Get()->GetFolderPath();
	}
}
	

Now this is quite hacky way since it uses Private stuff of WorldBrowser, but it works well.

Thanks to these topics:

Wow great solution! Very innovative!

Thanks for sharing!

If anyone ever did care about :sparkles: packaged :sparkles:, they could use a Blueprint script (formerly blutility) with an editor trigger to do what you did, iterate through, and automatically assign to level script actor

Then, packaged game would be updated in 1 click no matter how the level evolves!

:heart:

Rama

1 Like