스팀 게임 업데이트 중단한 채로 플레이하는 방법

Choose your operating system:

ChunkDownloader언리얼 엔진 의 패치 솔루션입니다. 원격 서비스로부터 에셋을 다운로드하고 게임에서 사용할 수 있도록 이를 메모리에 마운트하여 손쉽게 업데이트와 에셋을 제공할 수 있습니다. 이 가이드에서는 프로젝트에 ChunkDownloader를 구현하는 방법을 보여줍니다. 이 가이드를 완료하면 다음과 같은 작업을 할 수 있습니다.

  • ChunkDownloader 플러그인을 활성화하고 프로젝트의 종속성에 추가합니다.

  • 콘텐츠를 청크에 정리하고, .pak 파일로 패키징하고, 다운로드를 위한 매니페스트 파일을 준비합니다.

  • ChunkDownloader를 게임의 코드에 구현하여 원격 .pak 파일을 다운로드합니다.

  • 마운트된 .pak 파일의 콘텐츠에 안전하게 액세스합니다.

1. 필수 설정 및 권장 에셋

계속 진행하기 전에 다음 가이드를 검토하고 각각의 단계를 따라야 합니다.

  • ChunkDownloader 플러그인 설정

  • 에셋 청크 준비

  • ChunkDownloader에 대한 매니페스트 및 에셋 호스팅

이 가이드에서는 프로젝트에 ChunkDownloader 플러그인을 추가하고, 에셋에 대한 청크 스키마를 설정하고, 이를 로컬 테스트 서버에 배포하는 방법을 설명합니다. 검토를 위해 예시 프로젝트의 이름은 PatchingDemo 여야 하며, 다음과 같이 생성되어야 합니다.

  1. 기본 템플릿 을 기반으로 하는 C++ 프로젝트 입니다.

  2. ChunkDownloader 플러그인은 플러그인(Plugins) 메뉴에서 활성화됩니다.

  3. Pak 파일 사용(Use Pak File)청크 생성(Generate Chunks) 은 모두 프로젝트 세팅(Project Settings) > 프로젝트(Project) > 패키징(Packaging) 에서 활성화됩니다.

  4. Paragon의 Boris, CrunchKhaimera 에셋이 프로젝트에 추가됩니다.

    • 언리얼 마켓플레이스 에서 무료로 다운로드할 수 있습니다.

    • 개별 폴더로 분리되어 있는 한 원하는 에셋을 사용할 수 있습니다.

  5. 세 캐릭터의 폴더 각각에는 프라이머리 에셋 라벨 이 다음 청크 ID 에 적용되어 있습니다.

    폴더

    청크 ID

    ParagonBoris

    1001

    ParagonCrunch

    1002

    ParagonKhaimera

    1003

  6. 콘텐츠를 쿠킹하고 위 청크 ID 각각에 대한 .pak 파일이 있습니다.

  7. 다음 정보가 포함된 BuildManifest-Windows.txt 라는 매니페스트 파일 이 있습니다.

    BuildManifest-Windows.txt

    $NUM_ENTRIES = 3
    $BUILD_ID = PatchingDemoKey
    pakchunk1001-WindowsNoEditor.pak    922604157   ver 1001    /Windows/pakchunk1001-WindowsNoEditor.pak
    pakchunk1002-WindowsNoEditor.pak    2024330549  ver 1002    /Windows/pakchunk1002-WindowsNoEditor.pak
    pakchunk1003-WindowsNoEditor.pak    1973336776  ver 1003    /Windows/pakchunk1003-WindowsNoEditor.pak

    각 청크에 대한 모든 필드는 동일한 줄에 포함되어야 하며, 탭으로 구분되어야 합니다. 그렇지 않은 경우 구문 분석이 올바르게 되지 않습니다.

  8. .pak 파일과 매니페스트 파일은 로컬 호스팅 웹 사이트에 배포됩니다. 이를 설정하는 방법에 관한 지침은 ChunkDownloader에 대한 매니페스트 및 에셋 호스팅을 참조하세요.

  9. 프로젝트에 대한 DefaultGame.ini 파일에는 CDN URL 이 다음과 같이 정의되어 있습니다.

    DefaultGame.ini

    [/Script/Plugins.ChunkDownloader PatchingDemoLive]
    +CdnBaseUrls=127.0.0.1/PatchingDemoCDN

