World Partition C++ API
프로그래밍을 통한 World Partition 제어 및 커스텀 스트리밍 구현
WorldPartitionSubsystem 접근
World Partition 시스템의 핵심 서브시스템
UWorldPartitionSubsystem은 런타임에서 World Partition의 상태를 쿼리하고 제어할 수 있는 월드 서브시스템입니다.
#include "WorldPartition/WorldPartitionSubsystem.h"
// WorldPartitionSubsystem 접근
void AMyActor::AccessWorldPartition()
{
if (UWorldPartitionSubsystem* WPSubsystem =
GetWorld()->GetSubsystem<UWorldPartitionSubsystem>())
{
// World Partition 활성화 여부 확인
bool bIsEnabled = WPSubsystem->IsStreamingEnabled();
// 현재 로드된 셀 정보 조회
// 스트리밍 상태 모니터링
// 커스텀 스트리밍 로직 구현
}
}
월드의 World Partition 상태와 스트리밍을 관리하는 서브시스템. GetWorld()->GetSubsystem<>()으로 접근합니다.
커스텀 스트리밍 소스 구현
플레이어 외 위치 기반 셀 로딩
기본 플레이어 기반 스트리밍 외에 추가적인 스트리밍 소스가 필요한 경우 UWorldPartitionStreamingSourceComponent를 사용합니다.
#pragma once
#include "CoreMinimal.h"
#include "WorldPartition/WorldPartitionStreamingSourceComponent.h"
#include "CustomStreamingSource.generated.h"
/**
* 커스텀 스트리밍 소스 컴포넌트
* 플레이어 외 위치에서 셀 로딩이 필요할 때 사용
*/
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MYGAME_API UCustomStreamingSourceComponent
: public UWorldPartitionStreamingSourceComponent
{
GENERATED_BODY()
public:
UCustomStreamingSourceComponent();
// 스트리밍 소스 활성화/비활성화
UFUNCTION(BlueprintCallable, Category = "Streaming")
void EnableStreaming(bool bEnable);
// 스트리밍 우선순위 설정
UFUNCTION(BlueprintCallable, Category = "Streaming")
void SetCustomStreamingPriority(float NewPriority);
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
UPROPERTY(EditAnywhere, Category = "Streaming")
float CustomLoadingRange = 25600.0f;
UPROPERTY(EditAnywhere, Category = "Streaming")
bool bActivateOnBeginPlay = true;
};
#include "CustomStreamingSource.h"
#include "WorldPartition/WorldPartitionSubsystem.h"
UCustomStreamingSourceComponent::UCustomStreamingSourceComponent()
{
PrimaryComponentTick.bCanEverTick = false;
// 기본 스트리밍 설정
bStreamingSourceEnabled = false;
StreamingSourcePriority = 1.0f;
}
void UCustomStreamingSourceComponent::BeginPlay()
{
Super::BeginPlay();
if (bActivateOnBeginPlay)
{
EnableStreaming(true);
}
}
void UCustomStreamingSourceComponent::EndPlay(
const EEndPlayReason::Type EndPlayReason)
{
EnableStreaming(false);
Super::EndPlay(EndPlayReason);
}
void UCustomStreamingSourceComponent::EnableStreaming(bool bEnable)
{
// 스트리밍 소스 활성화 상태 변경
SetStreamingSourceEnabled(bEnable);
UE_LOG(LogTemp, Log, TEXT("Streaming Source %s: %s"),
*GetOwner()->GetName(),
bEnable ? TEXT("Enabled") : TEXT("Disabled"));
}
void UCustomStreamingSourceComponent::SetCustomStreamingPriority(
float NewPriority)
{
StreamingSourcePriority = FMath::Clamp(NewPriority, 0.0f, 10.0f);
}
런타임 셀 상태 조회
현재 로드된 셀 정보 확인 및 모니터링
#include "WorldPartition/WorldPartitionRuntimeCell.h"
// 스트리밍 상태 모니터링
void AStreamingMonitor::CheckStreamingStatus()
{
if (UWorldPartitionSubsystem* WPSubsystem =
GetWorld()->GetSubsystem<UWorldPartitionSubsystem>())
{
// 스트리밍 활성화 여부
if (!WPSubsystem->IsStreamingEnabled())
{
UE_LOG(LogTemp, Warning,
TEXT("World Partition streaming is disabled"));
return;
}
// 현재 플레이어 위치의 셀 상태 확인
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
if (APawn* Pawn = PC->GetPawn())
{
FVector PlayerLocation = Pawn->GetActorLocation();
// 디버그 정보 출력
UE_LOG(LogTemp, Log, TEXT("Player at: %s"),
*PlayerLocation.ToString());
}
}
}
}
// 콘솔 명령어를 통한 디버깅
// wp.Runtime.ToggleDrawRuntimeHash2D - 2D 그리드 시각화
// wp.Runtime.ToggleDrawRuntimeHash3D - 3D 그리드 시각화
// stat WorldPartition - 스트리밍 통계
개발 중 wp.Runtime.ToggleDrawRuntimeHash2D 콘솔 명령을 사용하면 현재 로드된 셀을 시각적으로 확인할 수 있습니다. 녹색은 로드된 셀, 빨간색은 언로드된 셀을 나타냅니다.
서버 스트리밍 (멀티플레이어)
UE 5.1+ 서버 측 World Partition 스트리밍
기본적으로 Dedicated Server와 Listen Server에서는 모든 셀이 항상 로드됩니다. UE 5.4+에서는 서버 스트리밍을 활성화하여 서버의 메모리 사용량을 최적화할 수 있습니다.
; 서버 스트리밍 활성화
[/Script/Engine.WorldPartition]
wp.Runtime.EnableServerStreaming=1
; 서버 셀 로딩 범위 설정
wp.Runtime.ServerCellLoadingRange=51200
// 서버에서의 스트리밍 소스 관리
void AMyGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
// 서버에서 각 플레이어를 스트리밍 소스로 등록
if (HasAuthority())
{
if (APawn* Pawn = NewPlayer->GetPawn())
{
// 플레이어 Pawn에 스트리밍 소스 컴포넌트 추가
UWorldPartitionStreamingSourceComponent* StreamingComp =
Pawn->FindComponentByClass<UWorldPartitionStreamingSourceComponent>();
if (!StreamingComp)
{
StreamingComp = NewObject<UWorldPartitionStreamingSourceComponent>(Pawn);
StreamingComp->RegisterComponent();
StreamingComp->SetStreamingSourceEnabled(true);
}
}
}
}
서버 스트리밍 활성화 시 AI나 NPC가 언로드된 영역에서 제대로 동작하지 않을 수 있습니다. Always Loaded 설정이나 Data Layer를 활용하여 중요 액터가 항상 로드되도록 설정하세요.
Level Streaming과의 통합
기존 레벨 스트리밍 API와의 호환
World Partition은 내부적으로 레벨 스트리밍을 사용합니다. 특수한 경우 기존 레벨 스트리밍 API를 함께 활용할 수 있습니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "LevelStreamerActor.generated.h"
/**
* 볼륨 기반 레벨 스트리밍 액터
* 인테리어, 던전 등 World Partition 외 영역에 사용
*/
UCLASS()
class MYGAME_API ALevelStreamerActor : public AActor
{
GENERATED_BODY()
public:
ALevelStreamerActor();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
UBoxComponent* OverlapVolume;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Streaming")
FName LevelToLoad;
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep,
const FHitResult& SweepResult);
UFUNCTION()
void OnOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
};
#include "LevelStreamerActor.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/Character.h"
ALevelStreamerActor::ALevelStreamerActor()
{
OverlapVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapVolume"));
RootComponent = OverlapVolume;
OverlapVolume->OnComponentBeginOverlap.AddDynamic(
this, &ALevelStreamerActor::OnOverlapBegin);
OverlapVolume->OnComponentEndOverlap.AddDynamic(
this, &ALevelStreamerActor::OnOverlapEnd);
}
void ALevelStreamerActor::OnOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep,
const FHitResult& SweepResult)
{
ACharacter* PlayerChar = UGameplayStatics::GetPlayerCharacter(this, 0);
if (OtherActor == PlayerChar && !LevelToLoad.IsNone())
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.UUID = GetUniqueID();
LatentInfo.Linkage = 0;
UGameplayStatics::LoadStreamLevel(
this,
LevelToLoad,
true, // bMakeVisibleAfterLoad
false, // bShouldBlockOnLoad (비동기)
LatentInfo
);
}
}
void ALevelStreamerActor::OnOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex)
{
ACharacter* PlayerChar = UGameplayStatics::GetPlayerCharacter(this, 0);
if (OtherActor == PlayerChar && !LevelToLoad.IsNone())
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.UUID = GetUniqueID();
LatentInfo.Linkage = 0;
UGameplayStatics::UnloadStreamLevel(
this,
LevelToLoad,
LatentInfo,
false // bShouldBlockOnUnload
);
}
}
핵심 요약
- UWorldPartitionSubsystem으로 런타임 World Partition 상태 쿼리 및 제어
- UWorldPartitionStreamingSourceComponent로 커스텀 스트리밍 소스 구현
- 서버 스트리밍은 UE 5.4+에서 지원, 멀티플레이어 서버 메모리 최적화
- 디버그 명령어
wp.Runtime.ToggleDrawRuntimeHash2D로 셀 시각화 - 기존 Level Streaming API는 인테리어/던전 등 특수 상황에서 병행 사용 가능
도전 과제
배운 내용을 직접 실습해보세요
C++에서 커스텀 스트리밍 소스를 구현하여, 플레이어가 아닌 특정 위치(퀘스트 목적지 등)를 기준으로 미리 셀을 로드하세요. UWorldPartitionStreamingPolicy를 활용하세요.
IWorldPartitionStreamingSourceProvider 인터페이스를 구현하고, 셀이 로드/언로드될 때 호출되는 콜백에서 RPG NPC 초기화/정리 로직을 실행하세요.
UWorldPartitionSubsystem을 통해 런타임에 특정 셀의 로딩 상태를 쿼리하고, 이벤트(보스전 등) 시 필요한 영역을 강제 로드하는 시스템을 C++로 구현하세요.