본문으로 건너뛰기

안동민 개발노트

본문 시작

레벨 스트리밍과 World Partition

Level Streaming과 World Partition의 로드 단위를 비교하고 대규모 월드의 영역을 필요할 때 불러옵니다.

이전 절에서 액터의 생성과 관리에 대해 알아보며, 게임 월드 내의 객체들을 어떻게 효율적으로 다룰 수 있는지 이해했습니다.

하지만 게임 월드가 매우 커지고 복잡해질 경우, 모든 액터와 모든 지형 데이터를 동시에 메모리에 로드하는 것은 불가능에 가깝습니다.

이 문제를 해결하기 위해 언리얼 엔진은 레벨 스트리밍(Level Streaming)월드 파티션(World Partition)을 제공합니다.

특히 UE5 프로젝트에서는 월드 파티션이 대규모 월드의 기본 경로입니다.

월드 구조에 맞춰 스트리밍 경로를 선택한다

새 대형 월드는 World Partition이 기본 경로이고, 명시적 구역은 Level Streaming으로 직접 제어할 수 있다.

  1. World Partition

    연속된 대형 월드 · 자동 거리 스트리밍 Source 플레이어·컴포넌트 Grid Cell 범위에 따라 로드 HLOD 언로드 셀의 원경

  2. Level Streaming

    던전·실내 · 제작자가 경계를 정한 Sublevel Persistent 기준 월드 Trigger Volume·C++·BP Sublevel 로드·표시 전환

  3. Data Layers

    퀘스트·시간대별 Actor 상태

  4. OFPA

    Actor별 파일로 협업 충돌 완화

  5. Streaming Source

    텔레포트 목적지를 미리 로드


레벨 스트리밍 (Level Streaming)

레벨 스트리밍은 메인 레벨(Persistent Level)에 필요한 서브 레벨(Sub-levels)들을 동적으로 로드하거나 언로드하여 메모리 사용량을 최적화하고 로딩 시간을 단축하는 기술입니다.

플레이어가 월드를 탐색함에 따라, 현재 위치 주변의 레벨만 메모리에 로드하고, 멀리 떨어진 레벨은 언로드하여 리소스를 절약합니다.

레벨 스트리밍의 작동 방식

퍼시스턴트 레벨 (Persistent Level): 항상 메모리에 로드되어 있는 메인 레벨입니다.

게임의 시작 지점, 전역 게임 로직, 공통 리소스 등이 포함될 수 있습니다.

서브 레벨 (Sub-levels): 퍼시스턴트 레벨에 동적으로 스트리밍될 작은 레벨들입니다.

각각의 서브 레벨은 특정 지역, 건물 내부, 던전 등을 포함할 수 있습니다.

스트리밍 볼륨 (Streaming Volume): 플레이어가 특정 볼륨(영역)에 진입하거나 이탈할 때 서브 레벨의 로드/언로드를 트리거하는 투명한 볼륨입니다.

레벨 스트리밍의 장점

  • 메모리 최적화: 모든 레벨 데이터를 동시에 로드할 필요가 없어 메모리 사용량을 크게 줄일 수 있습니다.
  • 로딩 시간 단축: 게임 시작 시 모든 것을 로드하는 대신, 필요한 부분만 점진적으로 로드하므로 초기 로딩 시간이 짧아집니다.
  • 끊김 없는 월드: 로딩 화면 없이 넓은 월드를 연속적으로 탐색하는 경험을 제공할 수 있습니다.
  • 협업 용이: 여러 팀원이 각자의 서브 레벨을 독립적으로 작업하고 나중에 병합할 수 있습니다.

레벨 스트리밍 구현 (C++)

레벨 스트리밍을 C++에서 제어하려면 UGameplayStatics 클래스의 함수들을 사용합니다.

AMyLevelStreamer.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyLevelStreamer.generated.h"

UCLASS()
class MYPROJECT_API AMyLevelStreamer : public AActor
{
    GENERATED_BODY()

public:
    AMyLevelStreamer();

    // 스트리밍할 서브 레벨의 이름 (에디터에서 설정)
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Level Streaming")
    FName LevelToStreamName;

protected:
    virtual void BeginPlay() override;

public:
    // 레벨을 로드하는 함수
    UFUNCTION(BlueprintCallable, Category = "Level Streaming")
    void LoadMyLevel();

    // 레벨을 언로드하는 함수
    UFUNCTION(BlueprintCallable, Category = "Level Streaming")
    void UnloadMyLevel();
};
AMyLevelStreamer.cpp
#include "MyLevelStreamer.h"
#include "Kismet/GameplayStatics.h" // UGameplayStatics::LoadStreamLevel 등 포함
#include "Engine/LevelStreaming.h"   // ULevelStreaming에 접근하기 위해 포함

AMyLevelStreamer::AMyLevelStreamer()
{
    PrimaryActorTick.bCanEverTick = false; // 이 액터는 틱이 필요 없을 수 있음
}

