C++를 사용한 레벨 로딩 및 언로딩

C++로 생성한 커스텀 스트리밍 액터를 사용하여 레벨을 스트리밍하는 방법을 설명합니다.

Choose your operating system:

Windows

macOS

Linux

이번 예시에서 파티오 레벨 스트리밍을 받기 시작하여 플레이어가 모퉁이를 돌아 파티오에 접근할 때쯤에는 스트리밍 레벨이 완전히 로드되어 화면에 나타나도록 만들겠습니다.

StreamingLevelVisible.png

초기 설정으로 SunTemple_PersistentSunTemple_Streaming 두 레벨이 준비되어 있습니다. 플레이어 스타트(Player Start)SunTemple_Persistent 에 있으며, 게임 속 플레이어는 캐릭터(Character) 로 표현됩니다.

  1. 콘텐츠 브라우저(Content Browser) 에서 SunTemple_Persistent 를 엽니다.

  2. 플레이어 스타트 를 사원의 시작 지점으로 이동합니다.

    PlayerStart.png

  3. 창(Windows) 을 클릭한 다음 레벨(Levels) 을 선택합니다.

  1. 레벨 드롭다운 메뉴를 클릭한 다음, 기존 추가(Add Existing...) 를 선택하여 새로운 서브레벨을 추가합니다.

    AddExisting.png

  2. 레벨 열기(Open Level) 대화창에서 추가할 SunTemple_Streaming 을 선택한 다음, 열기(Open) 를 클릭합니다.

    SunTempleStreaming_Select.png

  3. 퍼시스턴트 레벨(Persistent Level)우클릭 한 다음, 드롭다운 메뉴에서 현재로 만들기(Make Current) 를 선택합니다.

C++로 레벨 스트리밍 받기

  1. 콘텐츠 브라우저 를 열고 새 C++ 클래스(C++ Class) 를 생성합니다. 이 클래스는 액터(Actor) 에 기반할 것이므로 액터 를 선택하고 다음(Next) 을 클릭합니다.

  2. C++ 클래스 이름을 'LevelStreamerActor'로 지정한 다음, 클래스 생성(Create Class) 을 클릭합니다. 이제 새 클래스가 Visual Studio나 XCode에서 열립니다.

