Quick answer: Streaming hitches come from oversized cells, missing HLODs, and a loading range that lets the player reach an unloaded cell before it finishes streaming. Build HLODs, lower cell size, raise loading range, and enable async asset compilation to keep stream loads off the game thread.
Here is how to fix Unreal World Partition framerate hitches when crossing cell boundaries in an open world. The game runs at 60 FPS on flat plains, then drops to 5 FPS for half a second every time you enter a new region. The drop coincides with cell loading. World Partition is designed to keep memory bounded, but without HLODs and tuned grid settings, the per-cell load cost surfaces as visible hitches.
The Symptom
Smooth gameplay interrupted by ~100–500 ms hitches at predictable locations — usually the boundaries between Streaming Cells visible in the World Partition Editor grid. Profiling shows time spent in FWorldPartitionStreamingPolicy and asset loading on the game thread.
What Causes This
Cell size too large. Default cell size in the WP defaults to 25600 units (256 m). A cell this size can contain hundreds of actors and dozens of mesh assets. Loading them in one frame is the hitch.
No HLODs built. Without HLODs, distant content is either fully loaded (memory bloat) or absent (pop-in and hitch when it streams in). HLODs provide low-cost proxies that hide loads behind LOD transitions.
Loading range too tight. If the loading range matches the cell size, the player can reach the next cell before it finishes loading. Streaming runs as a high-priority blocking task to catch up, hitching the frame.
Synchronous asset loading. If a streamed actor uses FStreamableManager::LoadSynchronous or hard references to large assets, those loads block the game thread.
The Fix
Step 1: Build HLODs for the world. Open Window → World Partition → World Partition Editor. Select all cells (Ctrl-A), then click Build HLODs. For large worlds, use the commandlet so the build runs in batch on a build machine:
UnrealEditor MyProject.uproject ^
-run=WorldPartitionBuilderCommandlet ^
-Builder=WorldPartitionHLODsBuilder ^
-Map=L_OpenWorld
Step 2: Lower cell size. Open the level’s World Partition Settings. Reduce Cell Size to 6400 (64 m) for typical first-person scale, or even 3200 for dense urban scenes. Smaller cells mean more cells, but each load is cheaper and more granular.
Step 3: Raise the loading range. Set Loading Range higher than Cell Size by 2–3x. With cell size 6400 and loading range 16000, players have 1.5 cells of margin before reaching unloaded territory.
// Runtime tuning via console
wp.Runtime.SetActiveCellLoadingRange 16000
wp.Runtime.SetBlockOnSlowStreaming 1
wp.Runtime.EnableStreaming 1
Step 4: Use Runtime Grids for actor categorization. Add an extra grid named Foliage with cell size 12800 and loading range 25000 for ambient props. Use the default grid for gameplay-critical actors with smaller cell size. Each Actor’s World Partition properties expose a Runtime Grid dropdown.
Step 5: Convert hard refs to soft refs.
// Replace hard reference with TSoftObjectPtr
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UStaticMesh> HeavyMesh;
void UseHeavyMesh()
{
UAssetManager& AM = UAssetManager::Get();
FStreamableManager& SM = AM.GetStreamableManager();
SM.RequestAsyncLoad(HeavyMesh.ToSoftObjectPath(), [this]() {
if (UStaticMesh* M = HeavyMesh.Get()) MeshComp->SetStaticMesh(M);
});
}
Async loads keep the heavy work off the game thread, even if the actor itself is streamed in synchronously.
Prestreaming Player Path
If your player follows predictable paths (rails, vehicle routes), use WorldPartitionStreamingSource with a forward-extending bounds. The streamer prefers cells in the predicted direction:
void AVehicle::Tick(float dt)
{
Super::Tick(dt);
StreamingSource->Location = GetActorLocation() + GetActorForwardVector() * 5000;
StreamingSource->Rotation = GetActorRotation();
}
Verifying With Stats
Run stat WorldPartition to see streaming time per frame. Hitches usually show up as spikes over 8 ms in the Cell Loading row. After tuning, that spike should stay under 2 ms even at full speed.
“HLODs hide the load. Smaller cells make the load cheap. Larger loading range hides any remaining cost. All three together keep frame time flat.”
Related Issues
For Level Instance loading issues, see Level Instance Not Loading. For Niagara streaming issues, see Niagara Not Rendering in PIE.
Build HLODs first. Then shrink cells. Then expand loading range. Hitches disappear in that order.