void AMyLevelStreamer::BeginPlay()
{
    Super::BeginPlay();
    // 필요에 따라 BeginPlay에서 자동으로 레벨을 로드할 수도 있습니다.
}

void AMyLevelStreamer::LoadMyLevel()
{
    if (LevelToStreamName.IsNone())
    {
        UE_LOG(LogTemp, Warning, TEXT("LevelToStreamName is not set for %s"), *GetName());
        return;
    }

    // 레벨 스트리밍 로드
    // LoadStreamLevel(월드, 레벨이름, 로드 시 보이는지 여부, 로드 후 활성화 여부)
    UGameplayStatics::LoadStreamLevel(this, LevelToStreamName, true, true, FLatentActionInfo());
    UE_LOG(LogTemp, Warning, TEXT("Attempting to load level: %s"), *LevelToStreamName.ToString());
}

void AMyLevelStreamer::UnloadMyLevel()
{
    if (LevelToStreamName.IsNone())
    {
        UE_LOG(LogTemp, Warning, TEXT("LevelToStreamName is not set for %s"), *GetName());
        return;
    }

    // 레벨 스트리밍 언로드
    UGameplayStatics::UnloadStreamLevel(this, LevelToStreamName, FLatentActionInfo(), false);
    UE_LOG(LogTemp, Warning, TEXT("Attempting to unload level: %s"), *LevelToStreamName.ToString());
}
사용법

메인 레벨(Persistent Level)에서 AMyLevelStreamer 액터 블루프린트(BP_MyLevelStreamer)를 배치합니다.

BP_MyLevelStreamer의 디테일 패널에서 Level To Stream Name에 스트리밍할 서브 레벨의 이름을 지정합니다 (예: Level_Forest, Level_City).

레벨 블루프린트나 다른 액터의 로직에서 BP_MyLevelStreamerLoad My Level 또는 Unload My Level 함수를 호출하여 스트리밍을 제어합니다.

일반적으로는 플레이어가 특정 트리거 볼륨(Trigger Volume)에 진입했을 때 이 함수들을 호출하도록 설정합니다.


월드 파티션 (World Partition, UE5 기준)

월드 파티션은 UE5에서 대규모 오픈 월드를 관리하는 기본 시스템입니다.

월드를 스트리밍 셀 단위로 자동 분할하고, 플레이어 위치/데이터 레이어 상태에 맞춰 필요한 셀만 로드합니다.

월드 파티션의 주요 특징

  • 자동 셀 분할: 하나의 월드를 스트리밍 셀로 나누어 메모리 사용량을 안정화합니다.
  • 거리 기반 자동 로드: 플레이어 주변 셀 중심으로 로드/언로드가 이루어집니다.
  • Data Layer: 퀘스트 단계, 시간대, 월드 상태별로 액터 집합을 분리해 제어할 수 있습니다.
  • One File Per Actor: 액터 단위 파일 분리로 충돌을 줄여 협업 효율이 높습니다.

월드 파티션은 자동 셀 스트리밍만 보는 것이 아니라, Data Layer와 HLOD, 협업 파일 구조까지 함께 점검해야 안정적으로 운영됩니다.

자동 스트리밍의 운영 단위

UE5 월드 파티션은 플레이어 거리만으로 끝나지 않습니다. 셀, Data Layer, HLOD, One File Per Actor를 함께 맞춰야 큰 월드가 안정됩니다.

  1. 거리 기반
    스트리밍 셀

    거리 기반 월드를 셀로 나누고 플레이어 주변만 로드해 메모리 사용량을 안정화합니다.

  2. 상태 기반
    Data Layer

    상태 기반 퀘스트 단계, 시간대, 월드 상태별 액터 묶음을 켜고 끕니다.

  3. 원거리 / 협업
    HLOD와 파일 구조

    원거리 / 협업 먼 거리 표현 비용을 낮추고 One File Per Actor로 작업 충돌을 줄입니다.

월드 파티션 사용 절차 (UE5)

새로운 레벨을 생성하거나 기존 레벨을 엽니다.

Window > World Settings를 엽니다.

World Partition이 활성화된 템플릿인지 확인합니다. (새 UE5 오픈 월드 템플릿은 기본 활성화)

Window > World Partition 도구를 열어 셀 로드 범위, Data Layer 상태, HLOD 구성을 점검합니다.

UE4 참고: 구버전 프로젝트의 World Composition은 레거시 개념으로만 이해하면 충분합니다.

신규 UE5 프로젝트에서는 월드 파티션 기준으로 설계하는 편이 유지보수에 유리합니다.

C++와 월드 파티션