2. ChunkDownloader 초기화 및 종료

ChunkDownloader는 FPlatformChunkInstall 인터페이스의 구현이고, 호환되는 여러 인터페이스 중 하나에서 게임이 실행 중인 플랫폼에 따라 각기 다른 모듈을 로드할 수 있습니다. 모든 모듈은 사용 전에 로드 및 초기화해야 하고, 종료 및 정리해야 합니다.

ChunkDownloader에서 이를 수행하는 가장 간단한 방법은 커스텀 GameInstance 클래스를 사용하는 것입니다. GameInstance에는 연결할 수 있는 적절한 초기화 및 종료 함수가 있을 뿐만 아니라 게임이 실행 중인 동안 ChunkDownloader에 대한 지속적인 액세스도 가능합니다. 다음 단계는 이 구현 과정을 안내합니다.

  1. GameInstance 를 베이스 클래스로 사용하여 새 C++ 클래스 를 생성합니다. 이름은 PatchingDemoGameInstance 로 지정합니다.

    CreatePatchingGameInstance.png

    이미지를 클릭하면 확대됩니다.

  2. IDE에서 PatchingDemoGameInstance.h 를 엽니다. public 헤더에서 다음 함수 오버라이드를 추가합니다.

    PatchingDemoGameInstance.h

    public:
    /** Overrides */
        virtual void Init() override;
        virtual void Shutdown() override;

    Init 함수는 게임이 시작될 때 실행되므로 ChunkDownloader를 초기화할 수 있는 최적의 위치가 됩니다. 마찬가지로 Shutdown 함수는 게임이 중지될 때 실행되므로 이를 사용하여 ChunkDownloader 모듈을 종료할 수 있습니다.

  3. PatchingDemoGameInstance.h에서 protected 헤더 아래에 다음 변수 선언을 추가합니다.

    PatchingDemoGameInstance.h

    protected:
    //로컬 매니페스트 파일이 웹사이트에 호스팅된 파일과 같은 최신 업데이트 상태 여부를 트래킹합니다.
    bool bIsDownloadManifestUpToDate;
  4. PatchingDemoGameInstance.cpp 를 엽니다. #include "PatchingDemoGameInstance.h" 에 있는 파일 맨 위에 다음 #include를 추가합니다.

    PatchingDemoGameInstance.cpp

    #include "PatchingDemoGameInstance.h"
    
    #include "ChunkDownloader.h"
    #include "Misc/CoreDelegates.h"
    #include "AssetRegistryModule.h"

    이를 통해 ChunkDownloader와 에셋 및 델리게이트 관리에 유용한 몇 가지 툴에 액세스할 수 있습니다.

  5. protected 헤더의 PatchingDemoGameInstance.h에서 다음 함수를 선언합니다.

    PatchingDemoGameInstance.h

    void OnManifestUpdateComplete(bool bSuccess);
  6. PatchingDemoGameInstance.cpp 에서 OnManifestUpdateComplete 에 대해 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPatchingDemoGameInstance::OnManifestUpdateComplete(bool bSuccess)
    {
        bIsDownloadManifestUpToDate = bSuccess;
    }

    이 함수는 매니페스트 업데이트가 완료될 때 비동기 콜백으로 사용됩니다.

  7. PatchingDemoGameInstance.cppInit 함수에 대해 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPatchingDemoGameInstance::Init()
    {
        Super::Init();
    
        const FString DeploymentName = "PatchingDemoLive";
        const FString ContentBuildId = "PatcherKey";
    
        // 청크 다운로더를 초기화합니다
        TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetOrCreate();
        Downloader->Initialize("Windows", 8);
    
        // 캐싱된 빌드 ID를 로딩합니다
        Downloader->LoadCachedBuild(DeploymentName);
    
        // 빌드 매니페스트 파일을 업데이트합니다
        TFunction<void(bool bSuccess)> UpdateCompleteCallback = [&](bool bSuccess){bIsDownloadManifestUpToDate = bSuccess; };
        Downloader->UpdateBuild(DeploymentName, ContentBuildId, UpdateCompleteCallback);
    }

    이 코드의 역할을 다음과 같이 요약해 보겠습니다.

    1. 함수는 DeploymentNameContentBuildID 를 정의하여 DefaultGame.ini 에서 사용되는 값을 일치시킵니다. 이 값은 현재 테스트를 위해 고정된 값이지만 전체 빌드에서는 HTTP 요청을 사용하여 ContentBuildID 를 가져옵니다. 함수는 이 변수의 정보를 사용하여 웹 사이트에 매니페스트를 요청합니다.

    2. 함수는 FChunkDownloader::GetOrCreate 를 호출하여 ChunkDownloader를 설정하고 이에 대한 레퍼런스를 가져온 뒤, TSharedRef 에 저장합니다. 비슷한 플랫폼 인터페이스에 대한 레퍼런스를 가져오는 데 있어 선호되는 방법입니다.

    3. 함수는 원하는 플랫폼 이름, 이 경우에는 Windows를 사용하여 FChunkDownloader::Initialize 를 호출합니다. 이 예시에서는 TargetDownloadsInFlight에 8 의 값을 제공하고, 이는 ChunkDownloader가 한 번에 처리하는 최대 다운로드 수를 설정합니다.

    4. 함수는 DeploymentName 을 사용하여 FChunkDownloader::LoadCachedBuild 를 호출합니다. 디스크에 이미 다운로드되어 있는 파일이 있는지 확인하고, 이를 통해 ChunkDownloader가 최신 매니페스트로 업데이트된 경우 두 번째 다운로드를 건너뛸 수 있습니다.

    5. 함수는 FChunkDownloader::UpdateBuild 를 호출하여 매니페스트 파일의 업데이트된 버전을 다운로드합니다.

      • 이를 통해 완전히 새로운 실행 파일 없이도 시스템에서 패치 업데이트를 지원합니다.

      • UpdateBuild 는 작업의 성공 여부를 출력하는 콜백과 함께 DeploymentNameContentBuildID 를 가져옵니다.

      • 또한 OnManifestUpdateComplete 를 사용하여 bIsDownloadManifestUpToDate 를 설정하고, GameInstance는 이 패치 페이즈가 완료되는지 전역으로 인식할 수 있습니다.

    이 단계를 따르면 ChunkDownloader가 초기화되고 콘텐츠 다운로드를 시작할 준비가 되며, 다른 함수에 매니페스트의 상태를 알립니다.

  8. UPatchingDemoGameInstance::Shutdown 에 대해 다음 함수 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPatchingDemoGameInstance::Shutdown()
    {
        Super::Shutdown();
    
        // 청크 다운로더를 종료합니다
        FChunkDownloader::Shutdown();
    }

    FChunkDownloader::Shutdown을 호출하면 ChunkDownloader에서 현재 진행 중인 모든 다운로드를 중지하고, 모듈을 정리한 다음 언로드합니다.

