AB包梗概
是什么?
类似与平台的资产压缩包,将资产如模型,贴图,预设体材质球等打包。
作用?
通常unity加载资源时,我们使用Resources文件夹,但缺点很多
- 打包后即定死,只读,不允许修改,也就不支持动态更新。
- 打包后文件较大,占用空间。
相对Resources,AB可以更好的管理资源
- 存储位置可自定义
- 压缩方式也可自定义,减少包体大小
- 支持热更新(资源热更新和脚本热更新)
AB包热更新流程
- 第一步:连接服务器获取资源服务器地址
- 第二步:从资源服务器端(存储着各种AB包)下载资源对比文件
- 第三步:客户端通过资源对比文件检测哪些要更新,哪些要下载,然后下载更新AB包。
AB包的窗口配置
Unity官方提供了打包工具
Asset Bundle Browser
高版本upm内提供了Addressables,其内部封装好了Asset Bundle Browser。也可以去Github下载AB Browser文件导入即可。
在Window内打开ABB,可以看到如图

我们看到了三个页签
- Configure 配置页
- Build 打包页
- Inspect 检视页
(翻译了一遍。。。。)
直接pass!
Build
参数说明
其他的,咱们把鼠标放上去就知道什么意思了。
打包后生成的文件说明
打包后每个资产都会生成了一对文件
AB包文件 二进制文件,记录者对应资产信息
manifest文件 记录着AB包文件信息,提供了资源信息,依赖关系,版本信息等
简单看一下manifest文件信息

