
런타임에 cell이 어떻게 로드/언로드/활성화/비활성화 하는지 살펴보자.
용어정리
| 축 | 타입 | 역할 |
|---|---|---|
| 스트리밍 소스 | FWorldPartitionStreamingSource | "어디를, 어떤 상태로(Loaded/Activated) 로드할지"를 담은 요청 단위 |
| 스트리밍 정책 | UWorldPartitionStreamingPolicy | 매 프레임 소스를 모아 셀 상태 머신을 돌리는 두뇌 |
| 셀 | UWorldPartitionRuntimeCell | 상태 전이(Load/Unload/Activate/Deactivate)를 ULevelStreaming에 위임하는 추상 객체 |
스트리밍의 중심이 되는 소스로, APlayerController가 IWorldPartitionStreamingSourceProvider를 상속받아 사용된다.
만약 커스텀 소스를 만들고 싶다면 UWorldPartitionStreamingSourceComponent를 붙이면 된다.
소스는 "제공자(provider)"가 매 프레임 내놓는다. 인터페이스는 단수/복수 두 진입점을 가진다.
struct IWorldPartitionStreamingSourceProvider
{
virtual bool GetStreamingSource(FWorldPartitionStreamingSource& OutSource) const;
virtual bool GetStreamingSources(TArray<FWorldPartitionStreamingSource>& OutSources) const; // 기본: 단수 호출해 배열에 담음
virtual const UObject* GetStreamingSourceOwner() const;
};
기본 GetStreamingSources는 단수 GetStreamingSource를 호출해 배열 한 칸에 담는 구현이다. 즉 대부분의 제공자는 단수만 구현하면 된다.
소스 하나는 위치/회전/반경/우선순위/타깃 상태를 가진 단일 스트리밍 요청이다.
| 필드 | 의미 |
|---|---|
Name | 소스 고유 이름 |
Location / Rotation | 소스 위치/회전 |
TargetState | Loaded 또는 Activated — 이 소스가 원하는 셀 상태 |
Priority | 소스 우선순위 (Highest=0 … Lowest=255, 작을수록 우선) |
bBlockOnSlowLoading | 느린 스트리밍 시 블록 대상 소스인지 |
Velocity | 속도(예측 로딩 정렬 기여) |
TargetBehavior / TargetGrids | 대상 그리드 포함/제외 |
Shapes | 소스 내부 shape 목록(비어 있으면 구를 자동 생성) |
여기서 가장 중요한 건 TargetState다. enum은 딱 두 값뿐이다.
enum class EStreamingSourceTargetState : uint8 { Loaded, Activated };
Loaded — 셀을 메모리에 올려두되 월드에 추가하지 않음(액터가 틱/렌더되지 않음). 예: 근처지만 아직 안 보이는 영역을 미리 준비.Activated — 로드 + 월드에 추가(가시). 실제로 보고 상호작용하는 영역.소스의 영향 범위는 FSphericalSector(구면 부채꼴)로 표현된다. 중심/반경/축/각도를 가지며, 각도가 360이면 완전한 구다.
struct FSphericalSector { /* Center, Radius, Axis, Angle ... */ };
shape를 실제로 만드는 함수는 FStreamingSourceShapeHelper::ForEachShape다.
Shapes가 비어 있으면, 기본 반경 + ExtraRadius로 단일 구 shape를 만든다.Shapes가 있으면, 각 shape마다 반경(bUseGridLoadingRange면 그리드 로딩 반경 × LoadingRangeScale, 아니면 Radius), 각도(bIsSector면 SectorAngle, 아니면 360), 축을 계산해 FSphericalSector를 만든다.bForce2D(2D 투영)면 Yaw만 남기고 Z를 0으로 만든다.또한 소스는 특정 그리드만 노릴 수 있다.
IsSourceAffectingGrid가 Include/Exclude 규칙으로 그리드를 걸러낸 뒤에야 shape를 만든다
앞 편에서 본 SpatialHash의 GetCells가 바로 이 그리드명 버전 ForEachShape를 호출한다(소스와 셀 생성부가 맞물리는 경계)
정책은 소스를 직접 만들지 않고 서브시스템에서 받는다.

CanStream이 거짓이면 소스를 비우고, 최적화가 켜져 있고 소스 해시가 직전과 같으면 배열 전체를 다시 받지 않고 Velocity만 갱신한다(예측 로딩용).
셀의 상태를 제어하는 역할.
셀의 상태
- 로드 (Load)
- 언로드 (Unload)
- 활성화 (Activate)
- 비활성화 (Deactivate)
정책 자신은 상태 머신의 제어자일 뿐, 실제 레벨 조작은 셀 ULevelStreaming으로 내려간다.
로드/언로드/활성/비활성은 "행위(전이)"이고, 셀이 실제로 놓이는 "상태"는 3단계 enum이다.
enum class EWorldPartitionRuntimeCellState : uint8 { Unloaded, Loaded, Activated };
이 순서 Unloaded < Loaded < Activated는 코드가 의존하는 불변식이며 static_assert로 강제된다(...RuntimeCell.h:229). 뒤에서 볼 완료 판정의 CellState < QueryState 비교가 이 순서를 전제로 한다.