3. Pak 파일 다운로드

이제 ChunkDownloader에 대해 적절한 초기화 및 종료 함수가 있으니 .pak 다운로드 함수 기능을 노출시킬 수 있습니다.

  1. PatchingDemoGameInstance.h 에서 GetLoadingProgress 에 대한 다음 함수 선언을 추가합니다.

    PatchingDemoGameInstance.h

    UFUNCTION(BlueprintPure, Category = "Patching|Stats")
    void GetLoadingProgress(int32& FilesDownloaded, int32& TotalFilesToDownload, float& DownloadPercent, int32& ChunksMounted, int32& TotalChunksToMount, float& MountPercent) const;
  1. PatchingDemoGameInstance.cpp 에서 GetLoadingProgress 함수에 대한 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPatchingDemoGameInstance::GetLoadingProgress(int32& BytesDownloaded, int32& TotalBytesToDownload, float& DownloadPercent, int32& ChunksMounted, int32& TotalChunksToMount, float& MountPercent) const
    {
        // 청크 다운로더의 레퍼런스를 구합니다.
        TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
    
        // 로딩 통계 구조체를 구합니다.
        FChunkDownloader::FStats LoadingStats = Downloader->GetLoadingStats();
    
        // 다운로드된 바이트와 다운로드할 바이트를 구합니다.
        BytesDownloaded = LoadingStats.BytesDownloaded;
        TotalBytesToDownload = LoadingStats.TotalBytesToDownload;
    
        // 마운트된 청크와 다운로드할 청크의 수를 구합니다.
        ChunksMounted = LoadingStats.ChunksMounted;
        TotalChunksToMount = LoadingStats.TotalChunksToMount;
    
        // 위의 통계를 사용하여 다운로드 및 마운트 퍼센트를 계산합니다.
        DownloadPercent = (float)BytesDownloaded / (float)TotalBytesToDownload;
        MountPercent = (float)ChunksMounted / (float)TotalChunksToMount;
    }
  2. PatchingDemoGameInstance.h 의 #include 아래에서 다음 다이내믹 멀티캐스트 델리게이트를 추가합니다.

    PatchingDemoGameInstance.h

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPatchCompleteDelegate, bool, Succeeded);

    이 델리게이트는 패치 다운로드 작업이 성공했는지 여부를 알리는 boolean을 출력합니다. 델리게이트는 일반적으로 파일 다운로드 또는 설치와 같은 비동기 작업에 응답하는 데 사용됩니다.

  3. UPatchingDemoGameInstance 클래스에서 public 헤더 아래에 다음 델리게이트 선언을 추가합니다.

    PatchingDemoGameInstance.h

    /** Delegates */
    
    /** Fired when the patching process succeeds or fails */
    UPROPERTY(BlueprintAssignable, Category="Patching");
    FPatchCompleteDelegate OnPatchComplete;

    이를 통해 패치 작업이 완료될 때 블루프린트로 후킹할 수 있는 위치가 제공됩니다.

  4. protected 헤더에서 ChunkDownloadList 에 대한 다음 변수를 추가합니다.

    PatchingDemoGameInstance.h

    /** List of Chunk IDs to try and download */
    UPROPERTY(EditDefaultsOnly, Category="Patching")
    TArray<int32> ChunkDownloadList;

    이 목록을 사용하여 나중에 다운로드할 모든 청크 ID를 보관합니다. 개발 설정에서 필요에 따라 에셋 목록을 사용하여 이를 초기화하지만, 테스트 목적으로 블루프린트 에디터를 사용하여 채울 수 있도록 기본값을 노출시킵니다.

  5. public 헤더에서 PatchGame 에 대한 다음 선언을 추가합니다.

    PatchingDemoGameInstance.h

    /** Starts the game patching process. Returns false if the patching manifest is not up to date. * /
    UFUNCTION(BlueprintCallable, Category = "Patching")
    bool PatchGame();

    이 함수는 패치 프로세스 시작을 위해 블루프린트가 노출된 방법을 제공합니다. 성공 또는 실패 여부를 나타내는 boolean을 반환합니다. 다운로드 관리와 다른 종류의 비동기 작업에 있어 일반적인 패턴입니다.

  6. protected 헤더에서 다음 함수 선언을 추가합니다.

    PatchingDemoGameInstance.h

    /** Called when the chunk download process finishes */
    void OnDownloadComplete(bool bSuccess);
    
    /** Called whenever ChunkDownloader's loading mode is finished*/
    void OnLoadingModeComplete(bool bSuccess);
    
    /** Called when ChunkDownloader finishes mounting chunks */
    void OnMountComplete(bool bSuccess);

    이를 사용하여 다운로드 프로세스에서 비동기 콜백에 응답합니다.

  7. PatchingDemoGameInstance.cpp 에서 OnDownloadCompleteOnLoadingModeBegin 에 대한 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPGameInstance::OnLoadingModeComplete(bool bSuccess)
    {
        OnDownloadComplete(bSuccess);
    }
    
    void OnMountComplete(bool bSuccess)
    {
        OnPatchComplete.Broadcast(bSuccess);
    }

    OnLoadingModeComplete은 OnDownloadComplete으로 패스스루하고, 이후 단계에서 청크를 마운트합니다. OnMountComplete은 모든 청크의 마운팅이 완료되고, 콘텐츠를 사용할 준비가 되었음을 나타냅니다.

  8. PatchingDemoGameInstance.cpp 에서 PatchGame 에 대한 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    bool UPGameInstance::PatchGame()
    {
        // 다운로드 매니페스트를 최신 상태로 유지합니다.
        if (bIsDownloadManifestUpToDate)
        {
            // 청크 다운로더를 가져옵니다.
            TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
    
            // 현재 청크 상태를 제보합니다.
            for (int32 ChunkID : ChunkDownloadList)
            {
                int32 ChunkStatus = static_cast<int32>(Downloader->GetChunkStatus(ChunkID));
                UE_LOG(LogTemp, Display, TEXT("Chunk %i status: %i"), ChunkID, ChunkStatus);
            }
    
            TFunction<void (bool bSuccess)> DownloadCompleteCallback = [&](bool bSuccess){OnDownloadComplete(bSuccess);};
            Downloader->DownloadChunks(ChunkDownloadList, DownloadCompleteCallback, 1);
    
            // 로딩 모드를 시작합니다.
            TFunction<void (bool bSuccess)> LoadingModeCompleteCallback = [&](bool bSuccess){OnLoadingModeComplete(bSuccess);};
            Downloader->BeginLoadingMode(LoadingModeCompleteCallback);
            return true;
        }
    
        // 매니페스트를 검증하기 위해 서버와 연락하는데 실패하여 패치할 수 없었습니다.
        UE_LOG(LogTemp, Display, TEXT("Manifest Update Failed. Can't patch the game"));
    
        return false;
    
    }

    이 함수는 다음 단계를 거칩니다.

    1. 먼저 매니페스트가 최신 상태인지 확인합니다. ChunkDownloader를 초기화하지 않고 성공적으로 새 매니페스트 복사본을 가져온 경우 bIsDownloadManifestUpToDate 는 false가 되고, 이 함수는 false를 반환하여 패치 시작에 실패했음을 나타냅니다.

    2. 다음으로 패치 프로세스가 계속될 수 있다면 함수는 ChunkDownloader에 대한 레퍼런스를 가져옵니다. 이후 다운로드 목록을 반복하고 각 청크의 상태를 확인합니다.

    3. 2개의 콜백이 정의됩니다.

      • DownloadCompleteCallback 은 각 개별 청크 다운로드가 완료될 때 호출되고, 각각에서 다운로드가 성공 또는 실패할 때 메시지를 출력합니다.

      • LoadingModeCompleteCallback 은 모든 청크가 다운로드되면 발생합니다.

    4. 함수는 FChunkDownloader::DownloadChunks 를 호출하여 원하는 청크 다운로드를 시작합니다. 청크는 ChunkDownloadList 에 나열되어 있습니다. 이 목록은 함수 호출 전에 원하는 청크 ID로 채워져 있어야 합니다. 또한 DownloadCompleteCallback 을 전달합니다.

    5. 함수는 앞서 정의한 콜백과 함께 FChunkDownloader::BeginLoadingMode 를 호출합니다.

      • Loading Mode는 ChunkDownloader에 다운로드 상태 모니터링을 시작하도록 지시합니다.

      • 청크는 Loading Mode 호출 없이 백그라운드에서 수동으로 다운로드할 수 있지만 이를 사용하면 다운로드 통계가 출력되어 사용자가 다운로드 진행 상황을 추적할 수 있는 UI를 만들 수 있습니다.

      • 콜백 함수를 사용하면 전체 청크 배치가 다운로드될 때 특정 함수 기능을 실행할 수도 있습니다.

  9. PatchingDemoGameInstance.cpp 에서 OnDownloadComplete 에 대한 다음 구현을 생성합니다.

    PatchingDemoGameInstance.cpp

    void UPatchingDemoGameInstance::OnDownloadComplete(bool bSuccess)
    {
    if (bSuccess)
        {
            UE_LOG(LogTemp, Display, TEXT("Download complete"));
    
            // 청크 다운로더를 가져옵니다.
            TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
    
            FJsonSerializableArrayInt DownloadedChunks;
    
            for (int32 ChunkID : ChunkDownloadList)
            {
                DownloadedChunks.Add(ChunkID);
            }
    
            // 청크를 마운트합니다.
            TFunction<void(bool bSuccess)> MountCompleteCallback = [&](bool bSuccess){OnMountComplete(bSuccess);};
            Downloader->MountChunks(DownloadedChunks, MountCompleteCallback);
    
            OnPatchComplete.Broadcast(true);
    
        }
        else
        {
    
            UE_LOG(LogTemp, Display, TEXT("Load process failed"));
    
            // 델리게이트를 호출합니다.
            OnPatchComplete.Broadcast(false);
        }
    }

    또 다른 복잡한 함수이므로 역할별로 나눠서 살펴보겠습니다. .pak 파일이 사용자 디바이스에 성공적으로 다운로드되면 실행됩니다.

    1. 먼저 ChunkDownloader에 대한 레퍼런스를 가져옵니다.

    2. 다음으로 함수는 Json 어레이를 설정하고 ChunkDownloadList 의 정보로 채웁니다. 이 정보는 요청을 수행할 때 사용됩니다.

    3. 함수는 MountCompleteCallback 을 사용하여 패치가 성공적으로 적용되었는지 여부를 출력합니다.

    4. 함수는 Json 목록과 MountCompleteCallback 을 사용하여 ChunkDownloader::MountChunks 를 호출하고 다운로드한 청크의 마운트를 시작합니다.

    5. 다운로드가 성공한 경우 함수는 OnPatchComplete 델리게이트를 true 값으로 활성화합니다. 성공하지 않은 경우 false 값으로 활성화합니다. UE_LOG 는 실패 지점에 따라 오류 메시지를 출력합니다.

