Working with Levels in Unreal Engine

How to work with Levels in the Unreal Engine?

Levels contain everything a player can see and interact with, like environments, usable objects, other characters, and so on.

Sublevels

When working on Unreal Engine 4 legacy projects or non-game projects such as architectural visualization, you can use the Levels window for level management. For game development in Unreal Engine 5.0 and beyond, the Levels window has been made obsolete by World Partition. This page covers managing multiple levels through the Levels window.

Despate that if sublewels are still in used, they can be managed through C++.

Add Sublevel To Editor World

The code below is C++ way to move Level from Content Drawer window to Levels window. As it's processed in the editor, the code below must be in UBlueprintFunctionLibrary inherited class.

/**
 * Editor Only - Will not work in packaged build.
 * 
 * Add a sublevel to the world currently open in the editor
 *
 * @param SublevelPath  Path of the level to add: "/Game/Folder/MyLevel"
 * @param OutInfoMsg   More information about the action's result
 * @param return  If the action was a success or not
 */
UFUNCTION(BlueprintCallable, Category = "Add Sublevel From World")
static bool AddSublevelToEditorWorld(FString SublevelPath, FString& OutInfoMsg);
#include "EditorLevelUtils.h" // UnrealEd
#include "Editor/EditorEngine.h" // UnrealEd
#include "Engine/LevelStreamingAlwaysLoaded.h" // Engine

