SuperAPI AI Directory

levels-and-world-partition

Coding & development

View repository

Updated 11-06 16:23

About this skill

Levels & World Partition

A UWorld is the running game world. It always has one persistent level (UWorld::PersistentLevel) and any number of streamed levels. For large worlds, World Partition — the default for new projects in UE5 — replaces hand-managed sublevels with automatic spatial streaming around streaming sources.

When to use this skill

  • Organizing world content across levels and keeping memory under control.
  • Building an open world or large level that cannot be fully resident.
  • Toggling sets of actors at runtime (day/night, story states, destruction phases).
  • Making reusable, instanced level chunks (buildings, modular areas).
  • Working with legacy explicit sublevels in a project that predates World Partition.

Core mental model

UWorld
├── PersistentLevel (ULevel)          ← always loaded; world settings, managers
├── UWorldPartition                   ← present if WP is enabled
│   ├── runtime grid (spatial hash)   ← cells loaded/unloaded by streaming sources
│   ├── UDataLayerManager             ← controls Data Layer states
│   └── actor descriptor registry     ← OFPA: each actor = its own .uasset file
└── StreamingLevels [ ]               ← present when using legacy sublevels
    └── ULevelStreaming → ULevel       ← one per streamed sublevel

UWorld is accessible via AActor::GetWorld() anywhere in gameplay code. UWorld::GetWorldPartition() returns the UWorldPartition object (null for non-WP worlds). UWorld::GetDataLayerManager() returns the UDataLayerManager (World Partition only).

Two streaming models

Model Best for Mechanism
World Partition (modern) open worlds, large levels engine auto-streams grid cells around streaming sources
Sublevels (legacy/explicit) small games, hand-authored zones you load/unload named levels in code, Blueprint, or via Streaming Volumes

World Composition (pre-UE5) is superseded by World Partition. Do not start new projects with it.

World Partition

World Partition stores the entire world in a single persistent level. At runtime a spatial-hash grid subdivides the world into cells. Cells load and unload automatically as streaming sources (player controllers by default) move through the world.

Runtime grid settings

Configured in World Settings → World Partition Setup. Key fields per grid:

  • Cell Size — spatial footprint of one streaming cell (e.g. 25 600 cm = 256 m default).
  • Loading Range — radius around a streaming source inside which cells are loaded.
  • Block on Slow Streaming — stalls the game thread if cells are loading too slowly.

Streaming sources

APlayerController is a streaming source by default (opt out via Enable Streaming Source in its Details). For other viewpoints — cinematic cameras, portal previews, teleport destinations — add UWorldPartitionStreamingSourceComponent to any actor:

// Acquiring the component (it is added in the editor or at construction)
UWorldPartitionStreamingSourceComponent* Src =
    GetComponentByClass<UWorldPartitionStreamingSourceComponent>();

// Enable/disable at runtime
Src->EnableStreamingSource();
Src->DisableStreamingSource();

// Poll completion (e.g. before teleporting a player)
if (Src->IsStreamingCompleted())
{
    // cells are ready
}

Source: Runtime/Engine/Classes/Components/WorldPartitionStreamingSourceComponent.h: EnableStreamingSource:28, DisableStreamingSource:32, IsStreamingCompleted:44.

Checking WP state in C++

if (UWorldPartition* WP = GetWorld()->GetWorldPartition())
{
    // World is a partitioned world
}
UDataLayerManager* DLM = GetWorld()->GetDataLayerManager(); // null for non-WP worlds

Source: Runtime/Engine/Classes/Engine/World.h:2955 (GetWorldPartition), :2963 (GetDataLayerManager).

Data Layers

Data Layers group actors so entire sets can be enabled/disabled as a unit.

Type Purpose Modifiable at runtime?
Runtime gameplay toggling (day/night, destruction, quest states) yes
Editor organization in the editor no