LoadedActivatedActivated → Loaded (강등)Unloaded중요한 포인트. 셀의 현재 상태는 정책이 직접 저장하는 게 아니라, 셀이 감싼 ULevelStreaming의 실제 상태(ELevelStreamingState)에서 유도된다.
UWorldPartitionRuntimeLevelStreamingCell::GetCurrentState:
LoadedVisible → ActivatedLoadedNotVisible 이상 → LoadedbIsAlwaysLoaded면 Activated, 아니면 Unloaded셀 본체는 추상 클래스이고, 전이 함수는 전부 순수 가상이다. 구현은 UWorldPartitionRuntimeLevelStreamingCell이 맡는다.
| 메서드 | 의미 |
|---|---|
Load() const | 셀을 로드하되 월드에 추가하지 않음 |
Unload() const | 셀 언로드 |
CanUnload() const | 언로드 가능 여부 |
Activate() const | 로드 + 월드에 추가(가시) |
Deactivate() const | 월드에서 제거, 로드는 유지 |
GetCurrentState() const | 현재 셀 상태 |
셀은 이 밖에 bIsAlwaysLoaded(항상 로드 → 언로드 안 함), bIsSpatiallyLoaded, DataLayers, bIsHLOD, bBlockOnSlowLoading, RuntimeCellData(거리/우선순위 캐싱) 같은 멤버를 가진다.
전이는 전부 ULevelStreaming에 위임된다
UWorldPartitionRuntimeLevelStreamingCell은 네 전이를 모두 UWorldPartitionLevelStreamingDynamic에 위임한다.
| 셀 메서드 | 위임 대상 |
|---|---|
Load() | LevelStreaming->Load() |
Activate() | LevelStreaming->Activate() (LevelStreaming 없고 커스텀 HLOD면 OnCellShown()) |
Deactivate() | LevelStreaming->Deactivate() |
Unload() | LevelStreaming->Unload() |
CanUnload() | 항상 true |
그리고 ULevelStreamingDynamic의 전이는 결국 세 플래그 세팅으로 끝난다.
| 전이 | ShouldBeLoaded | ShouldBeVisible | 비고 |
|---|---|---|---|
Load() | true | false | 월드 스트리밍 레벨에 추가 |
Activate() | true | true | 월드에 추가(가시) |
Deactivate() | (유지) | false | 가시성만 내림 |
Unload() | false | false | 언로드/제거 요청 |
즉 "셀을 활성화한다"는 결국 bShouldBeLoaded = bShouldBeVisible = true로 만드는 것이고, 실제 레벨 추가/제거는 언리얼의 기존 레벨 스트리밍(UWorld::UpdateLevelStreaming → AddToWorld/RemoveFromWorld)이 처리한다. World Partition은 그 위에 "어떤 셀을 언제 그 상태로 만들지"를 얹은 층이다.
서브시스템이 매 프레임 해당 함수를 호출한다.

RuntimeHash->ForEachStreamingCellsSources가 소스가 커버하는 셀을 훑고, 콜백이 소스의 TargetState에 따라 FrameLoadCells / FrameActivateCells로 분류한다
같은 셀이 로드/활성 양쪽에 걸리면 FrameLoadCells = FrameLoadCells.Difference(FrameActivateCells)로 활성 쪽만 남긴다
현재 상태와 이번 프레임 희망을 비교해 네 집합을 만든다.
| 집합 | 의미 |
|---|---|
ToActivateCells | 새로 활성화할 셀 |
ToLoadCells | 새로 로드할(또는 활성→로드 강등) 셀 |
ToDeactivateCells | 활성→비활성 강등 (주로 서버) |
ToUnloadCells | 현재 로드/활성인데 이번 프레임 후보에 없는 셀 → 언로드 대상 |
예산(MaxCellsToLoad)이 한정돼 있으므로 SortCompare로 순서를 정한다.
정책은 "목록을 만들 뿐", 전이 함수를 직접 부르는 건 정책의 SetCellState*와 서브시스템이다.
| 함수 | 하는 일 |
|---|---|
SetCellStateToActivated | 셀이 LoadedCells에 있으면 빼고(승격), 아니면 예산(--MaxCellsToLoad) 소모 → ActivatedCells.Add + InCell->Activate() |
SetCellStateToLoaded | 활성 상태면 Deactivate() 후 로드로 강등, 아니면 예산 내 Load() → LoadedCells.Add |
SetCellsStateToUnloaded | CanUnload()가 참인 셀을 Unload()하고 두 집합에서 제거 |
호출 주체를 정리하면:
UpdateStreamingStateInternal이 GetCellsToUpdate로 모은 셀을 정렬·예산화해 SetCellStateToActivated/SetCellStateToLoaded 호출PostUpdateStreamingStateInternal_GameThread가 직접 SetCellsStateToUnloaded 처리즉 로드/활성은 예산 제한을 받고(프레임당 N개), 언로드는 곧바로 처리된다.
Activate()를 불렀다고 해서 그 프레임에 바로 화면에 뜨는 게 아니다.
레벨이 실제로 월드에 추가되어 보이게 되면 ULevelStreaming이 OnLevelShown/OnLevelHidden 델리게이트를 쏘고, 이게 셀의 OnCellShown/OnCellHidden을 거쳐 정책까지 전파된다.

그래서 정책은 활성 셀을 FActivatedCells 안에서 Cells(활성 요청됨)와 PendingAddToWorldCells(요청됐지만 아직 안 보임)로 나눠 추적한다
스트리밍이 끝났는지 판정할 때 이 구분이 중요하다.
IsStreamingCompleted(소스)는 비공간 셀(데이터 레이어)과 각 소스 기반 공간 질의를 차례로 검사한다. 셀의 현재 상태가 질의 상태와 다르면(스킵 불가 조건에서) 미완료로 본다.
스트리밍이 너무 느려서 플레이어가 아직 안 로드된 영역으로 넘어가버릴 위험이 있으면, 프레임을 멈추고서라도 로드를 끝내야 한다.
UpdateStreamingPerformance가 World->bRequestedBlockOnAsyncLoading = true로 만들어 다음 프레임 블로킹 로딩을 요청CanAddCellToWorld가 비블로킹 셀의 월드 추가를 막아 급한 셀부터 처리bBlockOnSlowLoading과 셀의 bBlockOnSlowLoading 플래그로 정함