参考文章
文章出处:http://blog.csdn.net/u012385432
最近发现有些网站复制了我的文章却没有注明出处,表示鄙视...转载可以,还请注明出处...不要剽窃别人的劳动成果...
前面的几篇文章中提及了有关.Pak文件和文件下载的部分,这两部分组合起来,其实就是我们的资源热更新了.当然代码的热更新不在这个讨论范围内.代码的热更新的话就更加麻烦了.这次讨论的只限资源的热更新...
前面文章链接:
1.下载文件链接
我们在做项目的时候,通常会有这样的需求,比如说美术新出了几个模型,需要替换原有工程中的模型.但是包已经发出去了,用户已经安装.这个时候又不可能因为一点东西让用户重新下载整个软件.或者说包体发出来的时候太大,老板要求在下载好主体的情况下,在进入软件的时候才去下载要用到的资源.这几种情况通常就是我们说的资源热更新了.在Unity里面,我们用AssetBunlde来做资源的热更新.不得不说UNITY对热更新的支持还是不错的.UNITY5.0版本以后,AssetBundle会自己生成Manifest.xml文件.不再需要用户去维护复杂的Dependency关系了.但是用UE4做的话就没那么舒服了...就像UNITY一样...在UE中,我们使用PAK来进行热更新.所以一般来说我们的项目发布的时候是没有那些UASSET资源的.加载uasset其实就是加载的pak文件中的uasset...
那么UE4的资源热更新主要有这么几步
1.打包好.Pak文件并将其放置到服务器上,其中需要一份数据文件Version.txt(json,xml或其他的格式都可以)来表示当前的版本信息.文件的内容我目前设计的很简单.主要就只有两个内容,一个是文件的MD5值,另一个是这次打包.Pak文件一共打包了什么Pak文件进去.
2.本地也需要一份Version.txt,用来比对服务器Version和本地的Version的区别,区别的标识就是文件的MD5值.如果发现MD5值不同的话就把服务器上的Version.txt中所涉及到的所有资源都下载下来并覆盖本地的资源,同时将服务器的Version.txt覆盖本地的Version.避免没必要的重复下载.
一.工具的准备 ---- .pak文件批量打包工具
首先,很蛋疼的是似乎在UE里面没办法通过C++来调用UnrealPak.exe工具,所以我用C#写了一个批量打包pak文件的工具.可以一次将多个uasset文件打包成一个.pak文件.或者将多个uasset文件打包成多个.pak文件.同时,在生成pak文件的时候还会生成版本文件.
页面效果如下:
打包出来的东西:
Version.txt内容:
实现这个工具其实非常的简单.几十行代码就可以了.接下来看一下代码...其中选中引擎根目录,选中要打包的文件等代码就不贴了.篇幅太大.以下是打包的具体代码
- private void Btn_MultipleBuild_Click(object sender, EventArgs e)
- {
- Btn_MultipleBuild.Enabled = false;
- Btn_MultipleBuild.Text = "正在打包...";
- //sb,sw,textWirter均是为了生成Json字符串而使用的
- StringBuilder sb = new StringBuilder();
- StringWriter sw = new StringWriter(sb);
- JsonTextWriter textWriter = new JsonTextWriter(sw);
- textWriter.Formatting = Formatting.Indented;
- DateTime Today = DateTime.UtcNow;
- int second = Today.Second;
- //生成文件的MD5值
- string fileMD5 = StrToMD5(second.ToString());
- textWriter.WriteStartObject();
- textWriter.WritePropertyName("FileVersion");
- textWriter.WriteStartObject();
- textWriter.WritePropertyName("MD5");
- textWriter.WriteValue(fileMD5);
- textWriter.WriteEndObject();
- // 检查选中的引擎根目录,其目录下是否包含有UnralPak.exe文件
- if (!File.Exists(TextBox_MultipleEnginePath.Text + @"\Engine\Binaries\Win64\UnrealPak.exe"))
- {
- MessageBox.Show("打包失败,没有找到 UnrealPak.exe,引擎路径不存在!");
- Btn_MultipleBuild.Enabled = true;
- Btn_MultipleBuild.Text = "打包";
- return;
- }
- textWriter.WritePropertyName("Files");
- textWriter.WriteStartArray();
- //根据多选框选中的文件来对文件进行打包
- string[] assetNameArray = TextBox_MultipleUassetPath.Text.Split(' ');
- for (int i = 0; i < assetNameArray.Length; i++)
- {
- string assetFullName = assetNameArray[i].Replace('\\','/');
- string[] assetArray = assetFullName.Split('/');
- string assetName = assetArray[assetArray.Length - 1].Replace(".uasset", "");
- string assetMD5 = StrToMD5(assetName + second.ToString());
- string outPath = TextBox_MultipleOutPath.Text + "\\" + assetName + ".pak";
- //通过Process相关类来多次调用UnrealPak.exe程序来打包
- ProcessStartInfo info = new ProcessStartInfo();
- info.FileName = TextBox_MultipleEnginePath.Text + @"\Engine\Binaries\Win64\UnrealPak.exe";
- info.Arguments = @outPath + @" " + @assetFullName;
- info.WindowStyle = ProcessWindowStyle.Minimized;
- Process process = Process.Start(info);
- process.WaitForExit();
- //将文件的信息写入到Json文件中
- textWriter.WriteStartObject();
- textWriter.WritePropertyName("FileName");
- textWriter.WriteValue(assetName);
- textWriter.WritePropertyName("MD5");
- textWriter.WriteValue(assetMD5);
- textWriter.WriteEndObject();
- }
- MessageBox.Show("生成pak完毕!");
- textWriter.WriteEndArray();
- textWriter.WriteEndObject();
- Btn_MultipleBuild.Text = "打包";
- Btn_MultipleBuild.Enabled = true;
- string saveData =
- TextBox_MultipleEnginePath.Text + ";" +
- TextBox_MultipleUassetPath.Text + ";" +
- TextBox_MultipleOutPath.Text;
- File.WriteAllText(Environment.CurrentDirectory + "/save.txt", saveData);
- //生成Version.txt文件
- File.WriteAllText(TextBox_MultipleOutPath.Text + "/Version.txt",sb.ToString());
- }
private void Btn_MultipleBuild_Click(object sender, EventArgs e)
{
Btn_MultipleBuild.Enabled = false;
Btn_MultipleBuild.Text = "正在打包...";
//sb,sw,textWirter均是为了生成Json字符串而使用的
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
JsonTextWriter textWriter = new JsonTextWriter(sw);
textWriter.Formatting = Formatting.Indented;
DateTime Today = DateTime.UtcNow;
int second = Today.Second;
//生成文件的MD5值
string fileMD5 = StrToMD5(second.ToString());
textWriter.WriteStartObject();
textWriter.WritePropertyName("FileVersion");
textWriter.WriteStartObject();
textWriter.WritePropertyName("MD5");
textWriter.WriteValue(fileMD5);
textWriter.WriteEndObject();
// 检查选中的引擎根目录,其目录下是否包含有UnralPak.exe文件
if (!File.Exists(TextBox_MultipleEnginePath.Text + @"\Engine\Binaries\Win64\UnrealPak.exe"))
{
MessageBox.Show("打包失败,没有找到 UnrealPak.exe,引擎路径不存在!");
Btn_MultipleBuild.Enabled = true;
Btn_MultipleBuild.Text = "打包";
return;
}
textWriter.WritePropertyName("Files");
textWriter.WriteStartArray();
//根据多选框选中的文件来对文件进行打包
string[] assetNameArray = TextBox_MultipleUassetPath.Text.Split(' ');
for (int i = 0; i < assetNameArray.Length; i++)
{
string assetFullName = assetNameArray[i].Replace('\\','/');
string[] assetArray = assetFullName.Split('/');
string assetName = assetArray[assetArray.Length - 1].Replace(".uasset", "");
string assetMD5 = StrToMD5(assetName + second.ToString());
string outPath = TextBox_MultipleOutPath.Text + "\\" + assetName + ".pak";
//通过Process相关类来多次调用UnrealPak.exe程序来打包
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = TextBox_MultipleEnginePath.Text + @"\Engine\Binaries\Win64\UnrealPak.exe";
info.Arguments = @outPath + @" " + @assetFullName;
info.WindowStyle = ProcessWindowStyle.Minimized;
Process process = Process.Start(info);
process.WaitForExit();
//将文件的信息写入到Json文件中
textWriter.WriteStartObject();
textWriter.WritePropertyName("FileName");
textWriter.WriteValue(assetName);
textWriter.WritePropertyName("MD5");
textWriter.WriteValue(assetMD5);
textWriter.WriteEndObject();
}
MessageBox.Show("生成pak完毕!");
textWriter.WriteEndArray();
textWriter.WriteEndObject();
Btn_MultipleBuild.Text = "打包";
Btn_MultipleBuild.Enabled = true;
string saveData =
TextBox_MultipleEnginePath.Text + ";" +
TextBox_MultipleUassetPath.Text + ";" +
TextBox_MultipleOutPath.Text;
File.WriteAllText(Environment.CurrentDirectory + "/save.txt", saveData);
//生成Version.txt文件
File.WriteAllText(TextBox_MultipleOutPath.Text + "/Version.txt",sb.ToString());
}
代码的话实在是太简单了..以至于没什么好说的.其中生成MD5值的代码是这样的: - <span style="white-space:pre"> </span>public string StrToMD5(string str)
- {
- byte[] data = Encoding.GetEncoding("GB2312").GetBytes(str);
- MD5 md5 = new MD5CryptoServiceProvider();
- byte[] OutBytes = md5.ComputeHash(data);
- string OutString = "";
- for (int i = 0; i < OutBytes.Length; i++)
- {
- OutString += OutBytes[i].ToString("x2");
- }
- return OutString.ToLower();
- }
<span style="white-space:pre"> </span>public string StrToMD5(string str)
{
byte[] data = Encoding.GetEncoding("GB2312").GetBytes(str);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] OutBytes = md5.ComputeHash(data);
string OutString = "";
for (int i = 0; i < OutBytes.Length; i++)
{
OutString += OutBytes[i].ToString("x2");
}
return OutString.ToLower();
}
那么既然已经能批量打包pak文件并生成Version.txt了.那么我们就把生成出来的这堆东西都丢到服务器上去就可以了.第一步完成.下面在UE4中完成第二部.资源的更新
二. 资源更新
1.首先我们在代码里面增加一个状态叫做GameUpdateResourcesState,资源更新状态,用来处理资源的更新
GameUpdateResourcesState.h:
- // Fill out your copyright notice in the Description page of Project Settings.
- #include "GameBaseState.h"
- #include "IDHManagers/HttpLoader.h"
- #pragma once
- //class AHttpLoader;
- class IDHOME_API GameUpdateResourcesState :public GameBaseState
- {
- private:
- struct FileMessage
- {
- FString FileName;
- FString FileMD5;
- };
- public:
- GameUpdateResourcesState();
- ~GameUpdateResourcesState();
- void OnEnter(TArray<void*> Params) override;
- void OnExit() override;
- private:
- void GetServerResoucesVersionFile();
- void CompareServerAndLocalVersion(bool bSuccess, FHttpResponsePtr Response);
- const FString LocalVersionFileLocation = FPaths::GameContentDir() + TEXT("Data/Version.txt");
- const FString ServerPakDirectory = TEXT("http://localhost:80/icons/Data/");
- const FString SavePakDirectory = FPaths::GameContentDir() + TEXT("DownLoadPaks/");
- bool GetVersionMessageFromString(FString JsonString, FString& VersionFileMD5, TArray<FileMessage>& FileMessages);
- void UpdateResources(const TArray<FileMessage>& Files);
- void DownloadFileComplete(FHttpResponsePtr Response, FString SavePath, FString FileName);
- AHttpLoader* HttpLoader = nullptr;
- private:
- int DownloadCompleteNumber = 0;
- int NeedDownloadNumber = 0;
- };
// Fill out your copyright notice in the Description page of Project Settings.
#include "GameBaseState.h"
#include "IDHManagers/HttpLoader.h"
#pragma once
/**
*
*/
//class AHttpLoader;
class IDHOME_API GameUpdateResourcesState :public GameBaseState
{
private:
struct FileMessage
{
FString FileName;
FString FileMD5;
};
public:
GameUpdateResourcesState();
~GameUpdateResourcesState();
void OnEnter(TArray<void*> Params) override;
void OnExit() override;
private:
void GetServerResoucesVersionFile();
void CompareServerAndLocalVersion(bool bSuccess, FHttpResponsePtr Response);
const FString LocalVersionFileLocation = FPaths::GameContentDir() + TEXT("Data/Version.txt");
const FString ServerPakDirectory = TEXT("http://localhost:80/icons/Data/");
const FString SavePakDirectory = FPaths::GameContentDir() + TEXT("DownLoadPaks/");
bool GetVersionMessageFromString(FString JsonString, FString& VersionFileMD5, TArray<FileMessage>& FileMessages);
void UpdateResources(const TArray<FileMessage>& Files);
void DownloadFileComplete(FHttpResponsePtr Response, FString SavePath, FString FileName);
AHttpLoader* HttpLoader = nullptr;
private:
int DownloadCompleteNumber = 0;
int NeedDownloadNumber = 0;
};
GameUpdateResourcesState.cpp: - // Fill out your copyright notice in the Description page of Project Settings.
- #include "IDHome.h"
- #include "GameUpdateResoucesState.h"
- #include "IDHManagers/AssetManager.h"
- #include "IDHGameState.h"
- GameUpdateResourcesState::GameUpdateResourcesState()
- {
- }
- GameUpdateResourcesState::~GameUpdateResourcesState()
- {
- }
- void GameUpdateResourcesState::OnEnter(TArray<void*> Params)
- {
- //获取服务器上的资源版本文件
- GetServerResoucesVersionFile();
- }
- void GameUpdateResourcesState::OnExit()
- {
- }
- void GameUpdateResourcesState::GetServerResoucesVersionFile()
- {
- if (HttpLoader == nullptr)
- {
- TArray<AActor*> Actors;
- UGameplayStatics::GetAllActorsOfClass(World, AHttpLoader::StaticClass(), Actors);
- HttpLoader = Cast<AHttpLoader>(Actors[0]);
- }
- FString Url = TEXT("http://localhost:80/icons/Data/Version.txt");
- FString SendDataString;
- FDownloadDelegate DownloadServerFileDelegate;
- DownloadServerFileDelegate.BindRaw(this, &GameUpdateResourcesState::CompareServerAndLocalVersion);
- HttpLoader->OnHttpRequest(Url, SendDataString, DownloadServerFileDelegate);
- }
- void GameUpdateResourcesState::CompareServerAndLocalVersion(bool bSuccess, FHttpResponsePtr Response)
- {
- //访问不到服务器或者服务器上没有该文件等...
- if (!bSuccess)
- {
- UE_LOG(LogClass, Log, TEXT("服务器上没有Version.txt文件,无需更新..."));
- AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
- return;
- }
- FString LocalFile;
- FString LocalMD5;
- TArray<FileMessage> LocalFileMessages;
- FString ServerFile = Response.Get()->GetContentAsString();
- FString ServerMD5;
- TArray<FileMessage> ServerFileMessages;
- //获取服务器版本信息
- GetVersionMessageFromString(ServerFile, ServerMD5, ServerFileMessages);
- //解析出来的MD5值非法或者没有需要下载的文件
- if(ServerMD5.IsEmpty() || ServerFileMessages.Num() == 0)
- {
- AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
- return;
- }
- //加载本地的资源版本文件
- if (FFileHelper::LoadFileToString(LocalFile, *LocalVersionFileLocation))
- {
- //获取本地版本信息
- GetVersionMessageFromString(LocalFile, LocalMD5, LocalFileMessages);
- if (LocalMD5.Equals(ServerMD5))
- {
- UE_LOG(LogClass, Log, TEXT("版本文件MD5值相同.无需更新"));
- AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
- return;
- }
- }
- //覆盖本地的资源版本文件
- FFileHelper::SaveStringToFile(ServerFile, *LocalVersionFileLocation);
- //开始更新资源
- UpdateResources(ServerFileMessages);
- }
- bool GameUpdateResourcesState::GetVersionMessageFromString(FString JsonString, FString& VersionFileMD5, TArray<FileMessage>& FileMessages)
- {
- TSharedPtr<FJsonObject> JsonObject;
- TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
- //将文件中的内容变成你需要的数据格式
- if (FJsonSerializer::Deserialize(Reader, JsonObject))
- {
- TSharedPtr<FJsonObject> FileObject = JsonObject->GetObjectField("FileVersion");
- VersionFileMD5 = FileObject->GetStringField("MD5");
- FileMessages.Empty();
- const TArray<TSharedPtr<FJsonValue>> Files = JsonObject->GetArrayField("Files");
- for (int i = 0; i < Files.Num(); i++)
- {
- const TSharedPtr<FJsonObject>* FileMessageObject;
- if (Files[i].Get()->TryGetObject(FileMessageObject))
- {
- FileMessage* FileMes = new FileMessage();
- FileMes->FileName = FileMessageObject->Get()->GetStringField("FileName");
- FileMes->FileMD5 = FileMessageObject->Get()->GetStringField("MD5");
- FileMessages.Add(*FileMes);
- }
- }
- return true;
- }
- else
- {
- UE_LOG(LogClass, Error, TEXT("无法解析json数据,Json数据可能有误..."));
- return false;
- }
- }
- void GameUpdateResourcesState::UpdateResources(const TArray<FileMessage>& Files)
- {
- NeedDownloadNumber = Files.Num();
- DownloadCompleteNumber = 0;
- //一个个文件进行下载
- for (int i = 0; i < Files.Num(); i++)
- {
- FString FileURL = ServerPakDirectory + Files[i].FileName + TEXT(".pak");
- FString SaveURL = SavePakDirectory + Files[i].FileName + TEXT(".pak");
- FRequestDelegate DownloadCompleteDelegate;
- DownloadCompleteDelegate.BindRaw(this, &GameUpdateResourcesState::DownloadFileComplete, SaveURL, Files[i].FileName);
- AAssetManager::Singleton()->DownloadPakFile(FileURL, DownloadCompleteDelegate);
- }
- }
- //文件下载完成回调
- void GameUpdateResourcesState::DownloadFileComplete(FHttpResponsePtr Response, FString SavePath, FString FileName)
- {
- FFileHelper::SaveArrayToFile(Response->GetContent(), *SavePath);
- UE_LOG(LogClass, Log, TEXT("文件:%s 已经下载完成,保存在%s"), *FileName, *SavePath);
- DownloadCompleteNumber++;
- //如果所有文件都下载完了.那么就进行下一个游戏状态.
- if (DownloadCompleteNumber == NeedDownloadNumber)
- {
- UE_LOG(LogClass, Log, TEXT("资源已全部更新完毕,跳转到登录状态"));
- AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
- }
- }
// Fill out your copyright notice in the Description page of Project Settings.
#include "IDHome.h"
#include "GameUpdateResoucesState.h"
#include "IDHManagers/AssetManager.h"
#include "IDHGameState.h"
GameUpdateResourcesState::GameUpdateResourcesState()
{
}
GameUpdateResourcesState::~GameUpdateResourcesState()
{
}
void GameUpdateResourcesState::OnEnter(TArray<void*> Params)
{
//获取服务器上的资源版本文件
GetServerResoucesVersionFile();
}
void GameUpdateResourcesState::OnExit()
{
}
void GameUpdateResourcesState::GetServerResoucesVersionFile()
{
if (HttpLoader == nullptr)
{
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsOfClass(World, AHttpLoader::StaticClass(), Actors);
HttpLoader = Cast<AHttpLoader>(Actors[0]);
}
FString Url = TEXT("http://localhost:80/icons/Data/Version.txt");
FString SendDataString;
FDownloadDelegate DownloadServerFileDelegate;
DownloadServerFileDelegate.BindRaw(this, &GameUpdateResourcesState::CompareServerAndLocalVersion);
HttpLoader->OnHttpRequest(Url, SendDataString, DownloadServerFileDelegate);
}
void GameUpdateResourcesState::CompareServerAndLocalVersion(bool bSuccess, FHttpResponsePtr Response)
{
//访问不到服务器或者服务器上没有该文件等...
if (!bSuccess)
{
UE_LOG(LogClass, Log, TEXT("服务器上没有Version.txt文件,无需更新..."));
AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
return;
}
FString LocalFile;
FString LocalMD5;
TArray<FileMessage> LocalFileMessages;
FString ServerFile = Response.Get()->GetContentAsString();
FString ServerMD5;
TArray<FileMessage> ServerFileMessages;
//获取服务器版本信息
GetVersionMessageFromString(ServerFile, ServerMD5, ServerFileMessages);
//解析出来的MD5值非法或者没有需要下载的文件
if(ServerMD5.IsEmpty() || ServerFileMessages.Num() == 0)
{
AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
return;
}
//加载本地的资源版本文件
if (FFileHelper::LoadFileToString(LocalFile, *LocalVersionFileLocation))
{
//获取本地版本信息
GetVersionMessageFromString(LocalFile, LocalMD5, LocalFileMessages);
if (LocalMD5.Equals(ServerMD5))
{
UE_LOG(LogClass, Log, TEXT("版本文件MD5值相同.无需更新"));
AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
return;
}
}
//覆盖本地的资源版本文件
FFileHelper::SaveStringToFile(ServerFile, *LocalVersionFileLocation);
//开始更新资源
UpdateResources(ServerFileMessages);
}
bool GameUpdateResourcesState::GetVersionMessageFromString(FString JsonString, FString& VersionFileMD5, TArray<FileMessage>& FileMessages)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
//将文件中的内容变成你需要的数据格式
if (FJsonSerializer::Deserialize(Reader, JsonObject))
{
TSharedPtr<FJsonObject> FileObject = JsonObject->GetObjectField("FileVersion");
VersionFileMD5 = FileObject->GetStringField("MD5");
FileMessages.Empty();
const TArray<TSharedPtr<FJsonValue>> Files = JsonObject->GetArrayField("Files");
for (int i = 0; i < Files.Num(); i++)
{
const TSharedPtr<FJsonObject>* FileMessageObject;
if (Files[i].Get()->TryGetObject(FileMessageObject))
{
FileMessage* FileMes = new FileMessage();
FileMes->FileName = FileMessageObject->Get()->GetStringField("FileName");
FileMes->FileMD5 = FileMessageObject->Get()->GetStringField("MD5");
FileMessages.Add(*FileMes);
}
}
return true;
}
else
{
UE_LOG(LogClass, Error, TEXT("无法解析json数据,Json数据可能有误..."));
return false;
}
}
void GameUpdateResourcesState::UpdateResources(const TArray<FileMessage>& Files)
{
NeedDownloadNumber = Files.Num();
DownloadCompleteNumber = 0;
//一个个文件进行下载
for (int i = 0; i < Files.Num(); i++)
{
FString FileURL = ServerPakDirectory + Files[i].FileName + TEXT(".pak");
FString SaveURL = SavePakDirectory + Files[i].FileName + TEXT(".pak");
FRequestDelegate DownloadCompleteDelegate;
DownloadCompleteDelegate.BindRaw(this, &GameUpdateResourcesState::DownloadFileComplete, SaveURL, Files[i].FileName);
AAssetManager::Singleton()->DownloadPakFile(FileURL, DownloadCompleteDelegate);
}
}
//文件下载完成回调
void GameUpdateResourcesState::DownloadFileComplete(FHttpResponsePtr Response, FString SavePath, FString FileName)
{
FFileHelper::SaveArrayToFile(Response->GetContent(), *SavePath);
UE_LOG(LogClass, Log, TEXT("文件:%s 已经下载完成,保存在%s"), *FileName, *SavePath);
DownloadCompleteNumber++;
//如果所有文件都下载完了.那么就进行下一个游戏状态.
if (DownloadCompleteNumber == NeedDownloadNumber)
{
UE_LOG(LogClass, Log, TEXT("资源已全部更新完毕,跳转到登录状态"));
AIDHGameState::Singleton()->ChangeState(GameStateEnum::LoginState);
}
}
这里面,AHttpLoader是我写的一个发起HttpRequest的一个单例类,主要用于和服务器交互,请求数据或者是下载文件等..还有几个DownLoadDelegate之类的委托.整体思路很简单 1.进入该状态以后,去下载服务器上的版本信息文件
2.如果连接不上服务器或者没有服务器上没有这个文件,那么就直接执行下一个游戏状态的逻辑.如果能连接上并下载到了版本信息文件.进行下一步
3.获取服务器版本文件信息,如果数据合法,那么开始加载本地信息文件,对两个文件的MD5值进行比较.如果相同,那么就进行下一游戏状态的逻辑,否则执行下一步
4.如果MD5值不同,那么就先覆盖本地的版本信息文件,然后开始更新资源
5.一个一个资源的下载,下载完最后一个文件的时候,资源更新状态的逻辑就已经都做完了,那么可以执行接下来的逻辑啦.
整个资源的热更新思路就是这么简单.
文章里面涉及到的Pak文件和下载部分的教程我之前都已经写过了.串在一起就是一份完整的热更新思路了.至于你拿到了pak文件以后要怎么使用,那是你的事,不在这次的讨论范围内.
但是有一点值得注意的,经过我测试,目前UE4的pak文件是没有依赖关系的.也就是说,如果你打包了一个UBlueprint,里面包含了一个UStaticMesh的话,那么当你直接加载这个UBlueprint到世界中的时候,他的Mesh是会丢失的.在Unity里面我们通过AssetBundle,可以先将Mesh加载进内存,这种情况下就能正常加载出来Prefab了..但是在UE4里面不行.所以可能我们需要自己去维护这一个依赖关系.甚至可能对于UBlueprint这些要生成一个文件来记录他包含了什么依赖,就像UNITY中做的那样...处理依赖这个问题在UE4里面估计会是一个比较复杂的问题...这一块就先不讨论了..mmmm...有可能以后也不会讨论...
1.创建一个PAK文件:
用CMD运行打开D:\Epic Games\UE_4.15\Engine\Binaries\Win64下有个UnrealPak文件
cd到路径后 通过运行时传递参数 UnrealPak.exe [要生成的pak文件] -create=[要打包的文件列表] -order=[文件在pak中排序描述文件] [输出格式] [是否加密] [是否压缩]
例如:UnrealPak.exe test.pak -create=paklist.txt -order=CookerOpenOrder.log -UTF8Output -encrypt -compress
UnrealPak.exe D:\OutPak.Pak C:\Users\Administrator\Desktop\Client\UnrealProjects\IDHome\Content\Materials\bingxiang.uasset
这样就有一个PAK文件了,接下来创建代码
.h
.cpp
#include "IPlatformFilePak.h"
这样就加载到内存了。