1~10行:记录着版本,CRC码,哈希码等关键信息
11~36行:记录着引用的脚本等脚本信息
注意:C#代码无法被打包进去,这就是为什么我们需要热更新语言。
37~39行:资源路径
40:依赖项
生成的文件中有一对文件名和目录相同,称为主包文件,记录着所有AB包依赖关系。
Inspect
直接pass!
使用AB包
这里只说我用过的
和使用Resources API异曲同工,注意类名即可
同步加载资源
1 2 3 4 5 6 7 8 9 10 11
| AssetBundle a = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/cube");
GameObject g = a.LoadAsset<GameObject>("cube"); Instantiate(g);
GameObject c = a.LoadAsset("cube", typeof(GameObject)) as GameObject; Instantiate(c);
建议使用非泛型方法,因为为Lua并不支持泛型 此外若重复加载AB包会报错
|
异步加载(协程方式)
1 2 3 4 5 6 7 8
| IEnumerator LoadGameObjAsync(string ABpath,string name) { AssetBundleCreateRequest ab= AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/"+ABpath); yield return ab; AssetBundleRequest abc= ab.assetBundle.LoadAssetAsync(name, typeof(GameObject)); yield return abc; Instantiate(abc.asset as GameObject); }
|
异步加载(非协程)
1 2 3 4 5 6 7 8 9 10
| AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/cube"); AssetBundleRequest ff = null; request.completed += (f) => { ff = request.assetBundle.LoadAssetAsync("cube"); ff.completed += (f) => { Instantiate(ff.asset as GameObject); }; };
|
异步加载用协程的方式更加明了,感觉。
卸载资源
1 2 3 4 5 6
| AssetBundle.UnloadAllAssetBundles(bool);
AssetBundle a = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/cube"); a.Unload(false);
|
AB包的依赖关系
如果一个或者多个 UnityEngine.Objects 引用了其他 AssetBundle 中的 UnityEngine.Object,那么 AssetBundle 之间就产生的依赖关系。相反,如果 UnityEngine.ObjectA 所引用的UnityEngine.ObjectB 不是其他 AssetBundle 中的,那么依赖就不会产生。
所以当我们加载一个资源却没有加载其依赖项的AB包时,加载的资源将会缺失其依赖资源,在unity内试试就知道啦
所以就得加载依赖项的AB包,但如果一个资源有多个AB包依赖,一个一个加载很繁琐,这时候就可以用到主包了,它记录着所有依赖关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| AssetBundle a = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/cube");
AssetBundle main = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/StandaloneWindows");
AssetBundleManifest manifest = main.LoadAsset("AssetBundleManifest", typeof(AssetBundleManifest)) as AssetBundleManifest;
string[] strs = manifest.GetAllDependencies(a.name); foreach(var k in strs) { AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/"+k); }
GameObject g = a.LoadAsset<GameObject>("cube"); Instantiate(g);
|
这样写有个缺点:
一个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 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| using System.Collections.Generic; using UnityEngine; using Cysharp.Threading.Tasks; using UnityEngine.Events;
public class ABMgr : Singleton<ABMgr> { private AssetBundle main; private AssetBundleManifest ABmanifest; public Dictionary<string, AssetBundle> ABBundles ; public ABMgr() { ABBundles = new Dictionary<string, AssetBundle>(); main = AssetBundle.LoadFromFile(PATH + MAiN_AB_NAME); ABmanifest=main.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); }
public string MAiN_AB_NAME { get { return "StandaloneWindows"; } } public string PATH { get { return Application.streamingAssetsPath + "/"; } } public AssetBundle Main { get { return main; } } public AssetBundleManifest ABManifest { get { return ABmanifest; } }
public void UnloadAB(string name, bool a) { if (!ABBundles.ContainsKey(name)) throw new System.Exception("无指定包"); ABBundles[name].Unload(a); ABBundles.Remove(name); }
public void UnloadAllAB(bool a) { AssetBundle.UnloadAllAssetBundles(a); ABBundles.Clear(); }
public void ReloadNecessaryBundle() { if(main==null) main = AssetBundle.LoadFromFile(PATH + MAiN_AB_NAME); if(ABmanifest==null) ABmanifest = Main.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); }
public AssetBundle LoadAB(string ABName) { AssetBundle bundle = null; if(!ABBundles.ContainsKey(ABName)) { bundle = AssetBundle.LoadFromFile(PATH + ABName); ABBundles.Add(ABName, bundle); } else bundle = ABBundles[ABName]; string[] strs=ABManifest.GetAllDependencies(bundle.name); foreach(var a in strs) { if(!ABBundles.ContainsKey(a)) { AssetBundle b = AssetBundle.LoadFromFile(PATH + ABName); ABBundles.Add(a, b); } } if (bundle == null) throw new System.Exception("AB包加载失败"); return bundle; }
public Object loadAsset(string ABName,string assetName,System.Type type) { LoadAB(ABName); return ABBundles[ABName].LoadAsset(assetName,type); }
public T loadAsset<T>(string ABName, string assetName) where T : Object { LoadAB(ABName); return ABBundles[ABName].LoadAsset<T>(assetName); }
public Object LoadAssetAsync(string ABName,string assetName,System.Type type,System.Action<AsyncOperation> action = null) { LoadAB(ABName); AssetBundleRequest assetBundleRequest= ABBundles[ABName].LoadAssetAsync(assetName,type); assetBundleRequest.completed += action; return assetBundleRequest.asset; }
public T LoadAssetAsync<T>(string ABName, string assetName,System.Action<AsyncOperation> action=null) where T : Object { LoadAB(ABName); AssetBundleRequest assetBundleRequest = ABBundles[ABName].LoadAssetAsync<T>(assetName); assetBundleRequest.completed += action; return assetBundleRequest.asset as T; } public async UniTask<Object> LoadAssetAsyncByUnitask(string ABName, string assetName, System.Type type, UnityAction<Object> action = null) { LoadAB(ABName); Object obj= await ABBundles[ABName].LoadAssetAsync(assetName, type); action(obj); return obj ; }
public async UniTask<T> LoadAssetAsyncByUnitask<T>(string ABName, string assetName, UnityAction<T> action=null) where T:Object { LoadAB(ABName); T obj = await ABBundles[ABName].LoadAssetAsync<T>(assetName) as T ; action(obj); return obj ;
} }
|
简单测试如下:
1 2 3 4 5 6 7 8
| async void Start() { await ABMgr.Instance.LoadAssetAsyncByUnitask<GameObject>("cube", "cube", (a) => { Instantiate(a); }); GameObject A = ABMgr.Instance.loadAsset("cube", "cube", typeof(GameObject)) as GameObject; Instantiate(A);
|

正常
vs算法 a性 项目
完。。。。。
Author:
cjforeal
Permalink:
https://cjforeal.github.io/2022/10/28/AssetBundle/
License:
Copyright (c) 2019 CC-BY-NC-4.0 LICENSE
Slogan: