Using Level Streaming in Unreal Engine to Keep Open Worlds Smooth
Learn how to split an Unreal Engine open world into streamed sub‑levels, set up Box Streaming Volumes, drive loads with Blueprint, and tune async loading to avoid hitches.
31 Jan 2026, 03:13 UTC

Problem: worlds outgrow a single map’s memory
When an open‑world level exceeds the memory budget of a single map, the engine must keep all geometry resident. This causes frame‑rate drops, long loading screens, and hitches as the player moves into new areas. The solution is to load and unload pieces of the world on demand, keeping only what is near the player in memory.
Thesis: Level Streaming provides an editor‑driven way to stream sub‑levels based on proximity
Unreal Engine’s Level Streaming system lets you split a world into multiple sub‑levels and attach streaming volumes that automatically load or unload those sub‑levels when the player enters or leaves the volume. The approach works with Blueprint or C++, supports asynchronous loading, and can be tuned with distance‑based culling to avoid hitches.
Setting up sub‑levels and streaming volumes
- In the Levels panel, right‑click and choose Add New → Sub‑Level. Create four sub‑levels named
Quad_NW,Quad_NE,Quad_SW,Quad_SE. - Open each sub‑level and place the geometry that belongs to that quadrant (e.g., terrain meshes, foliage).
- Return to the persistent level. From the Modes panel drag a Box Streaming Volume into the world for each quadrant. Scale the box so it tightly encloses the geometry of its sub‑level, with a small overlap (≈50 uu) to avoid gaps.
- Select a volume, then in its Details panel set Streaming Level to the matching sub‑level and enable Is Visible and Should Be Loaded.
- Place the PlayerStart inside the first volume (e.g., Quad_NW) so the game begins with that sub‑level loaded.
Triggering load/unload with Blueprint
Although the volumes handle visibility, you may want to log events or add custom logic. Create a Blueprint class BP_StreamingManager and add it to the persistent level.
- Add two overlapping Box Collision components: one for loading, one for unloading (slightly larger than the streaming volume).
- In the Event Graph, bind OnComponentBeginOverlap and OnComponentEndOverlap for each collider.
- When the player overlaps the load collider of a quadrant, call Load Stream Level with the sub‑level name and set Make Visible After Load to true. When they exit the unload collider, call Unload Stream Level.
- Optionally, use Print String to output the sub‑level name and the current time (
Get Game Time in Seconds) to monitor latency.
Optimizing with distance‑based culling and async loading
Streaming volumes guarantee that the correct sub‑level is loaded, but you can reduce I/O spikes by:
- Enabling Async Loading Thread in Project Settings → Engine → Streaming. This offloads the load operation to a background thread.
- Setting Streaming Distance on each volume (found under Details → Streaming) to a value slightly larger than the volume’s radius. The engine will begin pre‑loading when the player approaches that distance, smoothing the transition.
- Using Oodle compression (Project Settings → Engine → Compression) to reduce disk read times for large sub‑levels.
Debugging with the Level Streaming UI and stat commands
While playing in the editor:
- Open the console (`) and type
stat streaming. The output shows memory used by each streamed level and timestamps for load/unload events. - Type
stat unitto see frame‑time breakdown; look for spikes that correspond to load events. - Use
showdebug streamingto visualize the volumes and their current state directly in the viewport. - If you observe hitches, increase the async loading thread priority (
t.AsyncLoadingThreadPriority) or reduce the size of individual sub‑levels so each load stays under the storage bandwidth limit.
Worked example: four‑quadrant open field
Imagine a flat terrain split into four equal squares. Each square is a sub‑level with its own collision and foliage. The player starts in the northwest quadrant. As they walk east, they enter the Box Streaming Volume for the northeast quadrant; the load event fires, the northeast sub‑level appears, and the southwest volume’s unload event fires because the player has left its unload collider. The stat streaming console shows memory rising from ~150 MB (NW only) to ~300 MB (NW+NE) and then dropping back to ~150 MB when the southwest sub‑level is unloaded. No noticeable frame‑time hitch appears on an SSD because the async thread keeps the load under 8 ms.
Trade‑off: streaming reduces memory but can cause hitches if I/O is saturated
The main risk is that loading several large sub‑levels simultaneously exceeds the disk’s read throughput, producing a frame‑time spike. Overlapping streaming volumes exacerbate this by triggering redundant loads. Mitigation strategies include:
- Keeping sub‑level sizes under ~50 MB on typical HDDs or ~100 MB on SSDs.
- Ensuring volumes overlap only enough to avoid gaps (≈10‑20 % of their radius).
- Prioritizing critical sub‑levels (e.g., those containing gameplay‑essential assets) by setting Priority in the Streaming Volume details.
Actionable closing advice
Start small: divide your world into a handful of sub‑levels, attach tight‑fit streaming volumes, and verify behavior with stat streaming. Iterate on volume size and async settings until load times stay below your frame‑time budget (typically 8‑10 ms for 60 fps). Once the basic streaming loop is smooth, layer in custom Blueprint logic for gameplay triggers, and consider Oodle compression for final builds on target hardware.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.