UE4官網針對熱更新有較為詳細的步驟,但是其中漏掉的幾個問題導緻實現不了效果。
總的來說可以分為四大步。
第一步:自己的項目設置好插件ChunkDownloader
1.新建第三人稱c 項目工程,設置分塊打包
2.勾選插件ChunkDownloader
3.修改項目的 Build.cs 文件
PrivateDependencyModuleNames.AddRange(
new string[]
{
"ChunkDownloader"
}
4.保存後重新生成項目文件。操作:右鍵點擊你的 .uproject 文件,然後點擊 生成項目文件(Generate Project Files)。
第二步:資源分塊,然後打包。為了測試方便,新建三個map,分别放在不同的文件夾。我的是Test,Test2,Test3.
标簽設置如圖:紅框内需必備
屬性解釋:
依次對自己的測試資源添加标簽。
2.打包默認設置即可。這樣你可以在打出的包裡看到分好的pak文件。紅框ID與你的标簽ID對應。
第三步:構建資源清單與托管本地測試服務器
1.構建資源清單。這裡注意,英文字符下 TAB建進行屬性空格。
我的清單如下:
資源文件夾結構,其中Windows文件夾中放的是分塊後的pak文件
稍作解釋:
第1行是需要下載更新的pak數目
第2行理解為資源和清單所在的文件夾
後面的幾行就是pak資源相關的。共5個屬性。資源名字,資源大小(右鍵資源看其屬性字節數),版本号,ChunkID,資源所在位置。
2.文件托管到本地測試服務器
如何創建本地測試服務器這裡不啰嗦,可以參考ue4官網。
但是這裡要注意:新建文件夾PatchingDemoCDN,除了上傳我們前面準備好的資源和清單即PatchingDemoKey文件夾。我們還需要在PatchingDemoCDN文件夾中新建表單
這個表單中我們要寫上$BUILD_ID對應的也就是資源和清單所在的文件夾PatchingDemoKey
最後我們還需要在項目的配置表DefaultGame.ini中添加用來下載資源的網站地址。這裡我們是用本地服務器測試的。
[/Script/Plugins.ChunkDownloader PatchingDemoLive]
CdnBaseUrls=127.0.0.1/PatchingDemoCDN
第三步:編輯代碼和邏輯。(開始不必擴展,實現基本功能即可)
1.GameInstance不僅具有可以綁定的相應初始化和關閉函數,而且還可以在遊戲運行時持續訪問ChunkDownloader。
所以使用 GameInstance 作為基類創建 新C 類。将其命名為 PatchingDemoGameInstance。
最後代碼為:
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "PatchingDemoGameInstance.generated.h"
/**
*
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPatchCompleteDelegate, bool, Succeeded);
UCLASS()
class UPatchingDemoGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
/** Overrides */
virtual void Init() override;
virtual void Shutdown() override;
/** Delegates */
/** Fired when the patching process succeeds or fails */
UPROPERTY(BlueprintAssignable, Category = "Patching")
FPatchCompleteDelegate OnPatchComplete;
/** Starts the game patching process. Returns false if the patching manifest is not up to date. */
UFUNCTION(BlueprintCallable, Category = "Patching")
bool PatchGame();
UFUNCTION(BlueprintPure, Category = "Patching|Stats")
void GetLoadingProgress(int32& FilesDownloaded, int32& TotalFilesToDownload, float& DownloadPercent, int32& ChunksMounted, int32& TotalChunksToMount, float& MountPercent) const;
protected:
//Tracks Whether or not our local manifest file is up to date with the one hosted on our website
bool bIsDownloadManifestUpToDate;
void OnManifestUpdateComplete(bool bSuccess);
/** List of Chunk IDs to try and download */
UPROPERTY(EditDefaultsOnly, Category = "Patching")
TArray<int32> ChunkDownloadList;
/** 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);
};
.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PatchingDemoGameInstance.h"
#include "ChunkDownloader.h"
#include "Misc/CoreDelegates.h"
#include "AssetRegistryModule.h"
void UPatchingDemoGameInstance::Init()
{
Super::Init();
const FString DeploymentName = "PatchingDemoLive";
const FString ContentBuildId = "PatchingDemoKey";
// initialize the chunk downloader
TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetOrCreate();
// TODO 安卓下載方式
// Downloader->Initialize("Android", 8);
Downloader->Initialize("Windows", 8);
// load the cached build ID
Downloader->LoadCachedBuild(DeploymentName);
// update the build manifest file
TFunction<void(bool bSuccess)> UpdateCompleteCallback = [&](bool bSuccess){bIsDownloadManifestUpToDate = true;};
Downloader->UpdateBuild(DeploymentName, ContentBuildId, UpdateCompleteCallback);
}
void UPatchingDemoGameInstance::Shutdown()
{
Super::Shutdown();
// Shut down ChunkDownloader
FChunkDownloader::Shutdown();
}
void UPatchingDemoGameInstance::OnManifestUpdateComplete(bool bSuccess)
{
bIsDownloadManifestUpToDate = bSuccess;
}
void UPatchingDemoGameInstance::GetLoadingProgress(int32& BytesDownloaded, int32& TotalBytesToDownload, float& DownloadPercent, int32& ChunksMounted, int32& TotalChunksToMount, float& MountPercent) const
{
//Get a reference to ChunkDownloader
TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
//Get the loading stats struct
FChunkDownloader::FStats LoadingStats = Downloader->GetLoadingStats();
//Get the bytes downloaded and bytes to download
BytesDownloaded = LoadingStats.BytesDownloaded;
TotalBytesToDownload = LoadingStats.TotalBytesToDownload;
//Get the number of chunks mounted and chunks to download
ChunksMounted = LoadingStats.ChunksMounted;
TotalChunksToMount = LoadingStats.TotalChunksToMount;
//Calculate the download and mount percent using the above stats
DownloadPercent = ((float)BytesDownloaded / (float)TotalBytesToDownload)*100.0f;
MountPercent = ((float)ChunksMounted / (float)TotalChunksToMount)*100.0f;
}
void UPatchingDemoGameInstance::OnLoadingModeComplete(bool bSuccess)
{
OnDownloadComplete(bSuccess);
}
void UPatchingDemoGameInstance::OnMountComplete(bool bSuccess)
{
OnPatchComplete.Broadcast(bSuccess);
}
bool UPatchingDemoGameInstance::PatchGame()
{
// make sure the download manifest is up to date
if (bIsDownloadManifestUpToDate)
{
// get the chunk downloader
TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
// report current chunk status
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);
// start loading mode
TFunction<void(bool bSuccess)> LoadingModeCompleteCallback = [&](bool bSuccess) {OnLoadingModeComplete(bSuccess); };
Downloader->BeginLoadingMode(LoadingModeCompleteCallback);
return true;
}
// we couldn't contact the server to validate our manifest, so we can't patch
UE_LOG(LogTemp, Display, TEXT("Manifest Update Failed. Can't patch the game"));
return false;
}
void UPatchingDemoGameInstance::OnDownloadComplete(bool bSuccess)
{
if (bSuccess)
{
UE_LOG(LogTemp, Display, TEXT("Download complete"));
// get the chunk downloader
TSharedRef<FChunkDownloader> Downloader = FChunkDownloader::GetChecked();
FJsonSerializableArrayInt DownloadedChunks;
for (int32 ChunkID : ChunkDownloadList)
{
DownloadedChunks.Add(ChunkID);
}
//Mount the chunks
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"));
// call the delegate
OnPatchComplete.Broadcast(false);
}
}
2.使用 PatchingDemoGameInstance 作為基類創建 新藍圖,命名為 CDGameInstance
3.打開CDGameInstance,添加chunk列表
4.創建名為 PatchingGameMode 的新遊戲模式 藍圖
5.項目做如下設置。
6.第三人稱遊戲map設置gamemode為PatchingGameMode
7.打開PatchingGameMode,添加如下藍圖邏輯。
beginPlay後添加:patchGame返回值為true則進入tick即開始下載資源。否則根據資源數進行進一步檢測。
tick後添加:大概意思就是資源下載并且裝載完打開Test3地圖。
到此代碼,邏輯都寫好了。
第四步:打包測試
1.直接打包。
結果是包文件夾裡的Content中的paks文件夾有四個pak文件。删掉ID為1001-1003的pak。
2.運行.exe
結果如下:
下載中
下載完成,3秒後進入Test3地圖。
下載的文件:
,
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!