4. Patching Game Mode 설정

패치 프로세스를 초기화하기 위해 레벨과 게임 모드를 만들어 PatchGame을 호출하고 화면에 패치 통계를 출력합니다.

  1. 언리얼 에디터에서 콘텐츠 브라우저 에 새로운 Blueprints 폴더를 생성합니다. 그런 다음 PatchingDemoGameInstance 를 베이스 클래스로 사용하여 새 블루프린트 를 생성합니다.

    PatchingGameInstanceBlueprint.png

    이미지를 클릭하면 확대됩니다.

    새 블루프린트 클래스 이름을 CDGameInstance 로 지정합니다.

    CDGameInstance.png

    이미지를 클릭하면 확대됩니다.

    세팅을 편집하고 청크 다운로드 프로세스를 추적하기 위해 더욱 액세스하기 쉬운 방법으로 이 블루프린트를 사용합니다.

  2. PatchingGameMode 라는 새 게임 모드 블루프린트를 생성합니다.

    PatchingGameMode.png

  3. Maps 폴더를 생성하고 이름이 PatchingDemoEntryPatchingDemoTest 인 2개의 새 레벨을 생성합니다. Entry 레벨은 빈 맵을 기반으로 해야 하며, Test 레벨은 Default 맵을 기반으로 해야 합니다.

    PatchingMaps.png

    이미지를 클릭하면 확대됩니다.

  4. PatchingDemoEntry 에 대한 월드 세팅(World Settings) 에서 게임모드 오버라이드(GameMode Override)PatchingGameMode 로 설정합니다.

    GameModeOverride.png

    이미지를 클릭하면 확대됩니다.

  5. 프로젝트 세팅(Project Settings) 을 열고 프로젝트(Project) > 맵 & 모드(Maps & Modes) 로 이동합니다. 다음 파라미터를 설정합니다.

    MapsAndModesParams.png

    이미지를 클릭하면 확대됩니다.

    ID

    파라미터

    1

    게임 인스턴스 클래스

    CDGameInstance

    2

    에디터 시작 맵

    PatchingDemoTest

    3

    게임 기본 맵

    PatchingDemoEntry

  6. 블루프린트 에디터 에서 CDGameInstance 를 엽니다. 디폴트(Defaults) 패널에서 청크 다운로드 목록(Chunk Download List) 에 3개의 항목을 추가합니다. 항목에 1001, 1002 및 1003의 값을 제공합니다. 3개의 .pak 파일에 있는 청크 ID입니다.

    ChunkDownloadList.png

  7. 블루프린트 에디터 에서 PatchingGameMode 를 열고 EventGraph 로 이동합니다.

  8. Get Game Instance 노드를 생성하고 CDGameInstance 로 캐스트합니다.

  9. As CDGameInstance 를 클릭하여 드래그한 다음 변수로 승격(Promote to Variable) 을 클릭하여 게임 인스턴스에 대한 레퍼런스를 생성합니다. 새 변수인 Current Game Instance 를 호출합니다.

  10. Set Current Game Instance 의 출력 핀에서 클릭하고 드래그한 다음 Patch Game 에 대한 호출을 생성합니다.

  11. Patch Game값 반환(Return Value) 을 클릭하고 드래그한 다음 변수로 승격(Promote to Variable) 을 클릭하여 값을 저장할 boolean을 생성합니다. 새 변수 이름을 Is Patching In Progress 로 지정합니다.

    PatchGame.png

    이미지를 클릭하면 확대됩니다.

  12. Get Current Game Instance 노드를 생성하고, 출력 핀에서 클릭하고 드래그한 다음 Get Patch Status 에 대한 호출을 생성합니다.

  13. Get Patch Status값 반환 핀을 클릭하고 드래그한 다음 Break PatchStats 노드를 생성합니다.

  14. Tick 이벤트에서 클릭하고 드래그한 다음 새 Branch 노드를 생성합니다. Is Patching In ProgressCondition 입력에 연결합니다.

  15. Branch 노드의 True 핀을 클릭하고 드래그한 다음 Print String 노드를 생성합니다. BuildString (float) 을 사용하여 Break PatchStats 노드에서 Download Percent 를 출력합니다. Mount Percent 에 대해 이 단계를 반복합니다.

    DisplayPatchStatus.png

    이미지를 클릭하면 확대됩니다.

  16. Print String 노드에서 Branch 노드를 생성하고, AND 노드를 생성하고 Condition 핀에 연결합니다.

  17. Greater Than or Equal To 노드를 생성하여 Download Percent1.0 이상인지 확인하고 Mount Percent 에 대해 똑같은 작업을 반복합니다. 모두 AND 노드에 연결합니다. 두 가지 조건이 모두 true인 경우 레벨 열기(Open Level) 를 사용하여 PatchingGameTest 레벨을 엽니다.

    OpenLevelWhenDone.png

    이미지를 클릭하면 확대됩니다.

