/// <summary> /// 同步加载资源 /// </summary> private void loadAssetSync() { //通过资源名(即AB名)定位相关资源 var assetspath = AssetDatabase.GetAssetPathsFromAssetBundle(AssetBundleName); if (assetspath.Length == 0) { ResourceLogger.logErr(string.Format("找不到资源名 : {0}的资源!", AssetBundleName)); } else { mResourceInfo = createAssetDatabaseInfo(AssetBundleName, assetspath); mResourceInfo.updateLastUsedTime(); } ///AssetDatabase模式下没有依赖资源的概念, ///所以一口气回调资源加载任务完成和上层逻辑回调 LoadState = ResourceLoadState.SelfComplete; LoadSelfResourceCompleteNotifier(this); LoadSelfResourceCompleteNotifier = null; LoadState = ResourceLoadState.AllComplete; mResourceInfo.mIsReady = true; LoadResourceCompleteCallBack(mResourceInfo); LoadResourceCompleteCallBack = null; // 资源加载完成后,AssetDatabaseLoader的任务就完成了,回收重用 AssetDatabaseLoaderFactory.recycle(this); }
/// <summary> /// AB加载携程 /// </summary> /// <returns></returns> private IEnumerator assetBundleLoadAsync() { while (true) { if (ABAsyncQueue.Count > 0) { CurrentLoadingAssetBundleLoader = ABAsyncQueue.Dequeue(); //检查是否已经同步加载完成 //如果异步加载AB时,同步请求来了,打断异步后续逻辑 //LoadState == ResourceLoadState.None表明同步加载该资源已经完成,无需再异步返回 if (CurrentLoadingAssetBundleLoader.LoadState == ResourceLoadState.None) { //ResourceLogger.logWar("有资源还未开始异步加载就被同步加载打断!"); } else { CurrentLoadingAssetBundleLoader.LoadState = ResourceLoadState.Loading; var abname = CurrentLoadingAssetBundleLoader.AssetBundleName; var abpath = AssetBundlePath.GetABLoadFullPath(abname); AssetBundleCreateRequest abrequest = null; #if UNITY_EDITOR //因为资源不全,很多资源丢失,导致直接报错 //这里临时先在Editor模式下判定下文件是否存在,避免AssetBundle.LoadFromFileAsync()直接报错 if (System.IO.File.Exists(abpath)) { abrequest = AssetBundle.LoadFromFileAsync(abpath); } else { Debug.LogError(string.Format("AB : {0}文件不存在!", CurrentLoadingAssetBundleLoader.AssetBundleName)); } #else abrequest = AssetBundle.LoadFromFileAsync(abpath); #endif yield return(abrequest); //如果异步加载AB时,同步请求来了,打断异步后续逻辑 //LoadState == ResourceLoadState.None表明同步加载该资源已经完成,无需再异步返回 if (CurrentLoadingAssetBundleLoader.LoadState == ResourceLoadState.None) { ResourceLogger.log(string.Format("资源 : {0}加载已完成,异步加载被打断!", abname)); } else { var assetbundle = abrequest.assetBundle; if (assetbundle == null) { ResourceLogger.logErr(string.Format("Failed to load AssetBundle : {0}!", CurrentLoadingAssetBundleLoader.AssetBundleName)); } CurrentLoadingAssetBundleLoader.onSelfABLoadComplete(assetbundle); } } CurrentLoadingAssetBundleLoader = null; } else { yield return(null); } } }
/// <summary> /// 同步加载资源 /// </summary> private void loadAssetSync() { if (string.IsNullOrEmpty(AssetBundlePath)) { ResourceLogger.logErr(string.Format("找不到资源名 : {0}的资源!", AssetBundlePath)); } else { mResourceInfo = createAssetDatabaseInfo(AssetBundlePath); mResourceInfo.updateLastUsedTime(); } ///AssetDatabase模式下没有依赖资源的概念, ///所以一口气回调资源加载任务完成和上层逻辑回调 LoadState = ResourceLoadState.SelfComplete; LoadSelfResourceCompleteNotifier(this); LoadSelfResourceCompleteNotifier = null; LoadState = ResourceLoadState.AllComplete; mResourceInfo.mIsReady = true; LoadResourceCompleteCallBack(mResourceInfo); LoadResourceCompleteCallBack = null; // 资源加载完成后,AssetDatabaseLoader的任务就完成了,回收重用 AssetDatabaseLoaderFactory.recycle(this); }
public override T loadAsset <T>(string assetname) { if (mIsReady) { if (mLoadedAssetMap.ContainsKey(assetname)) { return(mLoadedAssetMap[assetname] as T); } else { var assetpath = $"{AssetBundlePath}/{assetname}"; T asset = AssetDatabase.LoadAssetAtPath <T>(assetpath); if (asset != null) { mLoadedAssetMap.Add(assetname, asset); return(asset); } else { ResourceLogger.logErr(string.Format("找不到符合类型 : {0},资源名: {1},Asset : {2}资源!", typeof(T).GetType(), AssetBundlePath, assetname)); return(null); } } } else { ResourceLogger.logErr(string.Format("异常状态,AB资源:{0}未就绪就请求Asset资源:{1}", AssetBundlePath, assetname)); return(null); } }
/// <summary> /// 加载所有Asset(比如Shader预加载) /// Note: /// 加载图集使用loadAllAsset<Sprite>()或者loadAsset<Sprite> /// </summary> /// <param name="assetname"></param> /// <returns></returns> public override void loadAllAsset <T>() { if (mIsReady) { if (!mIsAllAssetLoaded) { foreach (var assetpath in AssetsPath) { var allassets = AssetDatabase.LoadAllAssetsAtPath(assetpath); foreach (var asset in allassets) { var assetname = asset.name; if (!mLoadedAssetMap.ContainsKey(assetname)) { mLoadedAssetMap.Add(assetname, asset); } else { ResourceLogger.logErr(string.Format("资源名 : {0}里有同名资源!Asset资源 : {1}添加失败!请优化取名,避免重名Asset!", AssetBundleName, assetname)); } } } mIsAllAssetLoaded = true; } } }
/// <summary> /// 开始资源加载任务 /// </summary> public void startLoad() { if (LoadState == ResourceLoadState.None) { LoadState = ResourceLoadState.Waiting; loadAsset(); } else if (LoadState == ResourceLoadState.Waiting) { ResourceLogger.logErr(string.Format("AB : {0}处于等待加载中状态,不应该再被调用startLoad,请检查资源加载是否异常!", AssetBundleName)); } else if (LoadState == ResourceLoadState.Loading) { ResourceLogger.logErr(string.Format("AB : {0}处于加载中状态,不应该再被调用startLoad,请检查资源加载是否异常!", AssetBundleName)); } else if (LoadState == ResourceLoadState.SelfComplete) { ResourceLogger.logErr(string.Format("AB : {0}已经处于自身加载完成状态,不应该再被调用startLoad,请检查资源加载是否异常!", AssetBundleName)); } else if (LoadState == ResourceLoadState.AllComplete) { ResourceLogger.logErr(string.Format("AB : {0}已经处于自身以及依赖AB加载完成状态,不应该再被调用startLoad,请检查资源加载是否异常!", AssetBundleName)); } else if (LoadState == ResourceLoadState.Error) { ResourceLogger.logErr(string.Format("AB:{0}处于Error状态,无法加载!", AssetBundleName)); } }
/// <summary> /// 强制卸载指定资源(不管AB加载后属于哪一种类型,强制卸载) /// </summary> /// <param name="abname"></param> public void forceUnloadSpecificResource(string abname) { ResourceLoadType resourceloadtype = ResourceLoadType.NormalLoad; AbstractResourceInfo arinfo = null; foreach (var loadedabinfomap in mAllLoadedResourceInfoMap) { if (arinfo != null) { break; } foreach (var loadedabinfo in loadedabinfomap.Value) { if (loadedabinfo.Key.Equals(abname)) { arinfo = loadedabinfo.Value; resourceloadtype = loadedabinfomap.Key; break; } } } if (arinfo != null) { mAllLoadedResourceInfoMap[resourceloadtype].Remove(arinfo.AssetBundleName); arinfo.dispose(); ResourceLogger.log(string.Format("AB资源 : {0}已强制卸载!", abname)); } else { ResourceLogger.logErr(string.Format("AB资源 : {0}未被加载,无法强制卸载!", abname)); } }
/// <summary> /// 卸载指定类型不再使用的资源(Note:不支持卸载常驻资源类型) /// </summary> /// <param name="resourceloadtype">资源加载类型</param> protected void unloadSpecificLoadTypeUnsedResource(ResourceLoadType resourceloadtype) { if (resourceloadtype == ResourceLoadType.PermanentLoad) { ResourceLogger.logErr("不允许卸载常驻AB资源!"); return; } doUnloadSpecificLoadTypeUnsedResource(resourceloadtype); }
/// <summary> /// 添加指定资源到白名单 /// Note: /// 默认白名单里的资源都以ResourceLoadType.Preload方式加载 /// </summary> /// <param name="resname">资源名(既AB名)</param> public void addToWhiteList(string resname) { if (!mResourceWhileListMap.ContainsKey(resname)) { mResourceWhileListMap.Add(resname, resname); } else { ResourceLogger.logErr(string.Format("资源 : {0}重复添加白名单!", resname)); } }
/// <summary> /// 异步加载任务入队列 /// </summary> /// <param name="abl"></param> public static void enqueue(AssetBundleLoader abl) { if (abl.LoadMethod == ResourceLoadMethod.Async) { ABAsyncQueue.Enqueue(abl); } else { ResourceLogger.logErr(string.Format("严重错误,同步加载资源 : {0} 不应该添加到异步加载队列里!", abl.AssetBundleName)); } }
/// <summary> /// 启动AB异步加载任务携程 /// </summary> public void startABAsyncLoad() { if (IsLoadStart == false) { CoroutineManager.Singleton.StartCoroutine(assetBundleLoadAsync()); IsLoadStart = true; } else { ResourceLogger.logErr("AB异步加载任务携程已经启动!不能重复开启!"); } }
/// <summary> /// 获取指定加载类型的已加载资源信息映射Map /// </summary> /// <param name="loadtype">资源加载类型</param> /// <returns></returns> public Dictionary <string, AbstractResourceInfo> getSpecificLoadTypeARIMap(ResourceLoadType loadtype) { if (mAllLoadedResourceInfoMap.ContainsKey(loadtype)) { return(mAllLoadedResourceInfoMap[loadtype]); } else { ResourceLogger.logErr(string.Format("找不到资源类型 : {0}的已加载AB信息!", loadtype)); return(null); } }
/// <summary> /// 开始资源加载卸载统计 /// </summary> public void startResourceLoadAnalyse() { if (ResourceLoadAnalyseSwitch) { ResourceLoadAnalyseStart = true; ResourceLoadAnalyseMap.Clear(); ResourceLogger.log("开启AB加载统计!"); } else { ResourceLogger.logErr("请先开启AB资源加载分析开关!"); } }
/// <summary> /// 更新已加载AB的加载类型 /// </summary> /// <param name="resname">资源名</param> /// <param name="oldloadtype">旧的加载类型</param> /// <param name="newloadtype">新的家在类型</param> protected void updateLoadedResourceInfoLoadType(string resname, ResourceLoadType oldloadtype, ResourceLoadType newloadtype) { if (mAllLoadedResourceInfoMap[oldloadtype].ContainsKey(resname)) { var abi = mAllLoadedResourceInfoMap[oldloadtype][resname]; mAllLoadedResourceInfoMap[newloadtype].Add(resname, abi); mAllLoadedResourceInfoMap[oldloadtype].Remove(resname); ResourceLogger.log(string.Format("已加载的资源 : {0}从资源类型 : {1}更新到资源类型 : {2}!", resname, oldloadtype, newloadtype)); } else { ResourceLogger.logErr(string.Format("资源类型 : {0}里找不到已加载的资源 : {1},无法更新该资源的加载类型!", oldloadtype, resname)); } }
/// <summary> AB加载完成通知(用于更新AB加载管理) /// </summary> /// <param name="abl">AB加载任务信息</param> private void onABLoadCompleteNotifier(AssetBundleLoader abl) { var abname = abl.AssetBundleName; if (mABRequestTaskMap.ContainsKey(abname)) { mABRequestTaskMap.Remove(abname); mAllLoadedResourceInfoMap[abl.LoadType].Add(abname, abl.ABInfo); //AB加载数据统计 if (ResourceLoadAnalyse.Singleton.ResourceLoadAnalyseSwitch) { ResourceLoadAnalyse.Singleton.addResourceLoadedTime(abname); } } else { ResourceLogger.logErr(string.Format("收到不在加载任务请求队列里的AB:{0}加载完成回调!", abname)); } }
/// <summary> /// 为AB添加指定owner的引用 /// 所有owner都销毁则ab引用计数归零可回收 /// </summary> /// <param name="owner"></param> protected void retainOwner(UnityEngine.Object owner) { if (owner == null) { ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}添加引用!", AssetBundlePath)); return; } foreach (var referenceowner in mReferenceOwnerList) { if (owner.Equals(referenceowner)) { return; } } System.WeakReference wr = new System.WeakReference(owner); mReferenceOwnerList.Add(wr); }
/// <summary> /// 实例化指定Asset(上层获取并绑定Asset实例化GameObject对象接口) /// </summary> /// <param name="assetname"></param> /// <returns></returns> public override GameObject instantiateAsset(string assetname) { var goasset = loadAsset <GameObject>(assetname); if (goasset != null) { var goinstance = GameObject.Instantiate <GameObject>(goasset); //不修改实例化后的名字,避免上层逻辑名字对不上 //goinstance.name = goasset.name; // 绑定owner对象,用于判定是否还有有效对象引用AB资源 retainOwner(goinstance); updateLastUsedTime(); return(goinstance); } else { ResourceLogger.logErr(string.Format("AB:{0}里加载GameObject Asset:{1}失败!", AssetBundleName, assetname)); return(null); } }
/// <summary> /// 停止资源加载卸载统计并输出 /// </summary> public void endResourceLoadAnalyse() { if (ResourceLoadAnalyseSwitch) { if (ResourceLoadAnalyseStart) { ResourceLoadAnalyseStart = false; outputResourceLoadedInfoDetail(); ResourceLoadAnalyseMap.Clear(); ResourceLogger.log("结束资源加载统计!"); } else { ResourceLogger.logErr("请先启动资源加载统计!"); } } else { ResourceLogger.logErr("请先开启资源资源加载分析开关!"); } }
/// <summary> /// 移除指定拥有者绑定(用于解决上层绑定对象一直存在导致资源无法释放的问题) /// </summary> /// <param name="owner"></param> /// <returns></returns> public bool releaseOwner(UnityEngine.Object owner) { if (owner == null) { ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}解除绑定!", AssetBundlePath)); return(false); } var ownerindex = mReferenceOwnerList.FindIndex((ow) => ow.Target.Equals(owner)); if (ownerindex != -1) { ResourceLogger.log(string.Format("资源:{0}找到指定绑定对象:{1},解除绑定!", AssetBundlePath, owner)); mReferenceOwnerList.RemoveAt(ownerindex); return(true); } else { ResourceLogger.log(string.Format("资源:{0}找不到指定绑定对象:{1},解除绑定失败!", AssetBundlePath, owner)); return(false); } }
/// <summary> /// 资源请求携程 /// </summary> /// <returns></returns> private IEnumerator resourcesRequest() { foreach (var hotupdateres in mTestHotUpdateResourceList) { var resurl = TestResourceURL + hotupdateres; ResourceLogger.log(string.Format("下载资源 : {0}", resurl)); var webrequest = UnityWebRequest.Get(resurl); yield return(webrequest.SendWebRequest()); if (webrequest.isNetworkError) { ResourceLogger.logErr(string.Format("{0}资源下载出错!", hotupdateres)); ResourceLogger.logErr(webrequest.error); } else { if (webrequest.isDone) { ResourceLogger.log(string.Format("{0}资源下载完成!", hotupdateres)); var data = webrequest.downloadHandler.data; //检查包外是否存在同名资源,存在的话需要先删除再存储最新到包外 var outterabfullpath = AssetBundlePath.ABHotUpdatePath + hotupdateres; if (AssetBundlePath.IsABExitInOutterPath(hotupdateres)) { ResourceLogger.log(string.Format("删除包外资源 : {0}", hotupdateres)); File.Delete(outterabfullpath); } using (var fs = File.Create(outterabfullpath)) { fs.Write(data, 0, data.Length); fs.Flush(); fs.Close(); ResourceLogger.log(string.Format("包外资源 : {0}写入完成!", hotupdateres)); } } } } }
/// <summary> /// 加载所有Asset(比如Shader预加载) /// Note: /// 加载图集使用loadAllAsset<Sprite>()或者loadAsset<Sprite> /// </summary> /// <param name="assetname"></param> /// <returns></returns> public override void loadAllAsset <T>() { if (mIsReady) { if (!mIsAllAssetLoaded) { var allassets = Bundle.LoadAllAssets <T>(); foreach (var asset in allassets) { var assetname = asset.name; if (!mLoadedAssetMap.ContainsKey(assetname)) { mLoadedAssetMap.Add(assetname, asset); } else { ResourceLogger.logErr(string.Format("资源名 : {0}里有同名资源!Asset资源 : {1}添加失败!", AssetBundlePath, assetname)); } } mIsAllAssetLoaded = true; } } }
/// <summary> /// 获取并绑定指定Asste资源(上层获取并绑定特定类型Asset的接口) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="owner"></param> /// <param name="assetname"></param> /// <returns></returns> public override T getAsset <T>(UnityEngine.Object owner, string assetname) { if (owner != null) { var asset = loadAsset <T>(assetname); if (asset != null) { // 绑定owner对象,用于判定是否还有有效对象引用AB资源 retainOwner(owner); updateLastUsedTime(); return(asset); } else { ResourceLogger.logWar(string.Format("AB : {0}里不存在Asset : {1},获取Asset失败!", AssetBundleName, assetname)); return(null); } } else { ResourceLogger.logErr(string.Format("不能绑定Asset到空对象上!加载AB:{0} Asset:{1}失败!", AssetBundleName, assetname)); return(null); } }
/// <summary> /// 加载所有Asset(比如Shader预加载) /// Note: /// 加载图集使用loadAllAsset<Sprite>()或者loadAsset<Sprite> /// </summary> /// <param name="assetname"></param> /// <returns></returns> public override void loadAllAsset <T>() { if (mIsReady) { if (!mIsAllAssetLoaded) { foreach (var assetpath in AssetsPath) { var allassets = AssetDatabase.LoadAllAssetsAtPath(assetpath); foreach (var asset in allassets) { var assetname = asset.name; if (!mLoadedAssetMap.ContainsKey(assetname)) { mLoadedAssetMap.Add(assetname, asset); } else { if (typeof(T) == typeof(Sprite)) { // 暂时用重复的覆盖老(Sprite覆盖Texture2D)的(解决单张图作为Sprite时,Texture2D和Sprite同名的问题) // 问题: // 1. 此方式不会缓存图集的Texture2D(暂时可以通过sprite.Texture的方式访问Texture2D) mLoadedAssetMap[assetname] = asset; } else { ResourceLogger.logErr(string.Format("资源名 : {0}里有同名资源!Asset资源 : {1}添加失败!请优化取名,避免重名Asset!", AssetBundleName, assetname)); } } } } mIsAllAssetLoaded = true; } } }
public override T loadAsset <T>(string assetname) { if (mIsReady) { if (mLoadedAssetMap.ContainsKey(assetname)) { return(mLoadedAssetMap[assetname] as T); } else { if (AssetsPath == null) { ResourceLogger.logErr(string.Format("资源名 : {0}资源丢失,不存在!", AssetBundleName)); return(null); } else { // 图集需要全部加载才能加载到指定Sprite // 暂时通过加载AssetBundle里所有的资源来加载查找指定sprite if (typeof(T) == typeof(Sprite)) { loadAllAsset <T>(); if (mLoadedAssetMap.ContainsKey(assetname)) { return(mLoadedAssetMap[assetname] as T); } else { ResourceLogger.logErr(string.Format("找不到资源名 : {0}里 Asset资源 : {1}!", AssetBundleName, assetname)); return(null); } } else { var assetpathes = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(AssetBundleName, assetname); if (assetpathes.Length == 1) { T asset = AssetDatabase.LoadAssetAtPath <T>(assetpathes[0]); if (asset != null) { mLoadedAssetMap.Add(assetname, asset); return(asset); } else { ResourceLogger.logErr(string.Format("找不到符合类型 : {0},资源名: {1},Asset : {2}资源!", typeof(T).GetType(), AssetBundleName, assetname)); return(null); } } else if (assetpathes.Length > 1) { ResourceLogger.logErr(string.Format("资源名 : {0}里存在同名Asset : {}资源!请先纠正Asset取名!", AssetBundleName, assetname)); return(null); } else { ResourceLogger.logErr(string.Format("找不到资源名: {0},Asset : {1}资源!", AssetBundleName, assetname)); return(null); } } } } } else { ResourceLogger.logErr(string.Format("异常状态,AB资源:{0}未就绪就请求Asset资源:{1}", AssetBundleName, assetname)); return(null); } }
/// <summary> /// 加载指定Asset(可用上层访问Asset) /// Note: /// 因为没有绑定对象,仅用于临时访问Asset数据 /// 访问过后无人引用的话会被回收 /// </summary> /// <param name="assetname"></param> /// <returns></returns> public override T loadAsset <T>(string assetname) { if (mIsReady) { if (mLoadedAssetMap.ContainsKey(assetname)) { return(mLoadedAssetMap[assetname] as T); } else { if (Bundle == null) { ResourceLogger.logErr(string.Format("AB : {0}资源丢失,不存在!", AssetBundleName)); return(null); } else { // 图集需要全部加载才能加载到指定Sprite if (typeof(T) == typeof(Sprite)) { if (!mIsAllAssetLoaded) { var allassets = Bundle.LoadAllAssets(); // foreach (var asset in allassets) var count = allassets.Length; for (int i = count - 1; i >= 0; i--) { //Note: //只存储图集的Sprite Asset,不存储Texture2D //现发现Sprite Asset有和Texture2D同名的情况存在 //TODO:解决Sprite Asset和Texture2D同名问题,满足可加载使用图集的Texture2D Asset //暂时未存储Texture2D,要访问图集Texture2D可以通过Sprite.texture的形式访问 var asset = allassets[i]; if (mLoadedAssetMap.ContainsKey(asset.name) && asset is Texture2D) { //ResourceLogger.logErr(string.Format("{0} : AB里存在同名Asset : {1}", AssetBundleName, asset.name)); continue; } //这里把所有Asset都缓存,原来的写法只缓存sprite //if (asset is Sprite) //{ mLoadedAssetMap.Add(asset.name, asset); //} /* * else if (asset is Texture2D) //修改为同时存储 Sprite 和存储Texture2D对应的Sprite * { * mLoadedAssetMap.Add(asset.name.ToLower(), (asset as Texture2D).toSprite()); * } */ } mIsAllAssetLoaded = true; } if (mLoadedAssetMap.ContainsKey(assetname)) { return(mLoadedAssetMap[assetname] as T); } else { return(null); } } else { var asset = Bundle.LoadAsset <T>(assetname); if (asset != null) { mLoadedAssetMap.Add(assetname, asset); return(asset); } else { ResourceLogger.logErr(string.Format("AB:{0}里找不到Asset:{1}资源!", AssetBundleName, assetname)); return(null); } } } } } else { ResourceLogger.logErr(string.Format("异常状态,AB资源:{0}未就绪就请求Asset资源:{1}", AssetBundleName, assetname)); return(null); } }