热更新流程
- 启动游戏
- 根据当前版本号,和平台号去版本服务器上检查是否有热更
- 从热更服务器上下载md5文件,比对需要热更的具体文件列表
- 从热更服务器上下载需要热更的资源,解压到热更资源目录
- 游戏运行加载资源,优先到热更目录中加载,再到母包资源目录加载
这些目录包含着不同版本的资源文件,以及用于版本控制的Files.txt
,里面存放着资源文件的名称和md5码。
客户端版本号我们是4位来标识,假设是X.Y.Z.W
X:【巨大版本号】这一位其实就是1,没事一般不会动他,除非有太巨大的变化,目前反正还是1;
Y:【整包更新版本号】:我们游戏一般一个月会有一个比较大的版本迭代,这种版本会走商店,每次提交Y值+1;
Z:【服务器协议版本号】,一个月度版本周期内,万一SDK有问题或者C#层有发现bug,需要更新商店,这一位会+1,这里单独留一个Z处理这种商店版本号,是因为不想影响Y值,而商店提交新包要求版本号必须有增加,buildNum也是商店要求必须要升的;
W:【编译版本号\热更版本号】,每次热更都+1 。
【第2位加1之后,3、4位全部清0】
作用
相对于Resources下的资源,AB包可以更好地管理资源,更灵活方便,更易于做热更新相关工作
AssetBundle
AssetBundle 是一个存档文件,包含可在运行时由 Unity 加载的特定于平台的非代码资源(比如模型、纹理、预制件、音频剪辑甚至整个场景)。AssetBundle 可以表示彼此之间的依赖关系;例如,一个 AssetBundle 中的材质可以引用另一个 AssetBundle 中的纹理。
AssetBundle对于可下载内容(DLC)、减少初始安装大小、加载针对最终用户平台优化的资产以及减少运行时内存压力都很有用
AssetBundle里面包含了什么?
- 首先是磁盘上的实际文件,这称为AssetBundle archive。AssetBundle archive是一个容器,就像一个文件夹一样,其中包含了额外的文件。这些额外的文件包括两类:
- 序列化文件,其中包含分解为各个对象并写入此单个文件的资源。
- 资源文件,这是为某些资源(纹理和音频)单独存储的二进制数据块,允许 Unity 高效地在另一个线程上从磁盘加载它们。
- 也可以指:通过代码进行交互以便从特定 AssetBundle 存档加载资源的实际 AssetBundle 对象。该对象包含添加到此存档文件的资源的所有文件路径的映射
游戏资源目录
- Resources:全部资源都会被压缩,转化成二进制。打包后该路径不存在,不可写也不可读。只能使用
Resources.Load
加载资源。 - Streaming Assets:全部资源原封不动打包。在移动平台下,是只读的,不能写入数据,其他平台下可以使用
System.File
类进行读写。在任意平台都可以使用AssetBundle.LoadFromFile
来从此文件夹读取加载ab包。
AssetBundle管理
打包资源
将需要同时加载的资源放在同一个包里,各个包之间会保存相互依赖的信息
分组依据:
- 逻辑实体分组:是指根据资源所代表的项目功能部分将资源分配给 AssetBundle。这包括各种不同部分,比如一组角色的所有模型和动画、多个关卡之间共享的景物的纹理和模型
- 类型分组:要构建供多个平台使用的 AssetBundle,类型分组是最佳策略之一。如果音频压缩设置在 Windows 和 Mac 平台上完全相同,则可以将所有音频数据打包到 AssetBundle 并重复使用,而着色器往往使用更多特定于平台的选项进行编译
- 并发内容分组: 将需要同时加载和使用的资源捆绑在一起。可以将这些类型的捆绑包用于基于关卡的游戏(其中每个关卡包含完全独特的角色、纹理、音乐等)。
卸载资源
在管理Asset和AssetBundle时,最重要的一点是调用AssetBundle.unload
时的方式,unload参数为true或false。
此API将卸载正在调用的AssetBundle的包头信息。unload参数决定是否也卸载从此AssetBundle实例化的所有对象。如果设置为true,那么从AssetBundle创建的所有对象也将立即卸载,即使它们目前正在活动场景中被引用。
如果调用了AssetBundle.Unload(True)
,则M将从场景中移除,销毁并卸载。但是,如果调用AssetBundle.Unload(False)
,则AB的包头信息将被卸载,但M将保持在场景中,并且仍然是可用的。
调用AssetBundle.Unload(False)
破坏了M和AB之间的链接。如果AB稍后再次加载,则AB中包含的对象的新副本将会被加载到内存中。此后如果再次加载AB,将再次加载这个包头信息的新副本,但是M和这个包并不会重新建立连接。如果此时调用AssetBundle.LoadAsset()
来重新加载M,将会有两个相同的副本M在现场。
为了避免这种情况,应该使用AssetBundle.Unload(True)
,并来确保对象不被复制,具体做法是:
- 在应用程序的生命周期内定义一个合适的节点,并在此期间卸载不需要的AssetBundle,例如在关卡切换或加载屏幕期间。这是最简单和最常见的选择。
- 维护单个对象的引用计数,并仅当所有组成对象都未使用时才卸载AssetBundle。这允许应用程序在不重复内存的情况下卸载和重新加载单个对象
另一个问题是:如果AssetBundle卸载后重新加载一个对象,重新加载将失败,该对象将以(Missing)对象的形式出现在Unity编辑器的层次结构中,材质会呈现洋红色。主要会发生在Unity失去并试图恢复对其图形上下文的控制时,例如当移动应用程序被挂起或用户锁定他们的PC时
加载AssetBundles
AssetBundles可以通过四个不同的API进行加载。但受限于两个标准,这四个API的行为是不同的。两个标准如下:
- AssetBundles的压缩方式:LZMA、LZ4、还是未压缩的。
- AssetBundles的加载平台。
而四个API分别是:
AssetBundle.LoadFromMemory(Async optional)
:不要使用这个API,因为他会冗余多次AssetBundle.LoadFromFile(Async optional)
:用于从本地存储(如硬盘或SD卡)加载未压缩或LZ4压缩格式的AssetBundle。UnityWebRequest's DownloadHandlerAssetBundle
WWW.LoadFromCacheOrDownload
(on Unity 5.6 or older)
一般来说,只要有可能,就应该使用AssetBundle.LoadFromFile
。这个API在速度、磁盘使用和运行时内存使用方面是最有效的。
从AssetBundles中加载Assets
Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects
,这些API都绑定到AssetBundle对象上,并且这些API具有同步和异步变体,这些API的同步版本总是比异步版本快至少一个帧(因为异步版本为了确保异步,都至少延迟了1帧):
LoadAsset (LoadAssetAsync)
LoadAllAssets (LoadAllAssetsAsync)
LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)
加载多个独立的
UnityEngine.Objects
时应使用LoadAllAsset
。并且只有在需要加载AssetBundle中的大多数或所有对象时,才应该使用它。LoadAllAsset
比对LoadAsset
的多个单独调用略快一些
Object加载是在主线程上执行,但数据从工作线程上的存储中读取。任何不触碰Unity系统中线程敏感部分(脚本、图形)的工作都将在工作线程上转换。例如,VBO将从网格创建,纹理将被解压等等。当一个Object完成加载时,它的Awake回调将被调用,该对象的其余部分将在下一个帧中对UnityEngine可用。