Define a Data Layer as a UDataLayerAsset (a UDataAsset subclass). Each world creates UDataLayerInstance objects owned by UDataLayerManager. Three runtime states: Unloaded → Loaded → Activated.

Changing Data Layer state from C++ (5.3+):

// Preferred: UDataLayerManager (replaces deprecated UDataLayerSubsystem)
UDataLayerManager* DLM = GetWorld()->GetDataLayerManager();
if (DLM)
{
    const UDataLayerInstance* Instance = DLM->GetDataLayerInstanceFromAsset(MyDataLayerAsset);
    DLM->SetDataLayerInstanceRuntimeState(Instance, EDataLayerRuntimeState::Activated);
}

EDataLayerRuntimeState values: Unloaded, Loaded, Activated (Runtime/Engine/Public/WorldPartition/DataLayer/DataLayerInstance.h:23).

UDataLayerSubsystem exists for Blueprint compatibility but is deprecated since 5.3. Prefer UDataLayerManager in new C++ code.

See references/data-layers-and-hlod.md for HLOD interaction, load-filter flags, replication rules, and extended examples.

Level Instances & Packed Level Actors

Level Instances (ALevelInstance) embed a .umap as a reusable prefab. One edit propagates to every instance in the world. At runtime, levels using OFPA are embedded into the World Partition grid (default); levels without OFPA use level-streaming mode at a higher runtime cost.

Packed Level Actors (APackedLevelActor, a subclass of ALevelInstance) bake all Static Meshes from a Level Instance into a single ISM-backed actor — ideal for static buildings or dense visual assemblies where instancing is valuable.

Both types participate in World Partition Data Layers: actors inside the Level Instance inherit the Data Layer assigned to the Level Instance Actor.

Source:

  • Runtime/Engine/Public/LevelInstance/LevelInstanceActor.h:18 — ALevelInstance
  • Runtime/Engine/Public/PackedLevelActor/PackedLevelActor.h:25 — APackedLevelActor

See references/level-instances.md for runtime-behavior differences, in-context editing workflow, and property overrides.

One File Per Actor (OFPA)

With World Partition enabled, every actor is serialized to its own file under __ExternalActors__/ alongside the level package. Consequences:

  • Multiple developers can edit different actors without locking the level file.
  • Source control changelists contain many small files with encoded names; use the in-editor View Changelist window for human-readable names.
  • At cook time, all external actor files are embedded back into the level package.
  • Non-OFPA actors inside a Level Instance fall back to Level Streaming mode at runtime.

OFPA is enabled automatically with World Partition; set bUseExternalActors = true (Runtime/Engine/Classes/Engine/Level.h:445) to enable it on a non-partitioned level.

Legacy sublevel streaming

For projects without World Partition, use UGameplayStatics to load/unload sublevels:

#include "Kismet/GameplayStatics.h"

// Load async; bMakeVisible=true shows it immediately, bShouldBlockOnLoad=false = async
UGameplayStatics::LoadStreamLevel(this, TEXT("Zone_Forest"),
    /*bMakeVisible*/ true, /*bShouldBlockOnLoad*/ false, FLatentActionInfo());

// Unload async
UGameplayStatics::UnloadStreamLevel(this, TEXT("Zone_Forest"),
    FLatentActionInfo(), /*bShouldBlockOnUnload*/ false);

Source: Runtime/Engine/Classes/Kismet/GameplayStatics.h:306 (LoadStreamLevel), :314 (UnloadStreamLevel).

Level Streaming Volumes (ALevelStreamingVolume) trigger load/unload automatically when the player enters/exits; wired via the Levels window. Best for smaller games with discrete zones.

ULevelStreaming (Runtime/Engine/Classes/Engine/LevelStreaming.h:138) is the underlying object that tracks each sublevel's load state; UWorld::StreamingLevels holds the array.

See references/world-partition-streaming.md for the full WP streaming lifecycle, console commands for debugging, and notes on server-side streaming.

Decision guide

