[toc]
# 热更新总流程
首先先把需求拆出来
- 热更新时我们需要什么?
- 第一个就是大版本包
- 大版本包里面需要当前版本内所有的 Assetbundle (如果不需要压包), 当用户下载这个软件的时候,就会自动包含这些包
- 大版本包也称基础包
- 第二个就是更新包
- 如果我们在后期对包内某个资源进行了更新,我们就需要对这个包进行更新
- 即我们需要将这个包打出来,然后放到文件服务器上
- 再通过文件服务器的更新资源和本地更新包目录内的的 crc 校验码进行对比来确定需要更新哪些东西
- 为了让我们调用 ab 包内资源更贴近 Resource.Load
- 所以我们需要记录每个资源在项目里的相对路径,哪怕他实际路径不在那里,也要映射起来
# 资源热更流程示意图
如上图所示,我们在最初的时候会打一个完整的基础包,我们记为 Assetbundle_Base
如果后面出现了更新包,(比如某个资源出错了,需要更新一下), 这时候我们就需要打更新包,记为 AssetBundle_Update
其中更新包里只会包含 当前需要更新的文件
,配置文件里也只会有需要更新的 ab 列表。这就是资源热更的流程
# 资源拆分粒度
在猫老师的课程中,他把资源按照文件夹进行了拆分,总体大概如下: 即 GAsset 是资源存放的根目录,对于一个游戏而言,可能存在很多不同的模块,每个模块有自己的资源,这时候就需要把模块拆分开,所以就有了上面那个图 GAsset 作为资源总目录,它下面的每个子文件夹都是一个模块,我们对每个模块生成 这个模块的资源配置文件
然后对于模块内的 每个子文件夹都打包成一个ab包
,按照路径进行 前缀式的组合
,比如: GAsset/Launch/sprite/
, 就组合成 gasset_launch_sprite.assetbundle
这个 ab 包里包含 这个文件夹内所有的资源
,但 不包含这个文件夹的子文件里的资源
# 资源配置文件结构
知道了上面的资源粒度拆分,我们就需要得到一个模块中 有哪些ab包
,以及 每个资源对应的ab包和其依赖了哪些其他ab包以及它本身的路径
举个例子,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| { "BundleArray": { "gassets_launch": { "bundle_name": "gassets_launch", "crc": "96e20479", "size": 3042, "assets": [ "Assets/GAssets/Launch/PizzaCat.prefab", "Assets/GAssets/Launch/Sphere.prefab" ] }, "gassets_launch_mat": { "bundle_name": "gassets_launch_mat", "crc": "795e1328", "size": 24607, "assets": [ "Assets/GAssets/Launch/Mat/TestMaterial.mat" ] }, "gassets_launch_sprite": { "bundle_name": "gassets_launch_sprite", "crc": "604364b8", "size": 74946, "assets": [ "Assets/GAssets/Launch/Sprite/header.jpg" ] } }, "AssetArray": [ { "asset_path": "Assets/GAssets/Launch/PizzaCat.prefab", "bundle_name": "gassets_launch", "dependencies": [] }, { "asset_path": "Assets/GAssets/Launch/Sphere.prefab", "bundle_name": "gassets_launch", "dependencies": [ "gassets_launch_mat" ] }, { "asset_path": "Assets/GAssets/Launch/Mat/TestMaterial.mat", "bundle_name": "gassets_launch_mat", "dependencies": [] }, { "asset_path": "Assets/GAssets/Launch/Sprite/header.jpg", "bundle_name": "gassets_launch_sprite", "dependencies": [] } ] }
|
其中分为 Bundle区域
和 Asset区域
其中 Bundle区域
中记录每个 AssetBundle 里: - 包含哪些资源 - crc 校验码 - bundle 名字 - bundle 大小 Asset区域
记录每个资源: - 对应哪个父 Bundle - 依赖哪些 Bundle - 资源在原项目里的路径
# 运行时
# AB 包的加载 (包含在资源的加载中)
先把 Bundle 和 Asset 的信息加载到内存里: - 加载 Update_BundleInfo - 加载 Base_BundleInfo - 加载资源配置信息 然后运行时加载资源的时候,如下图所示
# AB 包的卸载
他这里暂时实现的方式是:在 Update 中检查 如果资源没有依赖的 GameObject 了,就从对应的 bundle 和依赖的 bundle 中移除这个资源,如果这个 bundle 的资源列表也为空,就卸载这个 bundle 但是一般来说要么用引用计数,LRU 之类的
# 实际实现 (慢慢填)
# Bundle 区域 BundleRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public enum BaseOrUpdate { Base, Update }
public class BundleInfo { public string bundle_name;
public string crc;
public int size;
public List<string> assets;
}
public class BundleRef { public BundleInfo bundleInfo;
public AssetBundle bundle;
public List<AssetRef> children;
public BaseOrUpdate witch;
public BundleRef(BundleInfo bundleInfo,BaseOrUpdate wicth_) { this.bundleInfo = bundleInfo;
this.witch = wicth_; } }
|
主要就是按照上面的需求中那样,包含了静态配置信息,以及运行时管理这个 Bundle 所需要的 Bundle引用信息
BaseOrUpdate
表示的是:当前这个 bundle 是更新的还是基本包
# Asset 区域 AssetRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class AssetInfo { public string asset_path;
public string bundle_name;
public List<string> dependencies; }
public class AssetRef { public AssetInfo assetInfo;
public BundleRef bundleRef;
public BundleRef[] dependencies;
public Object asset;
public bool isGameObject;
public List<GameObject> children;
public AssetRef(AssetInfo assetInfo) { this.assetInfo = assetInfo; } }
|