[UE5] World Partition and Level Streaming: Load Less of Your Map

Created: 2025-12-12Last updated: 2026-07-20

In UE5, World Partition is the default for large maps, while manual Level Streaming remains the tool for things like swapping into interiors. Covers grid-based loading, One File Per Actor and always-loaded actors, Data Layers, how to use Load Stream Level, and a hands-on where you stream in a building interior.

As your map grows, editor startup slows down and the wait before play gets longer. The cause is straightforward: the entire level is being loaded into memory at once. The player is standing in one corner, but the town and the forest on the far side are all in memory too.

There's only one direction for a fix. Load only what's nearby and discard the rest. UE5 gives you World Partition, which does this automatically, and Level Streaming, which lets you switch on your own timing. This article covers where both stand in UE5, when to use which, and how to build a transition from outdoors into a building interior.

Top-down view of a large map where only the grid cells around the player are colored in and distant cells are blank

What You'll Learn

  • Where UE5 stands today: World Partition is the default for large maps
  • How World Partition works ( divide into a grid, load only what's near )
  • Using Data Layers to swap content by time of day or event
  • Where manual Level Streaming is still the right tool
  • Hands-on: streaming in a building interior as the player enters

Sponsored

Why Split at All: Memory and CPU, Not Draw

The previous article, LOD, Culling, and Occlusion, covered techniques for not drawing things that are in your level. Culled objects aren't rendered, but they're still in memory.

Nested diagram showing that only a small fraction of all loaded actors get rendered: culling reduces drawing but not memory

Keeping everything loaded leaves these costs on the table.

Remaining costWhat it means
MemoryMeshes, textures, and materials all stay resident
CPUTicking actors keep running even offscreen
Initial load timeEverything has to finish loading before play starts
Editor sluggishnessEvery actor loads each time you open the level

Streaming addresses all four. If culling means "don't draw it," streaming means "don't hold it." They cover different ground, so neither substitutes for the other.


Where UE5 Stands: World Partition Is the Default

There's a lot of outdated information floating around here, so let's get it straight first.

In UE4, handling a large map meant either World Composition (tiling levels side by side) or manual Level Streaming (loading sublevels yourself). Both assume a human splits the level by hand.

UE5.0 introduced World Partition, and that assumption changed. You keep the level as a single unit, and the engine handles the splitting. World Composition was deprecated, and World Partition became the standard way to build large maps.

Side-by-side comparison of UE4's World Composition and manual Level Streaming against UE5's World Partition, where the engine does the splitting instead of a person

When creating a new level from File → New Level, choosing Open World or Empty Open World gives you World Partition enabled. Basic and Empty Level do not.

TemplateWorld Partition
Open WorldEnabled (with landscape and lighting)
Empty Open WorldEnabled (empty contents)
BasicDisabled
Empty LevelDisabled

And there's one important restriction. In a World Partition level, the traditional Levels panel workflow for managing sublevels is unavailable. You can't use Window → Levels to attach sublevels to a persistent level.

What you use instead is Level Instance (placing a level inside a level like an actor) and Data Layers, covered next. You can't mix both approaches in one level, so decide which way you're going before you start building the map.

Sponsored

How World Partition Works

World Partition divides the level into a grid and loads only the cells near the player.

Diagram of a grid-divided map where only the cells overlapping the Loading Range circle centered on the player are loaded

The mechanics are simple.

  1. Divide the whole level into fixed-size cells (default 12,800uu per side, or 128 meters)
  2. Each actor belongs to the cell it sits in
  3. Take a Loading Range around the player (default 25,600uu, or 256 meters)
  4. Load only the cells that range touches, and discard the rest

You can check the settings under World Partition Setup in World Settings. Smaller cells mean finer-grained loading and less memory, but more frequent swapping. Start with the defaults and only tune once memory becomes a problem.

Watching It Work

You can visualize the grid and loading state during play. Run this in the console.

wp.Runtime.ToggleDrawRuntimeHash2D

A top-down grid appears on screen, with loaded cells colored and unloaded cells darkened. Walk around and you'll see cells swap in and out as they follow the player. Watching this display for one minute is the fastest way to understand what World Partition is actually doing.

Run the same command again to hide it.

Loading in the Editor

World Partition levels behave the same way in the editor. When you open the level, no actors are loaded by default. Pick the region you want to work in via the World Partition editor (Window → World Partition), right-click, and choose Load Selected Cells.

"I opened the level and nothing showed up" isn't a bug. It's the design. It's also why a huge map opens in a couple of seconds.


Actors Work Differently: OFPA and Always-Loaded

World Partition also changes how actors are saved. This matters directly for collaborating over Git and for anything you want to keep resident.

One File Per Actor (OFPA)

In a World Partition level, each actor placed in the level gets saved to its own separate file (in the __ExternalActors__ folder). Traditionally every actor was packed into a single large .umap. This is an editor-side, source-control-friendly storage format; at cook time everything gets bundled back into the level.

Comparison of the traditional single umap containing all actors versus OFPA, where each actor becomes its own file

The payoff shows up in team development. Instead of everyone fighting over one .umap, files don't collide as long as people are touching different actors. But since actors become a large number of small files, set up your .gitignore and LFS configuration properly if you're managing this with Git.

Things You Want Always Loaded

World Partition streams actors in and out by distance. But some things need to be present regardless of distance: a manager that runs the whole game, an actor holding the score, an actor playing background music. It's a problem if these unload every time you walk away.

For those actors, turn off Is Spatially Loaded in the details panel (World Partition category). That removes them from the grid's distance streaming and keeps them permanently loaded. GameMode and GameState aren't affected since they're spawned at play start rather than placed in the level, but when you place your own manager Actor in a level, forgetting this gives you a "manager that sometimes disappears" and a bug that's hard to trace.


Data Layers: Switching by Something Other Than Distance

The grid decides loading by distance. But some content needs to swap on criteria distance can't express. A town that looks different by day and night, rubble that appears as the story progresses, a special stage that only exists during an event.

Illustration of day and night layers swapping over the same town grid

Data Layers group actors along an axis separate from distance and let you switch their load state as a group. Create them from Window → World Partition → Data Layers and assign actors in your level.

There are two kinds.

KindPurpose
Editor Data LayerEditor visibility organization only. No effect on the game
Runtime Data LayerLoad state can be changed at runtime

If you want to switch during gameplay, create a Runtime Data Layer. It has three runtime states.

StateMeaning
UnloadedNot loaded
LoadedIn memory, but not participating in the game
ActivatedLoaded and running in the game

Switch states from Blueprint with the Set Data Layer Runtime State node. Going through Loaded first and then to Activated lets you finish loading in advance and reveal everything at the exact moment you need it. That's the trick when you want to avoid a hitch right as an event starts.

Sponsored

Where Manual Level Streaming Still Wins

Even with World Partition as the default, manual Level Streaming hasn't gone away. They're good at different things.

SituationBetter approach
Roaming a large outdoor areaWorld Partition (automatic, by distance)
Entering and exiting buildings or dungeonsManual Level Streaming
Swapping an area as the story progressesManual Level Streaming or Data Layers
Full map transition behind a loading screenOpen Level (not streaming)

The deciding factor is clear. If the trigger is distance, use World Partition. If it's an event, go manual. Walking through a door, hitting a switch, defeating a boss. A grid can't express any of those.

In the manual approach, you attach sublevels that stream in and out to a resident Persistent Level.

  1. Create the level you want as a sublevel (for example, SubLevel_Interior)
  2. Open the persistent level and open the Levels panel via Window → Levels
  3. Add the sublevel with Levels → Add Existing
  4. Right-click the sublevel and choose Change Streaming Method → Blueprint

With Blueprint, nothing loads until you call Load Stream Level yourself. The other option, Always Loaded, keeps it loaded permanently and doesn't function as streaming.

To repeat: this workflow only works in levels where World Partition is disabled.


Hands-On: Streaming In a Building Interior

Entering an inn in an RPG, stepping through a mansion door in a horror game, descending into a dungeon entrance in an exploration game. "Go from outside to inside and the interior loads" shows up in every genre. Here we'll build one, from the trigger in front of the door all the way through.

Side-by-side view of the player entering a trigger in front of a door, loading the interior assets, and unloading them on exit

Setting Up the Repro

This hands-on uses a level with World Partition disabled (the Basic template), because the Levels panel isn't available in World Partition levels.

ItemSetting
Persistent levelCreate with File → New Level → Basic, save as L_Outdoor
SublevelCreate the same way, save as SubLevel_Interior
Sublevel contents1,200 Static Meshes of furniture and props (roughly 2,000 triangles each)
Persistent level sideThe building exterior, plus one Box Collision at its entrance
Trigger sizeBox Extent = X:200 / Y:400 / Z:150 (large enough to cover the entrance)

Step 1: Register the Sublevel

Open L_Outdoor and open the Levels panel via Window → Levels.

Choose SubLevel_Interior from Levels → Add Existing.... Once added, right-click → Change Streaming MethodBlueprint.

If you play now, the 1,200 interior meshes aren't loaded.

Step 2: Build the Trigger Actor

Create a new Actor Blueprint (BP_LevelStreamTrigger) and add a Box Collision component. Set Box Extent to the values above in the details panel, and set Collision Presets to OverlapAllDynamic.

Add one variable.

VariableTypeDefault
TargetLevelNameNameSubLevel_Interior

The type is Name because the Level Name pin on Load Stream Level takes a Name. Making it a variable lets you reuse the same Blueprint for other buildings.

Step 3: Wire Up the Load

Build the following graph in BP_LevelStreamTrigger's event graph.

Node graph running from On Component Begin Overlap through Cast To Character into Load Stream Level, with Should Block on Load set to False and the Completed pin wired to a notification
BP_LevelStreamTrigger (Event Graph)

On Component Begin Overlap (Box)
  → Cast To Character from Other Actor
  → (success) Load Stream Level (by Name)
       Level Name          : TargetLevelName
       Make Visible After Load : True
       Should Block on Load    : False
  → Completed → Print String "Interior loaded"

On Component End Overlap (Box)
  → Cast To Character from Other Actor
  → (success) Unload Stream Level
       Level Name          : TargetLevelName
       Should Block on Unload : False

Load Stream Level is a latent node. It has a clock icon in its lower-right corner and doesn't finish immediately when executed. The Completed pin fires when loading finishes. Wire your door-opening animation or arrival notification from that pin.

Setting Should Block on Load to False is the key. With True, the entire game halts until loading finishes, freezing the screen for as long as those 1,200 meshes take. Use False for anything outside a loading screen.

Drag your finished BP_LevelStreamTrigger into L_Outdoor and place it at the building entrance.

Step 4: Measure and Confirm

Launch with Standalone Game and run stat levels in the console. You get a list of loaded levels and their states (Loaded / Visible / Unloaded).

Away from the building, SubLevel_Interior either doesn't appear or shows as Unloaded. Check stat unit here.

Frame:   8.20 ms
Game:    2.10 ms
Draw:    7.80 ms
GPU:     6.40 ms

Enter the trigger and Print String shows Interior loaded, with SubLevel_Interior becoming Visible in stat levels. Measure again inside:

Frame:  11.60 ms
Game:    2.40 ms
Draw:   11.10 ms
GPU:     8.20 ms

Draw rose from 7.80ms to 11.10ms. That's not a failure. The 1,200 interior meshes are loaded and actually being drawn. What matters is that you weren't paying that 3.3 milliseconds while outside the building. With ten buildings, the gap against a load-everything-always setup is tenfold.

Leave the trigger and SubLevel_Interior returns to Unloaded, with Draw back around 7.80ms.

If it isn't working, here's how to narrow it down.

  • Load Stream Level does nothingLevel Name doesn't match the actual asset name. Enter just the asset name (SubLevel_Interior), not a path
  • Change Streaming Method isn't in the Levels panel → that level has World Partition enabled. Rebuild it from the Basic template
  • It loads but nothing is visibleMake Visible After Load is False. Set it to True, or call Make Visible separately
  • The screen freezes the moment you enterShould Block on Load is True
  • The trigger doesn't respond → check Collision Presets on the Box Collision. It needs to be OverlapAllDynamic

Two things to take away.

  • Put the trigger well before the door: loading takes time. Put the trigger right at the door and the room is still empty when it opens. Place it a few seconds of walking ahead to buy yourself the margin
  • Be careful with unloading: a player pacing back and forth in front of the door causes repeated load and discard cycles. Instead of unloading straight from End Overlap, insert a few seconds of Delay, or make the unload trigger larger than the load trigger

Building post-load logic off the Completed pin follows the same "announce what happened" thinking as Event-Driven Design with Event Dispatchers. It pays off when you want to initialize loaded actors in a loosely coupled way.

Sponsored

Bonus: Good to Know for Later

Learning Level Instance early pays off. When you want to place the same building ten times, it lets you author one level and place it like an actor. It works in World Partition levels too, and editing one updates all of them. Think of it as applying the same idea as reusing functionality with Blueprint Components, but at the level scale.

World Partition generates HLODs automatically. Nothing is displayed in cells that aren't loaded, which would leave a blank horizon, so World Partition builds HLODs (simplified meshes merged from multiple actors) to fill in the distance. Build them from Build → Build HLODs. If your distant scenery is missing, suspect this first.

Unloading doesn't immediately free memory. It's retained until garbage collection runs. If stat levels says Unloaded but memory hasn't dropped, that's just the delay. Nothing is wrong.

Open Level isn't streaming. It moves to an entirely different map and discards the current level completely. It's for things like going from the title screen into the game. It isn't suited to seamless transitions. How to insert a loading screen, and where the data that gets discarded goes, are covered in Open Level, Loading Screens, and Where Your Data Goes.

Editor measurements aren't reliable. How streaming feels depends heavily on disk speed, and the editor caches assets so it looks faster than reality. If you need numbers close to the target hardware, measure in a packaged build (see Finding Performance Bottlenecks with stat unit).


Summary

ApproachWhere it fitsSwitching trigger
World PartitionLarge outdoor maps. UE5's defaultDistance (automatic)
Data LayersDay/night, progression, eventsState (Set Data Layer Runtime State)
Manual Level StreamingInteriors, dungeons, area swapsEvents (Load Stream Level)
Open LevelTitle screen into the gameFull map transition

Three things to hold onto.

  • For a large map in UE5, start with World Partition. Create a new level from the Open World or Empty Open World template and it's enabled
  • World Partition and traditional sublevel management can't coexist. Pick one before you start building the map
  • Manual Level Streaming is still current. Switches that distance can't express (walking through a door, an event firing) are its territory

And a note on measurement. Streaming mainly affects memory and initial load time, and won't necessarily show up immediately in stat unit's Draw or GPU. To confirm it's working, check load states with stat levels first, then look at how stat unit changed. Judge with both steps.

That completes the trio: measure, draw less, load less. When you first ran stat unit, which number was largest? That's what decides where you go next.

Further Reading