이 시나리오에서는 캐릭터(Character) 가 LevelStreamerActor에 있는 OverlapVolume이라는 박스 컴포넌트와 오버랩되면 두 번째 레벨을 스트리밍으로 받겠습니다.

  1. LevelStreamerActor.h 에 VisibleAnywhere, BlueprintReadOnly이고 AllowPrivateAccess 메타 플래그가 있는 OverlapVolume을 선언합니다.

    private:
    // 레벨 스트리밍을 트리거하는 볼륨을 오버랩함
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    UBoxComponent* OverlapVolume;
  2. LevelStreamerActor.cpp 의 LevelStreamerActor 생성자에서 OverlapVolume을 생성하고 이를 RootComponent로 만듭니다.

    OverlapVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapVolume"));
    RootComponent = OverlapVolume;
  3. LevelStreamerActor.h 로 돌아가 BoxComponent의 OnComponentBeginOverlap 함수에 바운딩될 프로텍티드 OverlapBegins 함수를 선언합니다. 이 바인딩이 의미하는 바는 OverlapBegins에 반드시 UFUNCTION 매크로 태그가 지정되어져야 하며, OnComponentBeginOverlap과 같은 시그니처가 있어야 한다는 뜻입니다.

    protected:
    
    UFUNCTION()
    void OverlapBegins(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
  4. 또한 LevelStreamerActor.h 에서 LevelToLoad라고 하는 EditAnywhere인 프로텍티드 FName 변수를 만듭니다. 그러면 인스턴스별로 LevelToLoad를 변경할 수 있습니다.

    UPROPERTY(EditAnywhere)
    FName LevelToLoad;
  5. GameplayStatics 라이브러리의 함수를 몇 가지 사용할 예정이니 LevelStreamerActor.cpp 의 맨 위에 포함시킵니다.

    #include "Kismet/GameplayStatics.h"
  6. 이제 OverlapBegins 함수 기능을 생성할 준비를 마쳤습니다. LevelStreamerActor.cpp 에서 함수를 정의하기 시작합니다. GameplayStatics 함수인 GetPlayerCharacter 함수를 사용하여 인덱스 0에서 캐릭터를 가져올 수 있습니다.

    void ALevelStreamerActor::OverlapBegins(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
    {
            ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);        
    }
  7. MyCharacter를 가져온 다음, BoxComponent와 오버랩되는 OtherActor와 대조합니다. 또한, LevelToLoad가 비어 있지 않음을 확인한 다음, LoadStreamLevel을 호출합니다.

    if (OtherActor == MyCharacter && LevelToLoad != "")
    {
        FLatentActionInfo LatentInfo;
        UGameplayStatics::LoadStreamLevel(this, LevelToLoad, true, true, LatentInfo);
    }
  8. LevelStreamerActor 생성자에서 OverlapBegins를 BoxComponent의 OnComponentBeginOverlap에 바인딩합니다.

    OverlapVolume->OnComponentBeginOverlap.AddUniqueDynamic(this, &ALevelStreamerActor::OverlapBegins);

    최종 LevelStreamerActor.h 는 다음과 같습니다.

    #pragma once
    
    #include "GameFramework/Actor.h"
    #include "LevelStreamerActor.generated.h"
    
    UCLASS()
    class LEVELS_API ALevelStreamerActor : public AActor
    {
        GENERATED_BODY()
    
    public: 
        // 이 액터 프로퍼티의 기본값을 설정함
        ALevelStreamerActor();
    
        // 프레임마다 호출됨
        virtual void Tick( float DeltaSeconds ) override;
    
    protected:
    
        // 게임 시작 또는 스폰 시 호출됨
        virtual void BeginPlay() override;
    
        UFUNCTION()
        void OverlapBegins(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
    
        UPROPERTY(EditAnywhere)
        FName LevelToLoad;
    
    private:
        // 레벨 스트리밍을 트리거하는 볼륨을 오버랩함
        UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
        UBoxComponent* OverlapVolume;
    
    };

    최종 LevelStreamerActor.cpp 는 다음과 같습니다.

    #include "Levels.h"
    #include "Kismet/GameplayStatics.h"
    #include "LevelStreamerActor.h"
    
    // 기본값을 설정함
    ALevelStreamerActor::ALevelStreamerActor()
    {
        // 매 프레임마다 Tick()을 호출하도록 이 액터를 설정하세요.  필요 없는 경우 퍼포먼스 향상을 위해 이 설정을 끌 수 있습니다.
        PrimaryActorTick.bCanEverTick = true;
    
        OverlapVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapVolume"));
        RootComponent = OverlapVolume;
    
        OverlapVolume->OnComponentBeginOverlap.AddUniqueDynamic(this, &ALevelStreamerActor::OverlapBegins);
    }
    // 게임 시작 또는 스폰 시 호출됨
    void ALevelStreamerActor::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // 프레임마다 호출됨
    void ALevelStreamerActor::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }
    
    void ALevelStreamerActor::OverlapBegins(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
    {
            ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);
            if (OtherActor == MyCharacter && LevelToLoad != "")
            {
                FLatentActionInfo LatentInfo;
                UGameplayStatics::LoadStreamLevel(this, LevelToLoad, true, true, LatentInfo);
            }
    }
  9. 코드를 컴파일한 다음, 에디터로 돌아갑니다.

  10. LevelStreamer 액터를 레벨에 배치하고 그 위치와 스케일을 조절해서 퍼시스턴트 월드에서 캐릭터 가 들어설 때 스트리밍하기 시작할 영역을 감싸고 스트리밍 레벨이 배치되어 캐릭터가 걸어 다닐 전체 볼륨도 감싸도록 만듭니다.

  11. SunTemple_Streaming스트리밍할 레벨(Level to Stream) 로 입력합니다.

  12. 에디터에서 플레이(Play in Editor) 를 사용하여 스트리밍 레벨을 테스트해봅니다.

C++로 레벨 언로딩하기

캐릭터 가 BoxComponent를 나갈 때 레벨을 언로드하기 위해 UGameplayStatics::UnloadStreamLevel 을 호출하는 OverlapEnds 함수를 생성하여 OnComponentEndOverlap 에 바인딩하겠습니다. 다음 코드 스니펫을 LevelStreamerActor에 추가합니다.

LevelStreamerActor.h에:

UFUNCTION()
void OverlapEnds(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

LevelStreamerActor.cpp에:

void ALevelStreamerActor::OverlapEnds(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
        ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);
        if (OtherActor == MyCharacter && LevelToLoad != "")
        {
            FLatentActionInfo LatentInfo;
            UGameplayStatics::UnloadStreamLevel(this, LevelToLoad, LatentInfo);
        }
}

생성자에:

OverlapVolume->OnComponentEndOverlap.AddUniqueDynamic(this, &ALevelStreamerActor::OverlapEnds);