UE5 Level Streaming
Level Streaming은 큰 월드를 여러 서브레벨로 분할하고 필요할 때만 로드해 메모리와 CPU 비용을 줄이는 기법입니다. UE5에서는 World Partition이 자동 스트리밍을 제공하지만, 레거시 프로젝트나 세밀한 제어가 필요한 경우 여전히 수동 Level Streaming이 쓰입니다.
1. Level Streaming vs World Partition
섹션 제목: “1. Level Streaming vs World Partition”| 항목 | Level Streaming | World Partition |
|---|---|---|
| 설정 방식 | 서브레벨 수동 배치 | 에디터 자동 셀 분할 |
| 제어 단위 | 서브레벨 전체 | 셀(Cell) 단위 |
| 런타임 API | LoadStreamLevel / UnloadStreamLevel | 자동 (플레이어 위치 기반) |
| 적합 규모 | 중소형, 구획이 명확한 맵 | 오픈월드 |
| UE5 권장 | 레거시 / 특수 목적 | 신규 오픈월드 |
2. 서브레벨 등록 (에디터)
섹션 제목: “2. 서브레벨 등록 (에디터)”Windows > Levels 패널 → Levels 드롭다운 → Add Existing Level → 서브레벨 선택 후 Streaming Method: Blueprint (런타임 제어) 또는 Always LoadedStreaming Method를 Blueprint로 설정해야 런타임에 C++/Blueprint로 로드 상태를 제어할 수 있습니다.
3. C++로 서브레벨 로드/언로드
섹션 제목: “3. C++로 서브레벨 로드/언로드”#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;};#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);}이 방식은 던전 생성, 인스턴스 방 같은 프로시저럴 레벨 배치에 유용합니다.
5. 스트리밍 상태 폴링
섹션 제목: “5. 스트리밍 상태 폴링”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; }}6. 로드 화면 블로킹 패턴
섹션 제목: “6. 로드 화면 블로킹 패턴”문 열기처럼 레벨 전환이 즉각적으로 느껴져야 할 때는 로딩이 완료될 때까지 트리거를 블로킹합니다.
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은 구획이 명확한 선형 맵이나 레거시 프로젝트에 적합하다.