/// <summary> /// 异步资源加载,外部直接调用,(仅仅加载不需要实例化的资源,例如Texture和音频之类的) /// </summary> public void AsyncLoadResource(string path, OnAsyncFinish dealFinish, LoadResPriority priority, bool isSprite = false, uint crc = 0, params object[] paramList) { if (crc == 0) { crc = Crc32.GetCrc32(path); } AssetBundleInfo abInfo = GetCacheAssetBundleInfo(crc); if (abInfo != null) { dealFinish?.Invoke(path, abInfo.m_Obj, paramList); return; } //判断是否在加载中 AsyncLoadResUnit unit = null; //没有找到这个异步加载单位,或者这个异步加载单位是空 if (!m_LoadingAssetDic.TryGetValue(crc, out unit) || unit == null) { unit = m_AsyncLoadResParamPool.Spawn(true); unit.m_Crc = crc; unit.m_Path = path; unit.m_Sprite = isSprite; unit.m_Priority = priority; m_LoadingAssetDic.Add(crc, unit); //添加到正在异步加载的资源dic中 m_loadingAssetList[(int)priority].Add(unit); //按照加载优先级,添加到对应的正在异步加载的资源列表中 } //往回调列表里面添加回调 AsynCallBack callBack = m_AsynCallBackPool.Spawn(true); callBack.m_DealFinish = dealFinish; callBack.m_Params = paramList; //往这个异步加载单位的回调列表中添加一个回调 //可能多个地方加载同一份资源,这样做只加载一次资源, //加载完了后,根据回调列表一次返回这份资源 unit.m_CallBackList.Add(callBack); }
/// <summary> /// 释放一个资源块 /// </summary> /// <param name="abInfo"></param> public void ReleaseAssetBundle(AssetBundleInfo abInfo) { if (abInfo == null) { return; } //先卸载依赖资源 if (abInfo.m_DependceAssetBundle != null && abInfo.m_DependceAssetBundle.Count > 0) { for (int i = 0; i < abInfo.m_DependceAssetBundle.Count; i++) { UnloadAssetBundle(abInfo.m_DependceAssetBundle[i]); } } //再卸载这个资源 UnloadAssetBundle(abInfo.m_ABName); }
/// <summary> /// 异步资源加载,针对ObjectManager的,(需要实例化对象的异步加载) /// </summary> public void AsyncLoadResource(string path, ResourceObj resObj, OnAsyncResObjFinish dealFinish, LoadResPriority priority, uint crc = 0, params object[] paramList) { AssetBundleInfo abInfo = GetCacheAssetBundleInfo(resObj.m_Crc); if (abInfo != null) { resObj.m_AssetBundleInfo = abInfo; if (dealFinish != null) { dealFinish(path, resObj, paramList); } return; } //判断是否在加载中 AsyncLoadResUnit unit = null; //没有找到这个异步加载单位,或者这个异步加载单位是空 if (!m_LoadingAssetDic.TryGetValue(resObj.m_Crc, out unit) || unit == null) { unit = m_AsyncLoadResParamPool.Spawn(true); unit.m_Crc = resObj.m_Crc; unit.m_Path = path; unit.m_Priority = priority; m_LoadingAssetDic.Add(resObj.m_Crc, unit); //添加到正在异步加载的资源dic中 m_loadingAssetList[(int)priority].Add(unit); //按照加载优先级,添加到对应的正在异步加载的资源列表中 } //往回调列表里面添加回调 AsynCallBack callBack = m_AsynCallBackPool.Spawn(true); callBack.m_Params = paramList; callBack.m_DealResObjFinish = dealFinish; callBack.m_ResObj = resObj; //往这个异步加载单位的回调列表中添加一个回调 //可能多个地方加载同一份资源,这样做只加载一次资源, //加载完了后,根据回调列表依次返回这份资源 unit.m_CallBackList.Add(callBack); }
/// <summary> /// 回收一个资源 /// </summary> /// <param name="abInfo"></param> /// <param name="destroy"></param> protected void DestoryResouceItem(AssetBundleInfo abInfo, bool destroyCache = false) { //资源是空的,或者正在被引用不能清除 if (abInfo == null || abInfo.RefCount > 0) { return; } //设置的不清除缓存,就加入到双向链表里面 if (!destroyCache) { m_NoRefrenceAssetMapList.InsetToHead(abInfo); return; } //AssetDic中删除这个资源 if (!AssetDic.Remove(abInfo.m_Crc)) { return; } //从双向链表中清除这个资源 m_NoRefrenceAssetMapList.Remvoe(abInfo); //释放AssetBundle引用 AssetBundleManager.Instance.ReleaseAssetBundle(abInfo); //清空资源对应的对象池 ObjectManager.Instance.ClearPoolObject(abInfo.m_Crc); if (abInfo.m_Obj != null) { #if UNITY_EDITOR Resources.UnloadUnusedAssets(); #endif abInfo.m_Obj = null; //编辑器下的卸载要用这个方法才能从内存卸载 } }
/// <summary> /// 加载AssetBundle,存储到AssetBundleInfo.m_AssetBundle中, /// </summary> /// <param name="crc">AssetBundle的crc标记</param> /// <returns></returns> public AssetBundleInfo LoadAssetBundleInfo(uint crc) { AssetBundleInfo abInfo = null; if (!m_AssetBundleInfoDic.TryGetValue(crc, out abInfo) || abInfo == null) { Debug.LogError("bundle字典中没有找到item,或者找到了item是空,crc :" + crc); return(abInfo); } //加载该资源块中的资源 abInfo.m_AssetBundle = LoadAssetBundle(abInfo.m_ABName); //加载该资源块中的资源所依赖的资源 if (abInfo.m_DependceAssetBundle != null) { for (int i = 0; i < abInfo.m_DependceAssetBundle.Count; i++) { LoadAssetBundle(abInfo.m_DependceAssetBundle[i]); } } return(abInfo); }
/// <summary> /// 加载AB配置表 /// </summary> /// <returns></returns> public bool LoadAssetBundleConfig(bool isInit = true) { AssetBundleConfig abConfig = null; #if UNITY_EDITOR if (ResourceManager.Instance.m_LoadFromAssetBundle) { #endif //初始化时候的ab配置文件在streamingAssets文件夹中 string configPath = Application.streamingAssetsPath + "/" + m_ABConfigABName; //非初始化,检查热更后进来的 if (!isInit) { //热更完毕后检查配置文件是否热更了 string hotABPath = HotPatchManager.Instance.ComputeABPath(m_ABConfigABName); if (string.IsNullOrEmpty(hotABPath)) { return(true); } else { //文件改变了,卸载本地的ab包,从热更路径下下载新的ab包 if (configAB != null) { configAB.Unload(true); configPath = hotABPath; } } } m_AssetBundleInfoDic.Clear(); //加密后的ab包要先解密,才能加载 if (Encrypt) { byte[] bytes = AES.AESFileByteDecrypt(configPath, FrameConstr.m_ABSecretKey); configAB = AssetBundle.LoadFromMemory(bytes); } else { configAB = AssetBundle.LoadFromFile(configPath); } TextAsset textAsset = configAB.LoadAsset <TextAsset>(m_ABConfigABName); if (textAsset == null) { Debug.LogError("AssetBundleConfig is no exist!"); return(false); } //创建一个内存流 MemoryStream stream = new MemoryStream(textAsset.bytes); //二进制序列化对象 BinaryFormatter bf = new BinaryFormatter(); abConfig = (AssetBundleConfig)bf.Deserialize(stream); //关闭内存流 stream.Close(); #if UNITY_EDITOR } else { m_AssetBundleInfoDic.Clear(); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(Application.dataPath + "/AssetBundleConfig.xml"); XmlSerializer xs = new XmlSerializer(typeof(AssetBundleConfig)); StringReader sr = new StringReader(xmlDoc.InnerXml); abConfig = (AssetBundleConfig)xs.Deserialize(sr); } #endif for (int i = 0; i < abConfig.ABList.Count; ++i) { ABBase abBase = abConfig.ABList[i]; AssetBundleInfo abInfo = new AssetBundleInfo(); abInfo.m_Crc = abBase.Crc; abInfo.m_AssetName = abBase.AssetName; abInfo.m_ABName = abBase.ABName; abInfo.m_DependceAssetBundle = abBase.ABDependce; if (m_AssetBundleInfoDic.ContainsKey(abInfo.m_Crc)) { Debug.LogError("重复的Crc : 资源名:" + abInfo.m_AssetName + " ab包名:" + abInfo.m_ABName); } else { m_AssetBundleInfoDic.Add(abInfo.m_Crc, abInfo); } } return(true); }
/// <summary> /// 异步加载 /// </summary> /// <returns></returns> IEnumerator AsyncLoadCor() { List <AsynCallBack> callBackList = null; while (true) { bool haveYield = false; //是否已经等了一帧了 //上次yield的时间 long lastYiledTime = System.DateTime.Now.Ticks; //遍历优先级列表,从0开始,0表示最高 for (int i = 0; i < (int)LoadResPriority.RES_NUM; i++) { //每次循环,只要高级列表有东西,就一直加载高级列表资源,直到加载完毕 if (m_loadingAssetList[(int)LoadResPriority.RES_HIGHT].Count > 0) { i = (int)LoadResPriority.RES_HIGHT; } else if (m_loadingAssetList[(int)LoadResPriority.RES_MIDDLE].Count > 0) { i = (int)LoadResPriority.RES_MIDDLE; } List <AsyncLoadResUnit> loadingList = m_loadingAssetList[i]; if (loadingList.Count <= 0) { continue; } AsyncLoadResUnit loadintItem = loadingList[0]; loadingList.RemoveAt(0); callBackList = loadintItem.m_CallBackList; Object obj = null; AssetBundleInfo abInfo = null; #if UNITY_EDITOR if (!m_LoadFromAssetBundle) { if (loadintItem.m_Sprite) //判断是否是Sprite,因为Unity Object不能转换成Sprite { obj = LoadAssetByEditor <Sprite>(loadintItem.m_Path); } else { obj = LoadAssetByEditor <Object>(loadintItem.m_Path); } //编辑器下模拟异步加载,等0.1f yield return(new WaitForSeconds(0.1f)); abInfo = AssetBundleManager.Instance.FindAssetBundleInfo(loadintItem.m_Crc); if (abInfo == null) { abInfo = new AssetBundleInfo(); abInfo.m_Crc = loadintItem.m_Crc; } } #endif if (obj == null) { abInfo = AssetBundleManager.Instance.LoadAssetBundleInfoAsync(loadintItem.m_Crc); yield return(abInfo); if (abInfo.isDone) { if (abInfo != null && abInfo.m_AssetBundle != null) { AssetBundleRequest abRequest = null; if (loadintItem.m_Sprite) //判断是否是Sprite,因为Unity Object不能转换成Sprite { abRequest = abInfo.m_AssetBundle.LoadAssetAsync <Sprite>(abInfo.m_AssetName); } else { abRequest = abInfo.m_AssetBundle.LoadAssetAsync(abInfo.m_AssetName); } yield return(abRequest); if (abRequest.isDone) { obj = abRequest.asset; } lastYiledTime = System.DateTime.Now.Ticks; } } } CacheAssetBundleInfo(loadintItem.m_Path, ref abInfo, loadintItem.m_Crc, obj, callBackList.Count); //处理回调 for (int j = 0; j < callBackList.Count; j++) { AsynCallBack callBack = callBackList[j]; //实例化对象的回调 if (callBack != null && callBack.m_DealResObjFinish != null && callBack.m_ResObj != null) { ResourceObj tempResObj = callBack.m_ResObj; tempResObj.m_AssetBundleInfo = abInfo; callBack.m_DealResObjFinish(loadintItem.m_Path, tempResObj, callBack.m_Params); callBack.m_DealResObjFinish = null; tempResObj = null; } //非实例化对象的回调 if (callBack != null && callBack.m_DealFinish != null) { callBack.m_DealFinish(loadintItem.m_Path, obj, callBack.m_Params); callBack.m_DealFinish = null; } callBack.Reset(); //还原回调,并且回收 m_AsynCallBackPool.Recycle(callBack); } obj = null; callBackList.Clear(); m_LoadingAssetDic.Remove(loadintItem.m_Crc); loadintItem.Reset(); m_AsyncLoadResParamPool.Recycle(loadintItem); //加载一个资源时间过长,等一帧 if (System.DateTime.Now.Ticks - lastYiledTime > MAXLOADRESTIME) { yield return(null); lastYiledTime = System.DateTime.Now.Ticks; haveYield = true; } } //内存循环加载很快,没有等一帧,但是加载整个优先级列表时间过长,等一帧 if (!haveYield || System.DateTime.Now.Ticks - lastYiledTime > MAXLOADRESTIME) { lastYiledTime = System.DateTime.Now.Ticks; yield return(null); } } }
/// <summary> /// 同步加载资源,针对给ObjectManager的接口 /// 给ResourceObj.m_AssetBundleInfo赋值用的 /// </summary> /// <param name="path"></param> /// <param name="resObj">传入的resourceObj,</param> /// <returns></returns> public ResourceObj LoadResource(string path, ResourceObj resObj) { if (resObj == null) { return(null); } //获取crc uint crc = resObj.m_Crc == 0 ? Crc32.GetCrc32(path) : resObj.m_Crc; //根据crc从缓存中获取 AssetBundle资源块信息 AssetBundleInfo abInfo = GetCacheAssetBundleInfo(crc); if (abInfo != null) { resObj.m_AssetBundleInfo = abInfo; return(resObj); } Object obj = null; #if UNITY_EDITOR if (!m_LoadFromAssetBundle) { abInfo = AssetBundleManager.Instance.FindAssetBundleInfo(crc); if (abInfo != null && abInfo.m_Obj != null) { obj = abInfo.m_Obj; } else { if (abInfo == null) { abInfo = new AssetBundleInfo(); abInfo.m_Crc = crc; } obj = LoadAssetByEditor <Object>(path); } } #endif //缓存中没有AssetBundle资源块信息,加载它 if (obj == null) { abInfo = AssetBundleManager.Instance.LoadAssetBundleInfo(crc); if (abInfo != null && abInfo.m_AssetBundle != null) { if (abInfo.m_Obj != null) { obj = abInfo.m_Obj; } else { obj = abInfo.m_AssetBundle.LoadAsset <Object>(abInfo.m_AssetName); } } } //将加载的资源放入缓存中 CacheAssetBundleInfo(path, ref abInfo, crc, obj); resObj.m_AssetBundleInfo = abInfo; abInfo.m_Clear = resObj.m_bClear; return(resObj); }
/// <summary> /// 预加载资源,仅仅加载不实例化 /// </summary> /// <param name="path">资源路径</param> public void PreloadRes(string path) { if (string.IsNullOrEmpty(path)) { return; } uint crc = Crc32.GetCrc32(path); //根据crc从缓存中获取一个AssetBundleInfo AssetBundleInfo abInfo = GetCacheAssetBundleInfo(crc, 0); if (abInfo != null) { return; } Object obj = null; #if UNITY_EDITOR if (!m_LoadFromAssetBundle) { abInfo = AssetBundleManager.Instance.FindAssetBundleInfo(crc); if (abInfo.m_Obj != null) { obj = abInfo.m_Obj as Object; } else { if (abInfo == null) { abInfo = new AssetBundleInfo(); abInfo.m_Crc = crc; } obj = LoadAssetByEditor <Object>(path); } } #endif if (obj == null) { abInfo = AssetBundleManager.Instance.LoadAssetBundleInfo(crc); if (abInfo != null && abInfo.m_AssetBundle != null) { if (abInfo.m_Obj != null) { obj = abInfo.m_Obj as Object; } else { obj = abInfo.m_AssetBundle.LoadAsset <Object>(abInfo.m_AssetName); } } } //缓存AssetBundle资源块 CacheAssetBundleInfo(path, ref abInfo, crc, obj); //设置跳场景不清空缓存 abInfo.m_Clear = false; //卸载资源设置成不销毁 ReleaseResource(path, false); //上面的步骤就是提前加载一次资源,然后缓存AssetBundle资源块, //再将资源块设置成跳场景不清空缓存,预加载资源后应该就是跳场景进入游戏了 //最后释放掉资源设置成不销毁,存到双向链表里面 }