Unity AB包加载与依赖管理全解析
如何将资源与AB包进行关联
选择资源 在Inspector窗口最下方的AssetBundle处点击new填写一个名称
那么你在Window下的AssetBundle Browser中就可以看到你所关联的AB包
进行打包
Build Target 是打包的平台,不同的平台需要重新打包
Output Path是输出的路径,Application.dataPath下的AssetBundles/PC
Clear Folders 是否需要情况文件夹,可以删除一些没有用的资源,但随着项目往后资源越多打包越慢
Copy to StreamingAssets 打包后拷贝到SteamingAssets目录下
NoCompression 不压缩 解压快 包大 不推荐
LZMA 压缩最,解压慢 缺点用一个资源要解压所有
LZ4 压缩相对于LZMA大一点,建议使用,用什么解压什么,内存占用低
AB包生成的文件
AB包-资源文件
manifest文件- AB包文件信息和当加载时提供了关键信息,资源信息,依赖关系,版本信息等等
关键AB包(和目录名一样的包) - 主包,AB包依赖关键信息
代码控制加载AB包资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class lesson2_1 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){//加载AB包(AB包不能重复加载否则报错)AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/model");//加载AB包中的资源//GameObject obj = ab.LoadAsset<GameObject>("Cube");//GameObject obj2 = ab.LoadAsset("Sphere",typeof(GameObject)) as GameObject;//Instantiate(obj, Vector3.zero, Quaternion.identity);//Instantiate(obj2);StartCoroutine(LoadABRes("model","Cube"));//卸载所有加载的AB包 参数为true 会把通过AB包加载的资源也卸载//AssetBundle.UnloadAllAssetBundles(false);//ab.Unload(false);}IEnumerator LoadABRes(string ABName, string resName){AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath+"/"+ABName);yield return abcr;AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync<GameObject>("Cube");yield return abr;GameObject obj = abr.asset as GameObject;Instantiate(obj);}// Update is called once per framevoid Update(){}
}
关于AB包的依赖
一个资源身上用到了别的AB包中的资源 这个时候如果只加载自己的AB包,通过它创建对象 会出现资源丢失的情况
这种时候 需要把依赖包 一起加载才能正常
比如:给立方体关联了一个材质球
但材质球和立方体并不属于一个AB包
这样当我们加载model的ab包中的立方体时,场景上的立方体就会丢失材质
解决方案
利用主包 获取依赖信息
//加载AB包(AB包不能重复加载否则报错)AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/model");//加载AB包中的资源GameObject obj = ab.LoadAsset<GameObject>("Cube");//GameObject obj2 = ab.LoadAsset("Sphere",typeof(GameObject)) as GameObject;Instantiate(obj, Vector3.zero, Quaternion.identity);//Instantiate(obj2);//加载主包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包资源管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class ABMgr:MonoBehaviour
{private static ABMgr instance;public static ABMgr Instance => instance;private void Awake(){instance = this;}private AssetBundle mainAB = null;private AssetBundleManifest manifest = null;private string PathUrl{get {return Application.streamingAssetsPath + "/";}}private string MainABName{get {
#if UNITY_IOSreturn "IOS";
#elif UNITY_ANDROIDreturn "Android";
#elsereturn "PC";
#endif}}public Dictionary<string,AssetBundle> abDic = new Dictionary<string,AssetBundle>();//同步加载private void LoadAB(string abName){//如果没有加载过主包,先加载主包if (mainAB == null && !abDic.ContainsKey(MainABName)){mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);abDic.Add(MainABName, mainAB);manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");}//从主包的关键配置文件中获取依赖信息AssetBundle ab = null;string[] strs = manifest.GetAllDependencies(abName);for (int i = 0; i < strs.Length; i++){//判断和abName有依赖的ab包是否加载过,没加载需要加载并存入字典if (!abDic.ContainsKey(strs[i]))abDic.Add(strs[i], AssetBundle.LoadFromFile(PathUrl + 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);//返回加载的资源return abDic[abName].LoadAsset(resName);}//同步加载 根据type指定类型public Object LoadRes(string abName, string resName,System.Type type){LoadAB(abName);//返回加载的资源return abDic[abName].LoadAsset(resName,type);}//同步加载 根据泛型指定类型public T LoadRes<T>(string abName, string resName) where T : Object{LoadAB(abName);//返回加载的资源return abDic[abName].LoadAsset<T>(resName);}//异步加载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);AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);yield return abr;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);AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName,type);yield return abr;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) where T: Object{LoadAB(abName);AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);yield return abr;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.UnloadAllAssetBundles(false);abDic.Clear();mainAB = null;manifest = null;}}
测试代码:
ABMgr.Instance.LoadResAsync("model", "Cube", (obj) =>{Instantiate(obj, Vector3.up, Quaternion.identity);});ABMgr.Instance.LoadResAsync("model", "Cube", (obj2) =>{Instantiate(obj2, -Vector3.up, Quaternion.identity);});ABMgr.Instance.LoadResAsync("model", "Cube", (obj3) =>{Instantiate(obj3, Vector3.right, Quaternion.identity);});
结果