Loading...
Loading...
Use this skill when working with World Partition, level streaming, level travel, OpenLevel, ServerTravel, data layer, world subsystem, level instance, sub-level, seamless travel, open world, or HLOD. See references/streaming-patterns.md for configuration patterns by game type.
npx skill4agent add quodsoler/unreal-engine-skills ue-world-level-streaming.agents/ue-project-context.md.uasset__ExternalActors__// MyGameMode.cpp
#include "WorldPartition/DataLayer/DataLayerManager.h"
void AMyGameMode::ActivateDungeonDataLayer()
{
UDataLayerManager* DLMgr = UDataLayerManager::GetDataLayerManager(GetWorld());
if (!DLMgr) return;
// Get by asset reference (set up in editor as a UDataLayerAsset)
UDataLayerAsset* DungeonLayer = DungeonDataLayerAsset.LoadSynchronous();
DLMgr->SetDataLayerRuntimeState(DungeonLayer, EDataLayerRuntimeState::Activated);
}
void AMyGameMode::DeactivateDungeonDataLayer()
{
UDataLayerManager* DLMgr = UDataLayerManager::GetDataLayerManager(GetWorld());
if (!DLMgr) return;
UDataLayerAsset* DungeonLayer = DungeonDataLayerAsset.LoadSynchronous();
DLMgr->SetDataLayerRuntimeState(DungeonLayer, EDataLayerRuntimeState::Unloaded);
}UnloadedLoadedActivatedIWorldPartitionStreamingSourceProviderAServerStreamingLevelsVisibilityLoadingRangeURuntimePartitionLevelStreaming.hRemoved -> Unloaded -> Loading -> LoadedNotVisible -> MakingVisible -> LoadedVisible -> MakingInvisible -> LoadedNotVisible
|
FailedToLoad (check logs; level asset missing or corrupt)ULevelStreaming* StreamingLevel = /* ... */;
ELevelStreamingState State = StreamingLevel->GetLevelStreamingState();
switch (State)
{
case ELevelStreamingState::Unloaded: /* not in memory */ break;
case ELevelStreamingState::Loading: /* async load in progress */ break;
case ELevelStreamingState::LoadedNotVisible: /* in memory, not rendered */ break;
case ELevelStreamingState::MakingVisible: /* adding to world */ break;
case ELevelStreamingState::LoadedVisible: /* fully active */ break;
case ELevelStreamingState::MakingInvisible: /* removing from rendering */ break;
case ELevelStreamingState::FailedToLoad: /* check logs */ break;
}GameplayStatics.h// MyActor.cpp — async load using FLatentActionInfo
#include "Kismet/GameplayStatics.h"
void AMyActor::StreamInRoom(FName LevelName)
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnRoomLoaded");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetUniqueID();
UGameplayStatics::LoadStreamLevel(
this, // WorldContextObject
LevelName, // e.g., FName("Room_01")
true, // bMakeVisibleAfterLoad
false, // bShouldBlockOnLoad — keep false for async
LatentInfo
);
}
UFUNCTION()
void AMyActor::OnRoomLoaded()
{
// Room is now loaded and visible
}
void AMyActor::StreamOutRoom(FName LevelName)
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnRoomUnloaded");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetUniqueID() + 1;
UGameplayStatics::UnloadStreamLevel(
this,
LevelName,
LatentInfo,
false // bShouldBlockOnUnload
);
}LoadStreamLevelBySoftObjectPtrULevelStreamingDynamic::LoadLevelInstanceLevelStreamingDynamic.h#include "Engine/LevelStreamingDynamic.h"
void AMyDungeonGenerator::SpawnRoom(FVector Location, FRotator Rotation)
{
bool bSuccess = false;
ULevelStreamingDynamic* StreamingLevel = ULevelStreamingDynamic::LoadLevelInstance(
this, // WorldContextObject
TEXT("/Game/Levels/Room_Corridor"), // LongPackageName — full path
Location,
Rotation,
bSuccess
);
if (bSuccess && StreamingLevel)
{
// Bind to delegate to know when visible
StreamingLevel->OnLevelShown.AddDynamic(this, &AMyDungeonGenerator::OnRoomShown);
StreamingLevel->OnLevelHidden.AddDynamic(this, &AMyDungeonGenerator::OnRoomHidden);
LoadedRooms.Add(StreamingLevel);
}
}
void AMyDungeonGenerator::UnloadRoom(ULevelStreamingDynamic* StreamingLevel)
{
if (StreamingLevel)
{
StreamingLevel->SetShouldBeLoaded(false);
StreamingLevel->SetShouldBeVisible(false);
StreamingLevel->SetIsRequestingUnloadAndRemoval(true);
}
}OptionalLevelNameOverrideULevelStreamingDynamic::FLoadLevelInstanceParams Params(
GetWorld(),
TEXT("/Game/Levels/Room_Corridor"),
FTransform(Rotation, Location)
);
Params.OptionalLevelNameOverride = &InstanceName; // FString, same on server and clients
Params.bInitiallyVisible = true;
bool bSuccess = false;
ULevelStreamingDynamic* Level = ULevelStreamingDynamic::LoadLevelInstance(Params, bSuccess);LevelStreaming.hBlueprintAssignableOnLevelLoadedOnLevelUnloadedOnLevelShownOnLevelHiddenAddDynamicStreamingLevel->OnLevelShown.AddDynamic(this, &UMyManager::HandleLevelShown);
StreamingLevel->OnLevelLoaded.AddDynamic(this, &UMyManager::HandleLevelLoaded);ALevelStreamingVolumeLevelStreamingVolume.h// EStreamingVolumeUsage — set on the volume in editor
SVB_Loading // load but do not make visible
SVB_LoadingAndVisibility // load and make visible (most common)
SVB_VisibilityBlockingOnLoad // force blocking load when entering
SVB_BlockingOnLoad // block load of associated levels
SVB_LoadingNotVisible // load, keep invisible (pre-warm)EditorStreamingVolumesULevelStreaming::bDisableDistanceStreaming = true// Get streaming level reference from world
const TArray<ULevelStreaming*>& Levels = GetWorld()->GetStreamingLevels();
for (ULevelStreaming* Level : Levels)
{
if (Level->GetWorldAssetPackageFName() == FName("/Game/Levels/MySubLevel"))
{
Level->SetShouldBeLoaded(true);
Level->SetShouldBeVisible(true);
break;
}
}UGameplayStatics::FlushLevelStreaming(this);ALevelInstanceULevelStreamingDynamicALevelInstanceGameplayStatics.hUGameplayStatics::OpenLevel(this, FName("/Game/Maps/MainMenu"), true);
UGameplayStatics::OpenLevel(this, FName("/Game/Maps/GameLevel"), true, TEXT("?Difficulty=Hard"));
UGameplayStatics::OpenLevelBySoftObjectPtr(this, GameLevelAsset, true); // packaging-safeWorld.hGetWorld()->ServerTravel(TEXT("/Game/Maps/Level02?listen"), /*bAbsolute=*/false);World.hvoid UWorld::SeamlessTravel(const FString& InURL, bool bAbsolute);
bool UWorld::IsInSeamlessTravel() const;
void UWorld::SetSeamlessTravelMidpointPause(bool bNowPaused);bUseSeamlessTravel = trueAGameModeBase// bUseSeamlessTravel is already declared in AGameModeBase — do NOT redeclare it.
// Just set it in the constructor:
// MyGameMode.cpp constructor
bUseSeamlessTravel = true;DefaultEngine.ini[/Script/Engine.GameMapsSettings]
TransitionMap=/Game/Maps/TransitionGetSeamlessTravelActorList// GameMode — called on server side during transition
void AMyGameMode::GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList)
{
Super::GetSeamlessTravelActorList(bToTransition, ActorList);
if (!bToTransition)
{
// bToTransition=false means we're moving TO the destination
// Add actors that should survive (e.g., GameState, custom managers)
ActorList.Add(MyPersistentManager);
}
}
// GameMode — called after destination map is loaded
void AMyGameMode::PostSeamlessTravel()
{
Super::PostSeamlessTravel();
// Re-initialize any post-travel systems
}
// GameMode — handle re-possessing players after travel
void AMyGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
Super::HandleSeamlessTravelPlayer(C);
// Restore player-specific state here
}// From GameMode, server-only
GetWorld()->ServerTravel(TEXT("/Game/Maps/Level02?listen"));
// Seamless travel is automatic because bUseSeamlessTravel is trueSetSeamlessTravelMidpointPause(true)APlayerController::ClientTravel(URL, ETravelType::TRAVEL_Absolute)UWorldSubsystemSubsystems/WorldSubsystem.hUWorld// MyStreamingManager.h
UCLASS()
class MYGAME_API UMyStreamingManager : public UWorldSubsystem
{
GENERATED_BODY()
public:
virtual void PostInitialize() override; // after all subsystems init
virtual void OnWorldBeginPlay(UWorld& InWorld) override; // after all BeginPlay
virtual void PreDeinitialize() override; // cleanup hook
virtual bool ShouldCreateSubsystem(UObject* Outer) const override; // filter world type
void RequestLoadZone(FName ZoneName);
void RequestUnloadZone(FName ZoneName);
private:
TMap<FName, TWeakObjectPtr<ULevelStreaming>> ActiveZones;
};UMyStreamingManager* Manager = GetWorld()->GetSubsystem<UMyStreamingManager>();
if (Manager)
{
Manager->RequestLoadZone(FName("Zone_A"));
}UTickableWorldSubsystemSuper::InitializeSuper::DeinitializeGetStatIdRETURN_QUICK_DECLARE_CYCLE_STAT| Mechanism | Lifetime | Use Case |
|---|---|---|
| Entire application session | Cross-level player state, session config |
| Entire application session | Services that outlive any world |
| Seamless travel actor list | Transition only | Actors that physically cross (GameState, managers) |
| Disk-persistent | Long-term saves, progression |
| Per world | World-scoped cache; push data to |
UGameInstanceUMyGameInstance* GI = GetGameInstance<UMyGameInstance>();
if (GI) GI->PlayerScore += 100;bShouldBlockOnLoad = trueMinTimeBetweenVolumeUnloadRequestsbUseSeamlessTravelTransitionMapDefaultEngine.iniUPROPERTY() UObject*TSoftObjectPtrTSoftClassPtrULevelStreamingDynamic::LoadLevelInstanceOptionalLevelNameOverrideStreamingLevelsUWorld::StreamingLevelsAddStreamingLevelsAddUniqueStreamingLevelsRemoveStreamingLevelsWorld.hStreamingLevelsToConsiderSuper::InitializeSuper::DeinitializeUTickableWorldSubsystem| API | Header | Notes |
|---|---|---|
| | Async latent load of named sub-level |
| | Async latent unload |
| | Blocking flush — use behind loading screens |
| | Non-seamless level travel |
| | Runtime level instancing |
| | Query current stream state |
| | Drive load state |
| | Drive visibility |
| | Remove level from world |
| | Multiplayer level transition |
| | Background seamless transition |
| | Iterate all streaming levels |
| | World Partition data layer control (use |
| | Post-BeginPlay init hook |
| | Control actor persistence |
ue-gameplay-frameworkPostSeamlessTravelue-data-assets-tablesue-networking-replicationue-cpp-foundationsUGameInstance