UE5 World Partition & Level Streaming
World Partition이란
섹션 제목: “World Partition이란”World Partition은 UE5에서 도입된 오픈 월드 레벨 관리 시스템입니다. 기존 레벨 스트리밍이 레벨 단위로 로드·언로드했다면, World Partition은 월드를 셀(Cell) 단위로 자동 분할해 플레이어 위치 기반으로 스트리밍합니다.
기존 Level Streaming 대비 장점:
- 레벨 경계 없는 하나의 퍼시스턴트 월드
- 셀 크기·로딩 거리 자동 관리
- One File Per Actor(OFPA)로 협업 충돌 감소
- HLOD(계층적 LOD) 자동 생성
프로젝트 설정
섹션 제목: “프로젝트 설정”; DefaultEngine.ini[/Script/Engine.WorldSettings]bEnableWorldPartition=True
[WorldPartition]RuntimeCellSize=12800 ; 셀 크기 (cm 단위, 128m)LoadingRange=25600 ; 로딩 거리 (256m)에디터에서: 월드 세팅 → World Partition → Enable World Partition 체크
Data Layers — 레이어 기반 콘텐츠 관리
섹션 제목: “Data Layers — 레이어 기반 콘텐츠 관리”Data Layers는 World Partition의 오브젝트를 논리적 레이어로 분류합니다. 낮/밤 사이클, 퀘스트 단계, DLC 콘텐츠를 레이어로 관리할 수 있습니다.
// DataLayerAsset: 에디터에서 생성 후 C++에서 참조#include "WorldPartition/DataLayer/DataLayerAsset.h"#include "WorldPartition/DataLayer/WorldDataLayersSubsystem.h"
UCLASS()class AWorldManager : public AActor{ GENERATED_BODY()
public: UPROPERTY(EditDefaultsOnly, Category = "DataLayers") TObjectPtr<UDataLayerAsset> _nightLayer;
UPROPERTY(EditDefaultsOnly, Category = "DataLayers") TObjectPtr<UDataLayerAsset> _questLayer;
// 낮/밤 전환 시 레이어 활성화 void SetNightTime(bool bIsNight) { auto* DataLayerSubsystem = GetWorld()->GetSubsystem<UWorldDataLayersSubsystem>(); if (!DataLayerSubsystem) return;
EDataLayerRuntimeState TargetState = bIsNight ? EDataLayerRuntimeState::Activated : EDataLayerRuntimeState::Unloaded;
DataLayerSubsystem->SetDataLayerRuntimeState(_nightLayer, TargetState); }
// 퀘스트 완료 후 새 콘텐츠 로드 void OnQuestComplete(int32 QuestId) { auto* DataLayerSubsystem = GetWorld()->GetSubsystem<UWorldDataLayersSubsystem>(); if (!DataLayerSubsystem) return;
DataLayerSubsystem->SetDataLayerRuntimeState( _questLayer, EDataLayerRuntimeState::Activated); }};런타임 스트리밍 제어
섹션 제목: “런타임 스트리밍 제어”#include "WorldPartition/WorldPartitionRuntimeCell.h"#include "WorldPartition/WorldPartition.h"
// 특정 위치의 셀 강제 로드 (컷씬, 텔레포트 전)void AGameMode::PreloadAreaAroundLocation(FVector Location, float Radius){ UWorldPartition* WorldPartition = GetWorld()->GetWorldPartition(); if (!WorldPartition) return;
// 스트리밍 소스 등록: 이 위치를 중심으로 로딩 강제 // (플레이어 카메라 외 추가 소스) FWorldPartitionStreamingSource Source; Source.Location = Location; Source.Rotation = FRotator::ZeroRotator; Source.TargetState = EStreamingSourceTargetState::Activated; Source.Shapes.Add(FStreamingSourceShape::MakeSphere(Radius)); Source.bReplay = false; Source.Priority = EStreamingSourcePriority::High;
// PlayerController에서 스트리밍 소스 추가 // GetWorld()->GetFirstPlayerController()->SetStreamingSource(Source);}HLOD (계층적 LOD) 설정
섹션 제목: “HLOD (계층적 LOD) 설정”HLOD는 멀리 있는 셀의 복잡한 지오메트리를 단순화된 메시로 대체해 원거리 렌더링 비용을 줄입니다.
// HLOD 레이어 설정 (에디터)// 월드 아웃라이너 → HLOD Layers → New HLOD Layer
// 에셋 기반 HLOD 설정#include "WorldPartition/HLOD/HLODLayer.h"
// C++에서 액터의 HLOD 레이어 지정UCLASS()class AMyStaticMesh : public AStaticMeshActor{ GENERATED_BODY()
public: AMyStaticMesh() { // HLOD 레이어 에셋 경로로 지정 // 에디터에서 설정하는 것이 일반적 }};HLOD Builder 타입:
| 타입 | 설명 |
|---|---|
Merged Mesh | 여러 메시를 하나로 병합 |
Instanced | 인스턴스 스태틱 메시로 변환 |
Nanite | Nanite 지오메트리로 변환 |
Custom | 커스텀 HLOD 로직 |
레거시 Level Streaming (기존 방식)
섹션 제목: “레거시 Level Streaming (기존 방식)”World Partition 이전 방식도 여전히 유효합니다.
// 서브레벨 스트리밍 제어#include "Engine/LevelStreamingDynamic.h"
UCLASS()class ALevelStreamController : public AActor{ GENERATED_BODY()
public: // 동적으로 레벨 로드 void LoadLevel(const FString& LevelPath) { bool bSuccess = false; ULevelStreamingDynamic* StreamingLevel = ULevelStreamingDynamic::LoadLevelInstance( GetWorld(), LevelPath, // "/Game/Levels/Village" GetActorLocation(), GetActorRotation(), bSuccess );
if (bSuccess && StreamingLevel) { // 로드 완료 콜백 StreamingLevel->OnLevelLoaded.AddDynamic( this, &ALevelStreamController::OnLevelLoaded);
StreamingLevel->SetShouldBeVisible(true); } }
// 레벨 언로드 void UnloadLevel(ULevelStreaming* StreamingLevel) { if (!StreamingLevel) return; StreamingLevel->SetShouldBeLoaded(false); StreamingLevel->SetShouldBeVisible(false); }
UFUNCTION() void OnLevelLoaded() { UE_LOG(LogTemp, Log, TEXT("Level loaded successfully")); }};스트리밍 진행률 모니터링
섹션 제목: “스트리밍 진행률 모니터링”// 로딩 화면을 위한 스트리밍 진행률 확인void ALoadingManager::Tick(float DeltaTime){ Super::Tick(DeltaTime);
UWorld* World = GetWorld(); if (!World) return;
// 레벨 스트리밍 상태 확인 bool bAllLoaded = true; for (ULevelStreaming* Level : World->GetStreamingLevels()) { if (Level->ShouldBeLoaded() && !Level->IsLevelLoaded()) { bAllLoaded = false;
float Progress = Level->GetLoadedLevel() ? 1.f : 0.f; // 단순화된 예시
UE_LOG(LogTemp, Log, TEXT("Loading level: %s (%.0f%%)"), *Level->GetWorldAssetPackageName(), Progress * 100.f); } }
if (bAllLoaded && _bWasLoading) { _bWasLoading = false; OnAllLevelsLoaded(); }}
void ALoadingManager::OnAllLevelsLoaded(){ // 로딩 화면 숨기기 UE_LOG(LogTemp, Log, TEXT("All levels loaded"));}World Partition 디버그
섹션 제목: “World Partition 디버그”콘솔 명령어:wp.Runtime.ToggleDrawRuntimeCells — 셀 경계 시각화wp.Runtime.ToggleDrawRuntimeHash — 해시 그리드 시각화wp.Runtime.OverrideRuntimeSpatialHashLoadingRange 512 — 로딩 거리 오버라이드
stat worldpartition — 스트리밍 통계| 기능 | 설명 |
|---|---|
| World Partition | 셀 기반 자동 스트리밍 |
| Data Layers | 논리적 콘텐츠 레이어 (낮/밤, 퀘스트) |
| HLOD | 원거리 LOD 자동 생성 |
| OFPA | 액터별 개별 파일 (협업 충돌 감소) |
| Level Streaming | 수동 서브레벨 로드/언로드 |
World Partition은 오픈 월드 개발의 핵심 시스템입니다. Data Layers로 게임 상태에 따라 월드를 동적으로 변형하고, HLOD로 원거리 렌더링 비용을 최소화하면 대규모 맵에서도 안정적인 성능을 유지할 수 있습니다.