bool UExampleClass::AddSublevelToEditorWorld(FString SublevelPath, FString& OutInfoMsg)
{
  // Find world
  UWorld* World = GEditor->GetEditorWorldContext().World();
  
  // Check if level is valid
  UWorld* LevelToAdd = Cast<UWorld>(StaticLoadObject(UWorld::StaticClass(), nullptr, *SublevelPath));
  if (LevelToAdd == nullptr)
  {
    OutInfoMsg = FString::Printf(TEXT("Add Sublevel To Editor World Failed - Path doesn't lead to a valid Level. '%s'"), *SublevelPath);
    return false;
  }

	// Add level
	ULevelStreaming* Level = EditorLevelUtils::AddLevelToWorld(World, *SublevelPath, ULevelStreamingAlwaysLoaded::StaticClass());
	
  // Return
	bool bSuccess = Level != nullptr;
	OutInfoMsg = FString::Printf(TEXT("Add Sublevel To Editor World %s - '%s'"), *FString(bSuccess ? "Succeeded" : "Failed"), *SublevelPath);
  return bSuccess;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Remove Sublevel from Editor World

The code below removes a (sub)Level from Levels window.

/**
 * Editor Only - Will not work in packaged build.
 * 
 * Remove a sublevel from the world currently open in the editor
 *
 * @param SublevelName  Name of the level to remove
 * @param OutInfoMsg  More information about the action's result
 * @param return  If the action was a success or not
 */
UFUNCTION(BlueprintCallable, Category = "Remove Sublevel From World")
static bool RemoveSublevelFromEditorWorld(FString SublevelName, FString& OutInfoMsg);
#include "EditorLevelUtils.h" // UnrealEd
#include "Editor/EditorEngine.h" // UnrealEd
#include "Engine/LevelStreamingAlwaysLoaded.h" // Engine

bool UExampleClass::RemoveSublevelFromEditorWorld(FString SublevelName, FString& OutInfoMsg)
{
  // Find world and level
  UWorld* World = GEditor->GetEditorWorldContext().World();
  ULevel* Sublevel = UExampleClass::GetSublevelFromWorld(World, SublevelName, bOutSuccess, OutInfoMsg);
  if (Sublevel == nullptr) return false;
	
  // Remove level
  EditorLevelUtils::RemoveLevelFromWorld(Sublevel);

  // Return
  OutInfoMsg = FString::Printf(TEXT("Remove Sublevel From Editor World Succeeded - '%s'"), *SublevelName);
  return true;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Set Sub-Level Settings with C++

The code below is C++ way to adjust Sublevel (Lighting Scenario, lock, loading option, visibility...) and its Level Details options. As it's processed in the editor, the code below must be in UBlueprintFunctionLibrary inherited class.

/**
 * Editor Only - Will not work in packaged build.
 * 
 * Sets the settings of a sublevel in the world currently open in the editor
 *
 * @param SublevelName  Name of the level
 * @param bIsLightingScenario If true, level will be set as a lighting scenario
 * @param Location  Location of the level
 * @param Yaw Rotation Z of the level
 * @param Color Color displayed in the level outliner
 * @param bIsLocked If true, level will be marked as locked
 * @param bIsVisible  If true, level will be visible
 * @param bAlwaysLoaded If true, level will be set AlwaysLoaded. Otherwise BlueprintLoaded
 * @param bIsInitiallyLoaded  If true, level will be loaded by default when the game starts. Only for BlueprintLoaded levels.
 * @param bIsInitiallyVisible If true, level will be visible by default when the game starts. Only for BlueprintLoaded levels.
 * @param OutInfoMsg  More information about the action's result
 * @param return If the action was a success or not
 */
	UFUNCTION(BlueprintCallable, Category = "Set Sublevel Settings")
	static bool SetSublevelSettings(FString SublevelName, bool bIsLightingScenario, FVector Location, float Yaw, FLinearColor Color, bool bIsLocked, bool bIsVisible, bool bAlwaysLoaded, bool bIsInitiallyLoaded, bool bIsInitiallyVisible, FString& OutInfoMsg);
#include "Editor/EditorEngine.h" // UnrealEd
#include "Engine/Level.h" // Engine
#include "EditorLevelUtils.h" // UnrealEd
#include "LevelUtils.h" // Engine
#include "Engine/LevelStreamingAlwaysLoaded.h" // Engine
#include "Engine/LevelStreamingDynamic.h" // Engine

bool UExampleClass::SetSublevelSettings(FString SublevelName, bool bIsLightingScenario, FVector Location, float Yaw, FLinearColor Color, bool bIsLocked, bool bIsVisible, bool bAlwaysLoaded, bool bIsInitiallyLoaded, bool bIsInitiallyVisible, FString& OutInfoMsg)
{
  // Find world and level
  UWorld* World = GEditor->GetEditorWorldContext().World();
  ULevel* Sublevel = UExampleClass::GetSublevelFromWorld(World, SublevelName, bOutSuccess, OutInfoMsg);
  ULevelStreaming* LevelStreaming = ULevelStreaming::FindStreamingLevel(Sublevel); 
  if (Sublevel == nullptr || LevelStreaming == nullptr) return false;
	
  // Lighting Scenario
  Sublevel->SetLightingScenario(bIsLightingScenario);

  // Transform
  FLevelUtils::SetEditorTransform(LevelStreaming, FTransform(FRotator(0.0f, Yaw, 0.0f), Location));

  // Color
  LevelStreaming->LevelColor = Color;

  // Locked
  if (FLevelUtils::IsLevelLocked(Sublevel) != bIsLocked) FLevelUtils::ToggleLevelLock(Sublevel);
	
  // Visibility
  EditorLevelUtils::SetLevelVisibility(Sublevel, bIsVisible, true);

  // Streaming Method (This replaces the existing LevelStreaming)
  TSubclassOf<ULevelStreaming> DesiredClass = bAlwaysLoaded ? ULevelStreamingAlwaysLoaded::StaticClass() : ULevelStreamingDynamic::StaticClass();
  if (LevelStreaming->GetClass() != DesiredClass) LevelStreaming = UEditorLevelUtils::SetStreamingClassForLevel(LevelStreaming, DesiredClass);
	
  // Initially loaded and visible
  ULevelStreamingDynamic* DynamicLevelStreaming = Cast(LevelStreaming);
  if (DynamicLevelStreaming != nullptr)
  {
    DynamicLevelStreaming->bInitiallyLoaded = bIsInitiallyLoaded;
    DynamicLevelStreaming->bInitiallyVisible = bIsInitiallyVisible;
  }

  // Return
  OutInfoMsg = FString::Printf(TEXT("Set Sublevel Settings Succeeded - '%s'"), *SublevelName);
  return true;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Spawn Actors In Sub-Level

As we work right in the editor, out of runtime, we use UBlueprintFunctionLibrary inherited class.

/**
 * Editor Only - Will not work in packaged build.
 * 
 * Spawn an actor into a sublevel of a world
 *
 * @param ActorClassPath	Path to the Blueprint Actor to spawn
 * @param SublevelName	Name of the level in which you want to spawn the actor
 * @param bOutSuccess	If the action was a success or not
 * @param OutInfoMsg	More information about the action's result
 *
 * @return The spawned actor
 */
UFUNCTION(BlueprintCallable, Category = "Spawn Actors In Sublevel")
static AActor* SpawnActorInSublevel(FString ActorClassPath, FString SublevelName, bool& bOutSuccess, FString& OutInfoMsg);
#include "Engine/World.h" // Engine
#include "Engine/Level.h" // Engine
#include "Editor/EditorEngine.h" // UnrealEd
AActor* UExampleClass::SpawnActorInSublevel(FString ActorClassPath, FString SublevelName, bool& bOutSuccess, FString& OutInfoMsg)
{
  // Load blueprint asset
  UBlueprint* BlueprintActor = Cast<UBlueprint>(StaticLoadObject(UBlueprint::StaticClass(), nullptr, *ActorClassPath));
  if (BlueprintActor == nullptr || !BlueprintActor->GeneratedClass->IsChildOf(AActor::StaticClass()))
  {
    bOutSuccess = false;
    OutInfoMsg = FString::Printf(TEXT("Spawn Actor In World Failed - Path doesn't lead to a valid Actor. '%s'"), *ActorClassPath);
    return nullptr;
  }
    
  // Find world and level
  UWorld* World = GEditor->GetEditorWorldContext().World();
  ULevel* Sublevel = GetSublevelFromWorld(World, SublevelName, bOutSuccess, OutInfoMsg);
  if (Sublevel == nullptr) return nullptr;
	
  // Spawn actor
  FActorSpawnParameters SpawnParameters;
  SpawnParameters.OverrideLevel = Sublevel;

  //AActor* SpawnedActor = World->SpawnActor(ActorClass, nullptr, nullptr, SpawnParameters); // Alternate way for TSubclassOf<AActor> ActorClass
  AActor* SpawnedActor = World->SpawnActor(BlueprintActor->GeneratedClass, nullptr, nullptr, SpawnParameters);

  // Return actor
  bOutSuccess = true;
  OutInfoMsg = FString::Printf(TEXT("Spawn Actor In Sublevel Succeeded - '%s'"), *SublevelName);
  return SpawnedActor;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Get Sublevel from world (Help function)

/**
 * Get sublevel from world
 *
 * @param World		World from which you want to get the level
 * @param SublevelName	Name of the level you want
 * @param bOutSuccess	If the action was a success or not
 * @param OutInfoMsg	More information about the action's result
 *
 * @return The level
 */
UFUNCTION(BlueprintCallable, Category = "Spawn Actors In Sublevel")
static class ULevel* GetSublevelFromWorld(class UWorld* World, FString SublevelName, bool& bOutSuccess, FString& OutInfoMsg);
#include "Engine/World.h" // Engine
#include "Engine/Level.h" // Engine
#include "Editor/EditorEngine.h" // UnrealEd

ULevel* UExampleClass::GetSublevelFromWorld(UWorld* World, FString SublevelName, bool& bOutSuccess, FString& OutInfoMsg)
{
  // Make sure world is valid
  if (World == nullptr)
  {
    bOutSuccess = false;
    OutInfoMsg = FString::Printf(TEXT("Get Sublevel From World Failed - World is not valid. '%s'"), *SublevelName);
    return nullptr;
  }

  // Find level
  ULevel* Sublevel = nullptr;
  for (ULevel* PotentialLevel : World->GetLevels())
  {
    if (PotentialLevel->GetOuter()->GetName() == SublevelName)
    {
      Sublevel = PotentialLevel;
      break;
    }
  }

  if (Sublevel == nullptr)
  {
    bOutSuccess = false;
    OutInfoMsg = FString::Printf(TEXT("Get Sublevel From World Failed - Sublevel doesn't exist in world. '%s'"), *SublevelName);
    return nullptr;
  }

  bOutSuccess = true;
  OutInfoMsg = FString::Printf(TEXT("Get Sublevel From World Succeeded - '%s'"), *SublevelName);
  return Sublevel;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Move / Copy Actors Between Sub-Level

An C++ way to make manual action of moving selecting actors to a selected sublevel (selectings actors in World Outliner window and then choosing a sublevel at Levels window, pressing right mouse button on it and choosing option Move selected Actors to Level). As it's processed in the editor, the code below must be in UBlueprintFunctionLibrary inherited class.

/**
 * Editor Only - Will not work in packaged build.
 * 
 * Move or copy actors into a sublevel of a world
 *
 * @param Actors  Actors you want to move
 * @param SublevelName  Name of the level in which you want to move the actor
 * @param bCopyActor  If true, will copy the actor instead of moving it
 * @param OutInfoMsg  More information about the action's result
 * @param return If the action was a success or not
 */
UFUNCTION(BlueprintCallable, Category = "Move Copy Actors To Sublevels")
static bool MoveOrCopyActorsToSublevel(TArray Actors, FString SublevelName, bool bCopyActor, FString& OutInfoMsg);
#include "EditorLevelUtils.h" // UnrealEd
bool UExampleClass::MoveOrCopyActorsToSublevel(TArray<AActor*> Actors, FString SublevelName, bool bCopyActor, FString& OutInfoMsg)
{
  // Remove invalid elements and make sure we still have elements to move
  Actors.Remove(nullptr);
  if (Actors.Num() == 0)
  {
    OutInfoMsg = FString::Printf(TEXT("Move Or Copy Actors To Sublevel Failed - No valid actors. '%s'"), *SublevelName);
    return false;
  }

  bool bSuccess;
  // Find world and level
  UWorld* World = Actors[0]->GetWorld();
  ULevel* Sublevel = UExampleClass::GetSublevelFromWorld(World, SublevelName, bSuccess, OutInfoMsg);

  if (Sublevel == nullptr) return false;

  // Move or copy actor
  int NumProcessedActors = 0;
  if (bCopyActor){
    NumProcessedActors = UEditorLevelUtils::CopyActorsToLevel(Actors, Sublevel);
  } else {
    NumProcessedActors = UEditorLevelUtils::MoveActorsToLevel(Actors, Sublevel);
  }

  bSuccess = NumProcessedActors == Actors.Num();
  FString SucceededOrFailed = bSuccess ? "Succeeded" : "Failed";
  OutInfoMsg = FString::Printf(TEXT("Move Or Copy Actors To Sublevel %s - '%s'"), *SucceededOrFailed, *SublevelName);
  return bSuccess;
}
PrivateDependencyModuleNames.AddRange
(
  new string[] 
  { 
    // Default Modules
    "Core", 
    "CoreUObject", 
    "Engine", 

    // New Modules - Editor Only
    "UnrealEd",
  }
);

Lock / unlock level

#include "Kismet/BlueprintFunctionLibrary.h" // Engine
 /**
 * Lock or unlock a level
 *
 * @param LevelName				  Name of the level to lock or unlock
 * @param bLockLevel			  If true, will lock the level. Otherwise unlock
 * @param bOutSuccess			  If the action was a success or not
 * @param OutInfoMsg		    More information about the action's result
 */
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Lock Actor And Level")
static void LockLevel(FString LevelName, bool bLockLevel, bool& bOutSuccess, FString& OutInfoMsg);
#include "EngineUtils.h" // Engine
#include "LevelUtils.h" // Engine

void UExampleClass::LockLevel(FString LevelName, bool bLockLevel, bool& bOutSuccess, FString& OutInfoMsg)
{
	// Get the world
	UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
	if (World == nullptr)
	{
		bOutSuccess = false;
		OutInfoMsg = FString::Printf(TEXT("Lock Level Failed - World is not valid"));
		return;
	}

	// Find level to lock based on level name
	ULevel* Level = nullptr;
	for (ULevel* Lvl : World->GetLevels())
	{
		if (Lvl->GetOuter()->GetName() == LevelName)
		{
			Level = Lvl;
			break;
		}
	}

	// Make sure you found a valid level
	if (Level == nullptr)
	{
		bOutSuccess = false;
		OutInfoMsg = FString::Printf(TEXT("Lock Level Failed - Level is not valid"));
		return;
	}

	// Already right state?
	if (FLevelUtils::IsLevelLocked(Level) != bLockLevel)
	{
		// Set new lock state
		FLevelUtils::ToggleLevelLock(Level);
	}

	bOutSuccess = true;
	OutInfoMsg = FString::Printf(TEXT("Lock Level Succeeded"));
}