월드 파티션은 엔진이 스트리밍을 자동 관리하므로, C++에서는 상태 확인과 예외 제어에 집중하는 방식이 일반적입니다.

  • 현재 로드된 레벨 확인: UGameplayStatics::GetCurrentLevelName() 또는 UWorld::GetStreamingLevels()를 사용하여 현재 스트리밍 중인 레벨 목록을 확인할 수 있습니다.
  • 액터 존재 여부 확인: 스트리밍된 레벨 내의 액터에 접근하기 전에 해당 레벨이 제대로 로드되었는지 확인하는 로직을 추가하는 것이 중요합니다.

언제 무엇을 사용할까요?

Level Streaming과 World Partition은 월드 규모와 제어 방식으로 고른다

월드 로딩 방식은 성능 옵션이 아니라 레벨 제작, 협업, 런타임 제어 구조를 함께 정하는 선택이다.

선택지맞는 상황함께 볼 것
Level Streaming던전, 실내, 스테이지처럼 경계가 분명하다트리거와 코드로 로드/언로드 시점을 제어한다
World Partition넓은 오픈월드와 많은 셀을 자동 관리한다거리, 셀, Data Layer, HLOD를 함께 본다
Collaboration작업 구역이 나뉘고 충돌을 줄여야 한다One File Per Actor와 Data Layer가 중요하다
Migration Cost초기 설계에서 월드 구조를 정한다나중에 바꾸면 제작 파이프라인이 크게 흔들린다
  • 레벨 스트리밍 (수동)
  • 특정 이벤트에 따라 명확하게 로드/언로드해야 하는 이벤트 기반의 레벨 전환 (예: 건물 내부 진입, 던전 입구).

  • 게임 내의 작은 구역들을 분할하여 관리할 때.

  • 월드 파티션 자동 로직 외에 특정 연출 타이밍 제어가 필요할 때.

  • 월드 파티션 (UE5) (자동)
  • 거대한 오픈 월드를 제작할 때.

  • 플레이어가 넓은 지역을 자유롭게 탐색하는 게임.

  • 수많은 타일 레벨로 구성된 월드를 자동으로 관리하고 싶을 때.

  • 대규모 팀 협업이 중요한 오픈 월드 프로젝트.

대부분의 현대 오픈 월드 게임은 UE5의 월드 파티션을 중심으로 구축됩니다.

월드 컴포지션은 구버전 레거시 프로젝트를 읽을 때 필요한 배경 지식으로 이해하면 충분합니다.

아래 다이어그램은 수동 레벨 스트리밍과 World Partition을 선택하고 운영할 때의 확인 지점을 정리한 것입니다.

명시적 장면 전환은 Level Streaming, 넓은 공간 자동 관리는 World Partition

둘 다 메모리를 줄이지만 경계를 정의하는 주체가 다르다. UE5의 새 대규모 월드는 World Partition이 기본 선택이다.

  1. Level Streaming · 수동 경계

    서브레벨 단위. 문·엘리베이터·던전처럼 게임 이벤트가 load·visibility·unload 시점을 정확히 소유할 때 적합하다.

  2. World Partition · 공간 경계

    단일 persistent world + grid cells. 플레이어 등 Streaming Source와 거리로 필요한 셀을 자동 관리한다.

  3. 1 · Actor

    · Actor Is Spatially Loaded와 Runtime Grid를 정한다.

  4. 2 · Cell

    · Cell 공간 위치에 따라 런타임 셀에 묶인다.

  5. 3 · Source

    · Source 플레이어·컴포넌트의 범위와 priority가 셀을 고른다.

  6. 4 · State

    · State 교차한 셀을 Loaded 또는 Activated 상태로 만든다.

  7. 5 · HLOD

    · HLOD 멀리 언로드된 셀은 시각 proxy로 이어 보인다.


이제는 언리얼 엔진에서 거대한 게임 월드를 효율적으로 구성하고 관리하기 위한 핵심 기술인 레벨 스트리밍과 월드 파티션의 개념을 이해하셨습니다.

이 기능들을 적절히 활용하면 메모리 문제를 해결하고, 로딩 시간을 줄이며, 끊김 없는 플레이 경험을 제공하는 대규모 게임을 만들 수 있습니다.

마지막으로 레벨 스트리밍의 로드, 참조, 언로드 책임을 운영 흐름으로 다시 확인해 보세요.

스트리밍은 로드 완료 전 참조를 막고 언로드 전 상태를 정리하는 일이다

큰 월드를 숨기는 것이 아니라 필요한 공간만 끊김 없이 경험하게 만드는 수명 관리다.

수명 구간해야 할 일위험 신호
Detect거리, 볼륨, 퀘스트, Data Layer 상태로 필요한 구역을 정한다로드 기준이 이벤트와 거리에서 충돌한다
Load / Ready로드를 요청하고 보임, 참조, 메모리 준비를 기다린다레벨이 보이기 전 액터 포인터를 믿는다
Reference스트리밍 액터 접근 전 유효성과 로드 상태를 확인한다hard reference 때문에 구역이 같이 올라온다
Unload지역 상태를 저장하고 참조를 끊은 뒤 내린다문 열림, 처치 상태, 퍼즐 진행이 사라진다