콘텐츠로 이동

UE5 Level Streaming

Level Streaming은 큰 월드를 여러 서브레벨로 분할하고 필요할 때만 로드해 메모리와 CPU 비용을 줄이는 기법입니다. UE5에서는 World Partition이 자동 스트리밍을 제공하지만, 레거시 프로젝트나 세밀한 제어가 필요한 경우 여전히 수동 Level Streaming이 쓰입니다.


항목Level StreamingWorld Partition
설정 방식서브레벨 수동 배치에디터 자동 셀 분할
제어 단위서브레벨 전체셀(Cell) 단위
런타임 APILoadStreamLevel / UnloadStreamLevel자동 (플레이어 위치 기반)
적합 규모중소형, 구획이 명확한 맵오픈월드
UE5 권장레거시 / 특수 목적신규 오픈월드

Windows > Levels 패널
→ Levels 드롭다운 → Add Existing Level
→ 서브레벨 선택 후 Streaming Method: Blueprint (런타임 제어) 또는 Always Loaded

Streaming Method를 Blueprint로 설정해야 런타임에 C++/Blueprint로 로드 상태를 제어할 수 있습니다.


LevelStreamingManager.h
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "LevelStreamingManager.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLevelLoaded, FName, LevelName);
UCLASS()
class MYGAME_API ULevelStreamingManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable)
FOnLevelLoaded OnLevelLoaded;
UFUNCTION(BlueprintCallable)
void LoadLevel(FName LevelName, bool bMakeVisible = true);
UFUNCTION(BlueprintCallable)
void UnloadLevel(FName LevelName);
UFUNCTION(BlueprintCallable, BlueprintPure)
bool IsLevelLoaded(FName LevelName) const;
};
LevelStreamingManager.cpp
#include "LevelStreamingManager.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/LevelStreamingDynamic.h"
void ULevelStreamingManager::LoadLevel(FName LevelName, bool bMakeVisible)
{
UWorld* World = GetWorld();
if (!World) return;
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnLoadComplete");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetTypeHash(LevelName);
UGameplayStatics::LoadStreamLevel(
World, LevelName, bMakeVisible, /*bShouldBlockOnLoad=*/false, LatentInfo);
}
void ULevelStreamingManager::UnloadLevel(FName LevelName)
{
UWorld* World = GetWorld();
if (!World) return;
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnUnloadComplete");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetTypeHash(LevelName) + 1000;
UGameplayStatics::UnloadStreamLevel(
World, LevelName, LatentInfo, /*bShouldBlockOnUnload=*/false);
}
bool ULevelStreamingManager::IsLevelLoaded(FName LevelName) const
{
UWorld* World = GetWorld();
if (!World) return false;
ULevelStreaming* StreamingLevel = UGameplayStatics::GetStreamingLevel(
World, LevelName);
return StreamingLevel && StreamingLevel->IsLevelLoaded();
}
// LatentInfo 콜백 (UFUNCTION 필요)
UFUNCTION()
void ULevelStreamingManager::OnLoadComplete()
{
// 로드 완료 후 처리
UE_LOG(LogTemp, Log, TEXT("Level load complete"));
OnLevelLoaded.Broadcast(NAME_None); // 실제 이름 추적 필요
}

4. 동적 서브레벨 생성 (런타임 인스턴스)

섹션 제목: “4. 동적 서브레벨 생성 (런타임 인스턴스)”

에디터에 사전 등록 없이 런타임에 새 레벨 인스턴스를 생성합니다.

bool bSuccess = false;
ULevelStreamingDynamic* StreamingLevel = ULevelStreamingDynamic::LoadLevelInstance(
GetWorld(),
TEXT("/Game/Maps/DungeonRoom"), // 레벨 에셋 경로
FVector(1000.f, 0.f, 0.f), // 배치 위치
FRotator::ZeroRotator,
bSuccess
);
if (bSuccess && StreamingLevel)
{
// 로드 완료 콜백 등록
StreamingLevel->OnLevelLoaded.AddDynamic(
this, &AMyActor::OnDynamicLevelLoaded);
}

이 방식은 던전 생성, 인스턴스 방 같은 프로시저럴 레벨 배치에 유용합니다.


void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
ULevelStreaming* Level = UGameplayStatics::GetStreamingLevel(
GetWorld(), TEXT("DungeonRoom_01"));
if (!Level) return;
ELevelStreamingState State = ULevelStreamingBlueprintLibrary::GetStreamingState(
GetWorld(), TEXT("DungeonRoom_01"));
switch (State)
{
case ELevelStreamingState::Loading:
// 로딩 UI 표시
break;
case ELevelStreamingState::LoadedVisible:
// 레벨 완전히 표시됨 → 플레이어 이동 허용
AllowPlayerEntry();
break;
case ELevelStreamingState::Unloaded:
// 언로드 완료
break;
default:
break;
}
}

문 열기처럼 레벨 전환이 즉각적으로 느껴져야 할 때는 로딩이 완료될 때까지 트리거를 블로킹합니다.

void ADoorActor::TryOpen()
{
FName TargetLevel = TEXT("NextRoom");
if (ULevelStreamingManager* Mgr = GetGameInstance()->GetSubsystem<ULevelStreamingManager>())
{
if (!Mgr->IsLevelLoaded(TargetLevel))
{
// 아직 로드 안 됨 → 로드 시작 + 문 잠금
bDoorLocked = true;
Mgr->LoadLevel(TargetLevel);
Mgr->OnLevelLoaded.AddDynamic(this, &ADoorActor::OnNextRoomReady);
}
else
{
OpenDoor();
}
}
}
void ADoorActor::OnNextRoomReady(FName LevelName)
{
bDoorLocked = false;
OpenDoor();
}

  • Level Streaming은 Windows > Levels 패널에서 서브레벨을 등록하고 Streaming Method를 Blueprint로 설정해야 런타임 제어가 가능하다.
  • UGameplayStatics::LoadStreamLevel / UnloadStreamLevel은 비동기 LatentAction 기반이므로 완료 콜백을 활용한다.
  • 프로시저럴 레벨 배치가 필요하면 ULevelStreamingDynamic::LoadLevelInstance를 사용한다.
  • 오픈월드 신규 프로젝트라면 World Partition이 더 적합하며, Level Streaming은 구획이 명확한 선형 맵이나 레거시 프로젝트에 적합하다.