Scenario Recommended approach
Small/linear game, explicit zones Persistent level + legacy streaming sublevels
Open world or very large level World Partition (default cell grid)
Variant world states Runtime Data Layers
Reusable repeating structures Level Instances / Packed Level Actors
Level Instance inside an open world ALevelInstance with OFPA → Embedded Mode

Gotchas

  • Everything in one level — memory blowout; add World Partition or explicit sublevels.
  • Hard pointer to an actor in another WP cell — the pointer becomes stale when the cell unloads; use TSoftObjectPtr and re-acquire via async-loading / asset-management.
  • Blocking loads (bShouldBlockOnLoad = true) on the game thread cause hitches; stream async and gate on the latent callback or IsStreamingCompleted.
  • OFPA + source control — thousands of tiny files; configure VCS ignore rules for generated directories and never commit from outside the editor.
  • World Composition for new projects — deprecated path; use World Partition instead.
  • UDataLayerSubsystem in 5.3+ — fully deprecated; all paths must use UDataLayerManager from UWorld::GetDataLayerManager().
  • Level Blueprint actors in WP — any actor referenced from a Level Blueprint is treated as Always Loaded, which defeats streaming; prefer Blueprint Classes.
  • Data Layer state replication — states for unfiltered (server+client) Data Layers must be set on the server; Client-Only layers must be set on the client. Setting on the wrong side has no effect (see UDataLayerManager::SetDataLayerInstanceRuntimeState).

Version notes

  • World Partition and OFPA are the default for all Games-category project templates in UE 5.0+. Streaming can be disabled (World Settings → Enable Streaming) while keeping the WP architecture (useful for small games that still want OFPA).
  • UDataLayerSubsystem deprecated 5.3; UDataLayerManager is the API for 5.3+.
  • TargetHLODLayers on UWorldPartitionStreamingSourceComponent deprecated 5.4; use TargetGrids instead.
  • ECurrentState on ULevelStreaming deprecated 5.2; use ELevelStreamingState.

References & source material

Engine source (UE 5.8, Engine/Source/):

  • Runtime/Engine/Classes/Engine/World.h:931 — UWorld; :953 PersistentLevel; :1001 StreamingLevels; :2955 GetWorldPartition; :2963 GetDataLayerManager.
  • Runtime/Engine/Classes/Engine/Level.h:419 — ULevel; :445 bUseExternalActors (OFPA).
  • Runtime/Engine/Classes/Engine/LevelStreaming.h:138 — ULevelStreaming.
  • Runtime/Engine/Classes/Kismet/GameplayStatics.h:306 — LoadStreamLevel; :314 UnloadStreamLevel.
  • Runtime/Engine/Public/WorldPartition/WorldPartition.h:152 — UWorldPartition.
  • Runtime/Engine/Public/WorldPartition/WorldPartitionSubsystem.h:63 — UWorldPartitionSubsystem.
  • Runtime/Engine/Public/WorldPartition/DataLayer/DataLayerAsset.h:29 — UDataLayerAsset.
  • Runtime/Engine/Public/WorldPartition/DataLayer/DataLayerInstance.h:59 — UDataLayerInstance; :23 EDataLayerRuntimeState.
  • Runtime/Engine/Public/WorldPartition/DataLayer/DataLayerManager.h:46 — UDataLayerManager; :82 SetDataLayerInstanceRuntimeState.
  • Runtime/Engine/Public/WorldPartition/DataLayer/DataLayerSubsystem.h:26 — UDataLayerSubsystem (deprecated 5.3).
  • Runtime/Engine/Classes/Components/WorldPartitionStreamingSourceComponent.h:16 — UWorldPartitionStreamingSourceComponent.
  • Runtime/Engine/Public/LevelInstance/LevelInstanceActor.h:18 — ALevelInstance.
  • Runtime/Engine/Public/PackedLevelActor/PackedLevelActor.h:25 — APackedLevelActor.

Official docs (UE 5.8, all fetched and confirmed):

Deep-dive references in this skill: