什么是AB包,它的主要作用是什么?
AB包全名AssetBundle(资源包)。是一种Unity提供的用于存放资源的包。通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,并且可以有选择地加载内容。
(直白点讲就是对内存有优化)。ab包主要用来热更新(你问什么是热更?这个我们一会再说,往下看)。相比于Resources加载,ab包加载对于内存的负担更小,而且更加快速。
unity资源加载的俩种方式
在Unity中,一般来说,资源加载方式主要分为Resources加载和AssetBundle加载。
Unity有个特殊文件夹Resources,放在这个文件夹下的资源可以通过Resources.Load()来直接加载。即Resources加载资源方式。
当获得AssetBundle之后,也可以调用AssetBundle对应的API来加载资源。
热更新
游戏或者软件更新时,无需重新下周客户端进行安装,而是在应用程序启动的情况下,在内部进行的资源或者代码更新。说白点你甚至可以在玩家游玩的时候修改一些代码或者美术资源。
等玩家下次更新以后就会完全变成另外一款游戏。
ab包的安装
这个就不细说了。
ab包的应用
重点!!!! c#代码是无法被打入ab包的,所以我们要用lua语言。
ab包打包常用的选项
Build Target:构建的平台目标(IOS,Android,windows)
Output Path:输出路径
Clear Floders:是否清空文件夹,一般会选择,即在多次打包时,将原文件夹清空(即原来所有的包清空重新构建),但是资源很大时会很耗时
Copy to StreamingAssets:将打包的AB包从Output Path复制到特数的StreamingAssets文件夹(与备份不同,在某些平台是只读文件夹,在PC是可读可写)
Compression:压缩方式
{
No Compression:不压缩,解压快,但包很大,不推荐使用
LZMA:压缩最小,但解压很慢,缺点是如果只需要AB包中的一个资源,会将包中的所有资源解压
LZ4:压缩率没有LZMA大,但是可以单独解压一个资源,内存占用低(建议使用)
}
打包之后:
分两个文件:,.manifest
(1)关键AB包(和目录名一样的包):主包,存储着包与包之间的依赖关键关系
(2)(没有后缀名的文件):资源文件
(3).manifest:AB包文件信息,对应资源文件相关的关键配置信息。当加载时提供了资源信息,依赖关系,版本信息等关键信息。
其他:(了解即可)
ETI:在资源包中,不包含资源的类型信息
FR:重新打包时需要重新构建包,和ClearFolder类似,不同的是FR不会删除不再存在的包(即在打包的时候删除了某个包,但是原文件夹中的包不会被清除,浪费存储空间)
ITC:增量构建检查时,忽略类型数的更改
Append Hash:将文件哈希值附加到资源包名上(几乎不用)
SM:严格模式,如果打包时报错了,打包直接失败无法成功
DRB:运行时构建
Inspector界面:主要用于观测包的相关信息(大小,路径等)
如何使用ab包加载资源和ab包常用的api
加载AB包
1 2
| AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + 包名(无后缀));
|
加载AB包中的资源
有三个重载:资源名,Type指定类型,泛型
注意:只是用名字加载,会出现同名不同类型资源分不清,因此不建议使用,且:同一个AB包不能够重复加载,否则报错
eg:
1 2 3 4
| GameObject obj = ab.LoadAsset<GameObject>("Cube"); GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;(通过Lua在C#代码加载对象时只能通过类型,因为Lua不支持泛型) Instantiate(obj);
|
1 2 3
| public GameObject obj; StartCoroutine(LoadABRes("modle", "Cube"))
|
1 2 3 4 5 6 7 8 9 10
| IEnumerator LoadABRes(string ABName, string resName) { AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName); yield return abcr; AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resName, typeof(GameObject)); yield return abq; obj = abq.asset as GameObject; }
|
1 2 3
|
AssetBundle.UnloadAllAssetBundles();
|
如果参数为true,那么解绑定包时会将场景中通过AB包加载的资源一起卸载,false时只对AB包解绑
参数true/false的意义与UnloadAllAssetBundles()相同
AB包的依赖性
在包中的一个资源A如果使用了另一个资源B,那么会自动的将B放在同一个包中,但是如果将B的AssetBundle选择另一个包,那么B会打包到另一个包中,此时如果加载包中的A并创建对象,那么A组件中与B相关的内容会丢失,除非此时也将另一个包中的B进行加载,即:
加载AB包中的资源需要将资源的依赖包一起加载才能正常显示
步骤:
(1)加载AB包1号(假设此包中的资源对2号包中的资源有依赖)
(2)加载AB包2号
(3)加载1号包中的资源
但是实际要完成以上操作,我们需要提前知道哪些包之间存在依赖关系,因此需要利用主包获取依赖信息。
步骤:假设寻找包”model“的依赖包
(1)加载主包,假设主包名为Main(打包后主包名和路径名一样)
1
| AssetBundle abMain = AssetBundle.LoadFromFIle(Application.streamingAssetsPath + "/" + ”Main");
|
(2)加载主包中的固定文件
1
| AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
|
(3)从固定文件中,得到依赖信息,返回的即是依赖包的名字
1 2
| string[] strs = abManifest.GetAllDependecies(”model");
|
(4)加载相关依赖包
1 2 3 4
| List<AssetBundle> dependecies = new LIst<AssetBundle>(); for(int = 0; i < strs.Length; ++i) { dependecies.Add( 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
|
public class SingletonAutoMono<T> : MonoBehaviour where T:MonoBehaviour { private static T instance; public static T GetInstance(){ if (istance == null) { GameObject obj = new GameObject(); obj.name = typeof(T).ToString(); DontDestroyOnLoad(obj); instance = obj.AddComponent<T>(); } return instance; } }
|
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
| public class ABMgr : SingletonAutoMono<ABMgr> { private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle> (); private AssetBundle mainAB = null; private AssetBundleManifest manifest = null; private ABMgr() { } private string PathUrl { get { return Application.streamingAssetsPath + "/"; } } private string MainABName { get { #if UNITY_IOS return "IOS"; #elif UNITY_ANDROID return "Andriod"; #else return "PC"; #endif } } public void LoadAB(string abName) { if (mainAB == null) { mainAB = AssetBundle.LoadFromFile(PahtUrl + MainABName); manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); } AssetBundle ab = null; string[] strs = manifest.GetAllDependencies(abName); for(int i = 0; i < strs.Length; ++i) { if (!abDic.ContainsKey(strs[i])) { ab = AssetBundle.LoadFromFile(PathUrl + str[i]); abDic.Add(strs[i],ab); } } if (!abDic.ContainsKey(abName)) { ab = AssetBundle.LoadFromFile(PathUrl + abName); abDic.Add(abName. ab); } } public Object LoadRes(string abName, string resName) { LoadAB(abName); Object obj = abDic[abName].LoadAsset(resName); if (obj is GameObject) return Instantiate(obj); else return obj; } public Object LoadRes(string abName, string resName, System.Type type) { LoadAB(abName); Object obj = abDic[abName].LoadAsset(resName, type); if (obj is GameObject) return Instantiate(obj); else return obj; } public T LoadRes<T> (string abName, string resName) where T:Object{ LoadAB(abName); T obj = abDic[abName].LoadAsset<T>(resName, type); if (obj is GameObject) return Instantiate(obj); else return obj; } public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack) { StartCoroutine(ReallyLoadResAsync(abName, resName, callBack)); } private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<Object> callBack) { LoadAB(abName); AssetBundlesRequest abr = abDic[abName].LoadAssetAsync(resName); yield return abr; if (abr.asset is GameObject) callBack(Instantiate(abr.asset)); else callBack(abr.asset); } 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) { LoadAB(abName); AssetBundlesRequest abr = abDic[abName].LoadAssetAsync(resName, type); yield return abr; if (abr.asset is GameObject) callBack(Instantiate(abr.asset)); else callBack(abr.asset); } public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callBack) where T:Object{ StartCoroutine(ReallyLoadResAsync<T>(abName, resName, callBack)); } private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<T> callBack) { LoadAB(abName); AssetBundlesRequest abr = abDic[abName].LoadAssetAsync<T>(resName); yield return abr; if (abr.asset is GameObject) callBack(Instantiate(abr.asset) as T); else callBack(abr.asset as T); } public void UnLoad(string abName) { if (abDic.ContainsKey(abName) { abDic[abName].Unload(false); abDic.Remove(abName); } } public void ClearAB() { AssetBundle.UnloadAllAsset Bundles(false); abDic.Clear(); mainAB = null; manifest = null; } }
|
同步加载和异步加载的区别及不同的加载方式
同步加载: 同步模式,又称阻塞模式,就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
即同步加载会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行
异步加载: 异步加载,又叫非阻塞,是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
加载方式
同步加载:
1 2 3 4 5 6
| Object obj = ABMgr.GetInstance().LoadRes("model", "Cube");
GameObject obj = ABMgr.GetInstance().LoadRes("model", "Cube") as GameObject;
|
1 2
| GameObject obj = ABMgr.GetInstance().LoadRes("model", "Cube", typeof(GameObject)) as GameObject;
|
1 2
| GameObject obj = ABMgr.GetInstance().LoadRes<GameObject>("model", "Cube");
|
异步加载
1 2 3 4
| ABMgr.GetInstance().LoadResAsync("model","Cube", (obj) => { (obj as GameObject).transform.position = -Vector3.up; });
|
1 2 3 4
| ABMgr.GetInstance().LoadResAsync("model","Cube", typeof(GameObject), (obj) => { (obj as GameObject).transform.position = -Vector3.up; });
|
1 2 3 4
| ABMgr.GetInstance().LoadResAsync<GameObject>("model","Cube", (obj) => { obj.transform.position = -Vector3.up; });
|