【Unity技术栈】Unity热更新之AssetBundle
聪头 游戏开发萌新

唐老狮–Unity热更新之AssetBundle

出自:https://www.bilibili.com/video/BV1LD4y1m7kF?p=1

AB包理论基础

定义:特定于平台的资产压缩包,有点类似压缩文件。资产包括:模型、贴图、预设体、音效、材质等等

作用:

  • 相对Resources下的资源AB包更好管理资源
image
  • 减小包体大小
    • 压缩资源
    • 减少初始包大小
  • 热更新
image

AB包资源打包

image image

以下使用Asset Bundle Browser打包

在需要打包的资源的Asset Labels下选择标签

image

Asset Bundle Browse的Build页签

image

打包后在Assets同级的AssetBundles文件夹内:AssetBundles/PC/

image

PC:存储所有依赖关系

AB包文件:资源文件

manifest文件:AB包文件信息,当加载时提供关键信息,资源信息,依赖关系,版本信息等等

  • Build Target:打包平台
  • Output Path:输出路径
  • Clear Folder:是否清空文件夹
  • Copy to StreamingAssets:复制到StreamingAssets

Advanced Settings:

  • Compression:压缩方式
    • No Compression:不压缩
    • Standard Compression(LZMA):压缩最小,解压慢,使用时必须全部解压
    • Chunk Based Compression(LZ4):压缩较小,解压快,使用时按需解压

了解:一般不勾选

  • Exclude Type Information:在资源包中 不包含资源的类型信息
  • Force Rebuild:重新打包时需要重新构建包(和Clear Folders很像),它不会删除无用包
  • Ignore Type Tree Changes:增量构建检查时,忽略类型数的更改
  • Append Hash:将文件的哈希值附加到资源包上
  • Strict Mode:严格模式,如果打包时报错了,则打包直接失败无法成功
  • Dry Run Build:运行时构建

使用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
public Image img;

void Start()
{
//1.加载 AB包
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
//2.加载 AB包中的资源(建议用泛型或Type指定类型加载)
//GameObject obj = ab.LoadAsset<GameObject>("Cube");
GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;
Instantiate(obj);
//3.卸载ab包
//卸载所有加载的ab包 true:资源受影响(丢失) false:资源不受影响(不丢失,通常选择)
AssetBundle.UnloadAllAssetBundles(false);
//卸载单个ab包
ab.Unload(false);

//异步加载-->协程
StartCoroutine(LoadABRes("head", "23_11100001"));
}

IEnumerator LoadABRes(string ABName, string resName)
{
//第一步 加载AB包
AssetBundleCreateRequest abcr =
AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);
yield return abcr;
//第二步 加载资源
AssetBundleRequest abq =
abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));
yield return abq;
img.sprite = abq.asset as Sprite;
}
  1. StreamingAssets在PC上可读可写,在Android/IOS上只读

  2. AB包不能重复加载,否则报错

AB包依赖

AB的依赖:一个资源身上用到了别的AB包中的资源,这个时候如果只加载自己的AB包,通过它创建对象,会出现资源丢失的情况。所以需要把依赖包一起加载了才能正常

打包资源时,若依赖项没有打包,会把依赖项自动打包至同一包中

依赖包的核心知识——利用主包 获取依赖信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Start()
{
//加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "PC");
//加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//从固定文件中 得到依赖信息(所有该包的依赖信息,而不是该资源的依赖信息)
string[] strs = abManifest.GetAllDependencies("model");
//加载依赖包
for(int i = 0; i < strs.Length; i++)
{
AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);
}
}

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
/****************************************************
文件:ABManager.cs
日期:2021/2/25 6:4:44
功能:提供 同/异步,通过泛型/Type 加载ab包资源
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace CT.Hotfix
{
public class ABManager : SingletonMono<ABManager>
{
//ab包存放路径
private static string PathUrl = Application.streamingAssetsPath + "/";
//主包名(使用不同宏对应不同平台)
#if UNITY_STANDALONE
private const string MainABName = "PC";
#elif UNITY_ANDROID
private const string MainABName = "Android";
#endif
//主包
private AssetBundle mainAB = null;
//依赖包
private AssetBundleManifest manifest = null;
//AB包缓存 避免重复加载ab包出错
private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();

new void Awake()
{
base.Awake();
}

//同步加载ab包
private void LoadAB(string abName)
{
//加载主包
if (mainAB == null)
{
mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
//获取依赖包
AssetBundle ab;
string[] strs = manifest.GetAllDependencies(abName);
//加载依赖包
for (int i = 0; i < strs.Length; i++)
{
//判断包是否加载过
if (!abDic.ContainsKey(strs[i]))
{
ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
}

#region SyncLoad
//使用泛型同步加载ab包内的资源
public T LoadRes<T>(string abName, string resName) where T : Object
{
//加载AB包
LoadAB(abName);
//根据泛型加载资源
T obj = abDic[abName].LoadAsset<T>(resName);
//如果是GameObject,就返回实例化对象
if(obj is GameObject)
return GameObject.Instantiate(obj);
return obj;
}
//根据类型同步加载ab包
public Object LoadRes(string abName, string resName, System.Type type)
{
//加载AB包
LoadAB(abName);
//根据类型加载资源
Object obj = abDic[abName].LoadAsset(resName, type);
//如果是GameObject,就返回实例化对象
if(obj is GameObject)
return GameObject.Instantiate(obj);
return obj;
}
#endregion

#region AsyncLoad
//使用泛型异步加载ab包资源
public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
{
StartCoroutine(ReallyLoadResAsync(abName, resName, callback));
}
private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
{
//加载AB包
LoadAB(abName);
//加载ab包资源
AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
yield return abr;
//异步加载结束后,传递给外部使用
if (abr.asset != null)
{
//如果是GameObject,就返回实例化对象
if(abr.asset is GameObject)
{
callback(GameObject.Instantiate(abr.asset) as T);
}
else
{
callback(abr.asset as T);
}
}
}
//使用类型异步加载ab包资源
public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
StartCoroutine(ReallyLoadResAsync(abName, resName, type, callback));
}
private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
//加载AB包
LoadAB(abName);

AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName, type);
yield return abr;
//异步加载结束后,传递给外部使用
if (abr.asset != null)
{
//如果是GameObject,就返回实例化对象
if(abr.asset is GameObject)
{
callback(GameObject.Instantiate(abr.asset));
}
else
{
callback(abr.asset);
}
}
}
#endregion

//单个包卸载
public void UnLoad(string abName)
{
if (abDic.ContainsKey(abName))
{
abDic[abName].Unload(false);
abDic.Remove(abName);
}
}
//所有包卸载
public void ClearAB()
{
AssetBundle.UnloadAllAssetBundles(false);
abDic.Clear();
mainAB = null;
manifest = null;
}
}
}

补充:AssetBundle加载

官方链接:https://docs.unity.cn/cn/2019.4/Manual/AssetBundles-Native.html

知乎参考链接:https://zhuanlan.zhihu.com/p/108335937

 评论