게임이 실행되면 Entry 맵이 열리고, ChunkDownloader가 실행되며, 청크 다운로드(Chunk Download) 목록에 청크 다운로드 및 마운트 진행 상황이 표시됩니다. 다운로드가 완료되면 테스트 맵으로 전환됩니다.

에디터에서 플레이(Play In Editor) 를 사용하여 실행하려는 경우 다운로드가 시작되지 않습니다. 패키징된 빌드를 사용하여 ChunkDownloader를 테스트해야 합니다.

5. 다운로드한 콘텐츠 표시

캐릭터 메시를 표시하기 위해 이에 대한 레퍼런스를 가져와야 합니다. 사용하기 전에 에셋이 로드되는지 확인해야 하므로 소프트 오브젝트 레퍼런스 가 필요합니다. 이 섹션은 액터를 스폰하고 소프트 레퍼런스에서 스켈레탈 메시를 채우는 방법에 관한 간단한 예시를 안내합니다.

  1. PatchingDemoTest 레벨을 열고 레벨 블루프린트(Level Blueprint) 를 엽니다.

  2. Meshes 라는 새 변수를 생성합니다.

    • 변수 타입(Variable Type) 에서 스켈레탈 메시(Skeletal Mesh) 를 선택합니다.

    • 타입 목록의 엔트리로 마우스를 가져가고 소프트 오브젝트 레퍼런스(Soft Object Reference) 를 선택합니다. 변수의 컬러가 파란색에서 연한 녹색으로 변경됩니다.

    SkeletalMeshSoftObjectRef.png

    이미지를 클릭하면 확대됩니다.

    소프트 오브젝트 레퍼런스는 모호한 에셋을 안전하게 참조할 수 있는 스마트 포인터의 유형입니다. 이를 사용하여 메시 에셋이 로드되고 사용 전에 사용가능 여부를 확인할 수 있습니다.

  3. 메시(Meshes)변수 타입(Variable Type) 옆에 있는 아이콘을 클릭하고 어레이(Array) 로 변경합니다. 블루프린트를 컴파일하여 변경 사항을 적용합니다.

    SoftObjectArray.png

    이미지를 클릭하면 확대됩니다.

  4. 메시(Meshes)기본값(Default Value) 에서 3개의 항목을 추가하고 Boris, CrunchKhaimera 에 대한 스켈레탈 메시를 선택합니다.

    SoftObjectDefaultValues.png

    이미지를 클릭하면 확대됩니다.

  5. EventGraph 의 레벨에서 BeginPlay 이벤트에서 클릭하고 드래그한 다음 For Each Loop 를 생성하고 Meshes 어레이에 연결합니다.

  6. For Each Loop배열 요소(Array Element) 핀을 클릭하고 드래그한 다음 Is Valid Soft Object Reference 노드를 생성합니다. Loop Body 에서 Branch 를 생성하고 값 반환(Return Value) 에 연결합니다.

  7. Spawn Actor From Class 노드를 생성하고 Branch 노드의 True 핀에 연결합니다. 클래스(Class) 에서 스켈레탈 메시 액터(Skeletal Mesh Actor) 를 선택합니다.

  8. For Each Loop배열 인덱스(Array Index) 에서 클릭하고 드래그한 다음 Integer x Float 노드를 생성합니다. Float 값을 192.0 으로 설정합니다.

  9. Integer x Float 노드의 값 반환(Return Value) 에서 클릭하고 드래그하여 Vector x Float 노드를 생성하고 (1.0, 0.0, 0.0) 의 값을 지정합니다.

    • 이를 통해 For Each Loop를 거칠 때마다 원점에서 192개 유닛만큼 떨어진 좌표를 만듭니다. 따라서 스폰할 때 각 메시에 어느 정도의 공간이 제공됩니다.

  10. 이전 단계에서의 벡터를 Make Transform 노드의 위치(Location) 로 사용하고, Spawn Actor 노드의 Spawn Transform값 반환(Return Value) 을 연결합니다.

  11. Spawn Actor 노드의 값 반환(Return Value) 에서 클릭하고 드래그한 다음 스켈레탈 메시 컴포넌트(Skeletal Mesh Component) 에 대한 레퍼런스를 가져옵니다. 이를 사용하여 Set Skeletal Mesh 를 호출합니다.

  12. Array Element 노드에서 클릭하고 드래그한 다음 Resolve Soft Object Reference 노드를 생성합니다. Set Skeletal Mesh 에 대한 New Mesh 입력 핀에 이 노드의 출력을 연결합니다.

    SpawnSoftObjectGraph.png

    이미지를 클릭하면 확대됩니다.

  13. 레벨 내에 있는 플레이어 스타트(Player Start)(-450, 0.0, 112.0) 으로 이동합니다.

    MovePlayerSpawn.png

    이미지를 클릭하면 확대됩니다.

  14. 진행 상황을 저장하고 블루프린트를 컴파일합니다.

레벨이 로드되면 각 캐릭터의 스켈레탈 메시가 스폰됩니다. 소프트 오브젝트 레퍼런스가 작동하지 않는 경우 각 캐릭터에 대한 청크가 아직 마운트되지 않았다는 뜻입니다.. 따라서 에셋을 사용할 수 없으며, 캐릭터가 스폰되지 않습니다.

CharactersSpawned.png

.pak 파일 내부에 포함된 에셋을 참조할 때 항상 표준, 하드 레퍼런스 대신 소프트 오브젝트 레퍼런스를 사용해야 합니다. 하드 레퍼런스를 사용하는 경우 청크 스키마가 중단됩니다.

6. 게임 테스트

마지막으로 독립형 빌드에서 프로젝트를 테스트해야 합니다. pak 마운트는 PIE 모드에서 작동하지 않으므로 패치 기능을 테스트하는 데 있어 필수 단계입니다.

  1. 프로젝트를 패키징합니다.

  2. .pak 파일 및 매니페스트를 IIS 테스트 웹 사이트 에 있는 해당 폴더에 복사합니다.

  3. IIS 프로세스와 웹 사이트 모두가 실행 중이어야 합니다.

  4. 패키징된 실행파일을 실행합니다.

최종 결과물

화면 왼쪽 상단에 패치 출력이 있는 검은색 화면이 표시되고, 패치와 마운트 상태가 100%에 도달하면 게임이 디폴트 맵에 로드되고 Boris, Crunch 및 Khaimera가 표시될 것입니다. 패치 또는 마운트 프로세스에 문제가 있는 경우 어느 것도 표시되지 않습니다.

직접 해보기

여기에서 몇 가지 단계를 수행하여 청크 다운로드 스키마를 더욱 구체적으로 만들 수 있습니다.

  • 로딩 모드 중 표시되는 UI를 빌드하고 플레이어에게 진행률 표시줄과 프롬프트를 표시합니다.

  • 타임아웃 및 설치 실패와 같은 오류에 대한 UI 프롬프트를 빌드합니다.

  • PrimaryAssetLabel의 커스텀 서브클래스를 생성하여 에셋에 관한 추가 메타데이터를 포함합니다. 예를 들어 Battle Breakers의 커스텀 PrimaryAssetLabel 클래스에는 현재 청크 사용을 위한 전제 조건으로 로드되어야 하는 Parent Chunk가 포함되어 있습니다.