前言
这篇文章里会介绍关于热更新的概念知识以及热更方案简单介绍,也有很多引用放了链接,新手可以作为学习参考,希望可以帮到大家。
目录
什么是热更新?
热更新,就是指不用重新安装整个游戏程序,只用在游戏运行时进行资源和内容的下载。这样做的好处,就是减少用户流失,如果每次进行游戏更新就需要重新下载一次游戏,用户流失是很大的。
热更新分为脚本更新和资源更新,资源更新比较简单打包进pak更新,脚本更新就涉及到使用脚本语言开发了,UE常用的就是lua,行业内用的较多的是腾讯开源的Unlua和sluaunreal。但是对于一般的小型项目,蓝图就足够进行热更新了,蓝图是可以直接作为资源更新的。
可以直接热更的内容
1.资源uasset(地图、模型、贴图、UI、蓝图)
2.国际化内容(多语言信息)
3.资产注册表
4.shader
5.数据文件
Cook和Pak
关于cook虚幻官方官方是这样说的:
虚幻引擎以内部使用的特定格式存储内容资源,如PNG用于存储纹理 数据,WAV用于音频数据。但是,该内容需要针对各平台转换为不同的格式, 因为平台使用专有格式,或者平台不支持虚幻用来存储资源的格式, 又或者存在更节省内存或性能更好的格式。将内容从内部格的式转换为特定于平台的格式的过程 称为 cook。
简而言之就是把资源转成符合平台的格式,Android有Android的格式,iOS有ios的格式,Windows有Windows的格式。
至于什么是pak,其实就是一个压缩包,把上述的东西打了一个压缩包,然后放进.pak结尾的文件里。
Mount和Load
mount
中文译为安装,而在UE的热更新中,它是对Pak的一个操作,前面说了,pak相当于一个压缩包,将资源进行了打包,而mount就是将pak文件中的虚拟路径映射到游戏运行时所需的资源,让游戏能够正确地加载使用这些资源。下面是一个示例:
在resource.pak中有一些游戏资源,通过下面的代码可以进行mount操作
FString PakFilePath = FPaths::ProjectContentDir() + TEXT("resources.pak");
FString MountPoint = "/Game/Resources";//MountPoint就是Pak中所有文件的公共路径
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (PlatformFile.CreateDirectory(*MountPoint))
{
if (FPakPlatformFile* PakPlatformFile = new FPakPlatformFile())
{
PakPlatformFile->Initialize(&PlatformFile, *MountPoint);
PakPlatformFile->Mount(*PakFilePath, 0, *MountPoint);
FPlatformFileManager::Get().SetPlatformFile(*PakPlatformFile);
}
}
关于MountPoint:MountPoint的作用实际上时用来检测要加载的文件是否存在于当前Pak中,只要检测要读取的文件是否以这个路径开头,就可以首次按排除掉基础路径不对的文件,基础路径如果不对,就说明这个文件在Pak中也不存在。(更具体的说明可以去看腾讯zlp同学的分享)
Load
前面说了Mount是将虚拟文件系统路径映射到物理存储介质中,这里的Load就是加载了,Load也是一个操作,是指在运行时加载代码或者资源并将其合并到游戏中。同样给出一个示例施使用Load来实习热更新:
加载包含新的游戏逻辑的脚本"NewGameplayScript.lua"
FString ScriptFilePath = FPaths::ProjectContentDir() + TEXT("Scripts/NewGameplayScript.lua");
if (FPaths::FileExists(ScriptFilePath))
{
// 加载新脚本
UObject* NewScript = LoadObject<UObject>(nullptr, *ScriptFilePath);
if (NewScript)
{
// 处理新脚本逻辑
}
}
同步加载与异步加载
在热更新中使用异步加载能提高游戏性能和流程性,同步加载可能导致游戏卡顿,选择合适的加载方式能够更好的实现热更新功能。
同步加载
同步加载是指在加载资源的过程中,程序会等待资源加载完毕后再继续执行后续代码。这意味着当执行到加载资源的代码时,程序会阻塞在那里直到资源完全被加载进内存。同步加载的优点是简单直观,但缺点是可能会导致游戏卡顿,特别是当加载大量资源时。
在热更新中的同步加载,通常会导致游戏暂停执行,直到新内容全部加载完成才能继续游戏,可能会给玩家带来不流畅的体验。
简单示例演示同步加载资源的方式:
UTexture* MyTexture = LoadObject<UTexture>(nullptr, TEXT("/Game/Textures/MyTexture"), nullptr, LOAD_None, nullptr);
异步加载
异步加载是指在加载资源的过程中,程序会继续执行后续代码,而不会等待资源加载完成。加载完成后会触发一个回调函数来处理加载完成后的操作。异步加载可以提高游戏的流畅性,避免卡顿现象,但需要额外处理回调函数。
在热更新中的异步加载,可以在后台加载新的内容,保持游戏的运行流畅性,待新内容加载完成后再切换到新内容并通知玩家。
简单示例演示异步加载资源的方式:
FStreamableManager StreamableManager;
TSoftObjectPtr<UTexture> TextureSoftObjectPtr(TEXT("/Game/Textures/MyTexture"));
StreamableManager.RequestAsyncLoad(TextureSoftObjectPtr.ToSoftObjectPath(), FStreamableDelegate::CreateLambda([]()
{
UTexture* MyTexture = TextureSoftObjectPtr.Get();
if (MyTexture)
{
// 异步加载完成后的处理逻辑
}
}));
可以看到,这里用到了一个名为TSoftObjectPtr的指针,这是常见的软引用方式之一。
软引用与硬引用
软引用(Soft Reference)
加载在内存中时,引用对象资源不会被加载到内存中,只有在需要的时候才会被手动加载进内存中。
常见软引用类型:
TSoftObjectPtr
TSoftClassPtr
FSoftObjectPath
FSoftClassPath
硬引用(Hard Reference)
与软引用对应的就是硬引用,当硬引用被加载到内存中时,被引用的对象资源也会被加载到内存中共。
更加具体的概念,可以查看官方文档—引用资产和写得比较好的博文
热更新方式介绍
热更新基础流程
简单制作了一个流程图说明热更新的基础步骤:
UE原生热更新方案
UE官方文档给出了一个热更新的基础方案整体流程如下:
非常建议大家跟着官方文档做一遍,
其实做的事情就是先将资源进行分块,打包成pak文件,模拟我们热更时需要添加的部分,把打包好的pak放进IIS中(模拟把热更包放到服务器上),然后使用ChunkDownloader插件去从服务器地址下载pak文件,放到本地\Saved\PersistentDownloadDir 文件夹下,游戏运行的时候会自动从这个文件夹加载资源到文件中。
官方给出的切分资源的方式比较麻烦,另外也有很多问题,比如没办法一次性打包多平台,管理也比较困难,推荐大家使用插件HotPatcher,具体使用方式可以参照这个文章。