/// <summary> /// 处理依赖关系 /// </summary> /// <param name="loaded"></param> public override void ProcessDependency(List <ResPackInfo> loaded) { if (string.IsNullOrEmpty(DependencyNames) || loaded == null || loaded.Count == 0) { return; } string[] tags = DependencyNames.Split(','); for (int j = 0; j < tags.Length; j++) { string tag = tags[j]; for (int i = 0; i < loaded.Count; i++) { BundlePackInfo info = loaded[i] as BundlePackInfo; if (info == null) { continue; } if (info.Path.Equals(tag, System.StringComparison.OrdinalIgnoreCase)) { if (Dependencies == null) { Dependencies = new List <BundlePackInfo>(1); } Dependencies.Add(info); } } } if (Dependencies == null) { JW.Common.Log.LogE("Specify DependencyNames {0}, but no dependency instance. bundle:{1}", DependencyNames, Path); } }
public BundleRef(BundlePackInfo info) { _info = info; Path = info.Path; RefCnt = 1; }
/// <summary> /// 卸载资源所属bundle /// </summary> /// <param name="ResObj">资源实例</param> private void UnloadBundle(string path) { BundlePackInfo info = GetResPackInfo(path) as BundlePackInfo; if (info != null) { BundleService.GetInstance().Unload(info); } }
/// <summary> /// 同步加载资源 /// </summary> /// <param name="ResObj">资源实例</param> private void LoadResource(ResObj resource) { //找配置 ResPackInfo packInfo = GetResPackInfo(resource); if (packInfo != null) { if (packInfo.GetPackType() == (byte)ResPackType.ResPackTypeBundle) { BundlePackInfo pi = packInfo as BundlePackInfo; //跟随Resource if (!pi.Outside && pi.IsNoBundle()) { // load from Resources resource.Load(); } else { // load from bundle // load bundle BundleService.GetInstance().LoadSync(pi); // load asset from bundle AssetBundle bundle = BundleService.GetInstance().GetBundle(packInfo.Path); if (bundle != null) { if (string.IsNullOrEmpty(resource.Ext)) { resource.Ext = pi.GetResExt(resource.Path); } resource.Load(bundle); } else { JW.Common.Log.LogE("Loading bundle failed for path:{0}, bundle path:{1}", resource.Path, packInfo.Path); } } } else { //二进制类型 直接根据文件路径 获取 resource.Load(packInfo.Path, packInfo.IsOutside(resource.Path)); } } else { //不从属于任何资源包, 从Resources目录下读取 Editor模式 或者保护为未打包的 resource.Load(); } }
/// <summary> /// 卸载bundle,只减引用计数 /// </summary> /// <param name="pi"></param> public void Unload(BundlePackInfo pi, bool immediately = false) { if (pi == null) { return; } if (pi.Life == EBundleLife.Immediate) { return; } BundleRef br = null; if (_bundleDict.TryGetValue(pi.Path, out br)) { if (pi.Life == EBundleLife.Cache) { if (br.RefCnt > 0) { br.RefCnt--; } else { Log.LogW("Unloading bundle [{0}] while its reference count is zero.", pi.Path); } if (br.RefCnt == 0 && immediately) { UnloadImmediately(br.Path); } } else if (pi.Life == EBundleLife.Resident) { if (br.RefCnt > 1) { br.RefCnt--; } else { JW.Common.Log.LogE("Unloading RESIDENT bundle {0}", br.Path); } } } else { Log.LogW("Unloading bundle which is not exist. path:{0}", pi.Path); } }
/// <summary> /// 取bundle完整路径 /// </summary> /// <param name="pi"></param> /// <returns></returns> string GetBundleFullPath(BundlePackInfo pi, bool withHeader) { #if UNITY_EDITOR && !UNITY_EDITOR_OSX // 在PC上用Windows的Shader包,解决材质丢失问题 if (pi.Path == "Shaders.ab") { if (withHeader) { return(string.Format("file:///{0}/../../IFS/Build/Win/shaders.ab", Application.dataPath)); } else { return(string.Format("{0}/../../IFS/Build/Win/shaders.ab", Application.dataPath)); } } else #endif { if (pi.Outside) { if (withHeader) { return(FileUtil.CombinePath(FileUtil.GetIFSExtractPathWithHeader(), pi.Path)); } else { return(FileUtil.CombinePath(FileUtil.GetIFSExtractPath(), pi.Path)); } } else { if (withHeader) { return(FileUtil.GetStreamingAssetsPathWithHeader(pi.Path)); } else { return(FileUtil.CombinePath(Application.streamingAssetsPath, pi.Path)); } } } }
/// <summary> /// bundle资源已加载完 /// </summary> /// <param name="pi"></param> public void OnAssetLoaded(BundlePackInfo pi) { if (pi == null) { return; } // 立即卸载 (延帧) if (pi.Life == EBundleLife.Immediate) { if (!_unloadingBundle.ContainsKey(pi.Path)) { _unloadingBundle.Add(pi.Path, DeferUnloadData.Get()); } else { JW.Common.Log.LogE("Immediately unload bundle when it's already in unloading queue. bundle:{0}", pi.Path); } } }
/// <summary> /// 异步加载资源 /// </summary> /// <param name="path">资源路径</param> /// <returns></returns> public ResObjRequest GetResourceAsync(string path) { if (string.IsNullOrEmpty(path)) { return(null); } // request ResObjRequest rr = new ResObjRequest(); // complete loading Action <ResObj> complete = (ResObj r) => { // try repaire //if (r.Content is GameObject) //{ // _repaireMachine.Repaire(r.Content as GameObject); //} rr.isDone = true; rr.resource = r; }; // loading progress Action <float> progress = (float prog) => { rr.progress = prog; }; // cache ResObj resource = _resCache.Add(path); if (resource.Content != null) { //cache包含直接返回 complete(resource); return(rr); } // real loading ResPackInfo packInfo = GetResPackInfo(resource); if (packInfo != null) { //打包的资源AssetBundle if (packInfo.GetPackType() == (byte)ResPackType.ResPackTypeBundle) { //AssetBundle 信息 BundlePackInfo pi = packInfo as BundlePackInfo; //补充扩展名 if (string.IsNullOrEmpty(resource.Ext)) { resource.Ext = pi.GetResExt(resource.Path); } // AssetBundle 缓存 AssetBundle bundle = BundleService.GetInstance().GetBundle(packInfo.Path); if (bundle == null) { //不在外且跟随Resource出档了 if (!pi.Outside && pi.IsNoBundle()) { // in Resources StartCoroutine(resource.LoadAsync(complete, progress)); } else { // load bundle first StartCoroutine(BundleService.GetInstance().LoadAsync(pi, delegate(BundleRef bundleRef) { if (bundleRef != null && bundleRef.Bundle != null) { bundle = bundleRef.Bundle; //从asset bundle 加载 StartCoroutine(resource.LoadAsync(bundle, complete, progress)); } else { JW.Common.Log.LogE("Async loading bundle failed, resource:{0}, bundle:{1}", path, packInfo.Path); complete(null); _resCache.Remove(resource, true); } }, delegate(BundlePackInfo failedPackInfo, string error) { JW.Common.Log.LogE("Load bundle failed, error:{0}", error); complete(null); _resCache.Remove(resource, true); })); } } else { // bundle is exists // 增加引用计数 BundleService.GetInstance().LoadSync(pi); // 加载 StartCoroutine(resource.LoadAsync(bundle, complete, progress)); } } else { //二进制文件包异步获取 StartCoroutine(resource.LoadAsync(packInfo.Path, packInfo.IsOutside(resource.Path), complete, progress)); } } else { // 非IFS 打包资源,从Resources目录异步读取 //JW.Common.Log.LogD("Load From Resource Async:"+ path); StartCoroutine(resource.LoadAsync(complete, progress)); } return(rr); }
public IEnumerator BatchLoadAsync(JWObjList <BundlePackInfo>[] bundleLists, Action complete, BundleBatchLoadingDelegate progress = null) { if (bundleLists == null || bundleLists.Length == 0) { if (progress != null) { progress(1f); } if (complete != null) { complete(); } yield break; } int totalBundles = 0; for (int i = 0; i < bundleLists.Length; ++i) { JWObjList <BundlePackInfo> bundles = bundleLists[i]; if (bundles != null) { for (int j = 0; j < bundles.Count; ++j) { if (bundles[j] != null) { ++totalBundles; } } } } // 并发加载多个bundle JWObjList <string> failedBundles = null; Dictionary <string, float> progressDict = new Dictionary <string, float>(totalBundles); // 因为是并发,所以计算进度稍微复杂一些,需要每个bundle的进度收集计算 float step = 1f / totalBundles; // 大致平分每个bundle的加载时间 for (int i = 0; i < bundleLists.Length; ++i) { JWObjList <BundlePackInfo> bundles = bundleLists[i]; StartCoroutine(LoadAsync(bundles, delegate(BundleRef bundleRef) { // complete one if (bundleRef != null) { // 已加载完成某一个bundle if (progressDict.ContainsKey(bundleRef.Path)) { progressDict[bundleRef.Path] = 1f; } else { progressDict.Add(bundleRef.Path, 1f); } } }, delegate(BundlePackInfo packInfo, string error) { if (failedBundles == null) { failedBundles = new JWObjList <string>(); } if (!failedBundles.Contains(packInfo.Path)) { failedBundles.Add(packInfo.Path); } }, delegate(BundlePackInfo packInfo, float prog) { // 加载进度回调,会有多次 if (progress != null) { // 记录每次每个bundle的进度 if (progressDict.ContainsKey(packInfo.Path)) { progressDict[packInfo.Path] = prog; } else { progressDict.Add(packInfo.Path, prog); } // 计算总进度 float totalProgress = 0; for (int bundleListIndex = 0; bundleListIndex < bundleLists.Length; ++bundleListIndex) { JWObjList <BundlePackInfo> bundlePackInfos = bundleLists[bundleListIndex]; if (bundlePackInfos != null) { for (int bundleIndex = 0; bundleIndex < bundlePackInfos.Count; ++bundleIndex) { if (bundlePackInfos[bundleIndex] != null) { float eachProg = 0; if (progressDict.TryGetValue(bundlePackInfos[bundleIndex].Path, out eachProg)) { totalProgress += eachProg * step; } } } } } progress(totalProgress); } })); } // 检查是否全部完成 while (true) { bool completed = true; for (int bundleListIndex = 0; bundleListIndex < bundleLists.Length; ++bundleListIndex) { JWObjList <BundlePackInfo> bundlePackInfos = bundleLists[bundleListIndex]; if (bundlePackInfos != null) { for (int bundleIndex = 0; bundleIndex < bundlePackInfos.Count; ++bundleIndex) { BundlePackInfo bundleInfo = bundlePackInfos[bundleIndex]; if (_bundleDict.ContainsKey(bundleInfo.Path)) { continue; } if (failedBundles != null && failedBundles.Contains(bundleInfo.Path)) { continue; } completed = false; yield return(null); break; } } if (!completed) { break; } } if (completed) { if (complete != null) { complete(); } yield break; } } }
/// <summary> /// 异步加载bundle列表 /// </summary> /// <param name="packInfo">bundle打包信息</param> /// <param name="complete">加载完成回调</param> /// <param name="failure">加载失败回调</param> /// <param name="loading">加载进度回调</param> /// <returns></returns> public IEnumerator LoadAsync(JWObjList <BundlePackInfo> packInfos, BundleLoadedDelegate complete, BundleLoadFailedDelegate failure, BundleLoadingDelegate loading = null) { if (packInfos == null) { JW.Common.Log.LogE("Async loading bundle with null pack info."); yield break; } //JWObjList<BundleRef> bundleRefs = new JWObjList<BundleRef>(); for (int i = 0; i < packInfos.Count; ++i) { BundlePackInfo packInfo = packInfos[i]; if (packInfo != null) { // ab has been loaded BundleRef br = null; if (_bundleDict.TryGetValue(packInfo.Path, out br)) { br.RefCnt++; if (_unloadingBundle.ContainsKey(packInfo.Path)) { _unloadingBundle.Remove(packInfo.Path); } if (loading != null) { loading(packInfo, 1f); } if (complete != null) { complete(br); } continue; } // loading if (_loadingBundle.Contains(packInfo.Path)) { yield return(null); while (_loadingBundle.Contains(packInfo.Path)) { yield return(null); } if (_bundleDict.TryGetValue(packInfo.Path, out br)) { br.RefCnt++; if (complete != null) { complete(br); } } continue; } if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4)) { LoadSync(packInfo); yield break; } //LZMA // 放在加载依赖前,因为加载依赖表示已经开始加载了 _loadingBundle.Add(packInfo.Path); // dependency for (int j = 0; packInfo.Dependencies != null && j < packInfo.Dependencies.Count; j++) { yield return(StartCoroutine(LoadAsync(packInfo.Dependencies[j], null, null))); } // no bundle if (packInfo.IsNoBundle() && !packInfo.Outside) { _loadingBundle.Remove(packInfo.Path); yield break; } // load string path = GetBundleFullPath(packInfo, true); JW.Common.Log.LogD("Async load bundle liat path:{0}", path); UnityWebRequest www = null; www = UnityWebRequest.GetAssetBundle(path); yield return(www.SendWebRequest()); // loading progress if (loading != null) { float progress = 0.0f; while (string.IsNullOrEmpty(www.error) && !www.isDone) { if (www.downloadProgress < 0.00001 && www.downloadProgress > -0.00001) { progress += 0.01f; loading(packInfo, progress); } else { loading(packInfo, www.downloadProgress); } yield return(null); } if (string.IsNullOrEmpty(www.error)) { loading(packInfo, 1f); } } _loadingBundle.Remove(packInfo.Path); // failed if (!string.IsNullOrEmpty(www.error)) { JW.Common.Log.LogE("Async loading bundle {0} failed, error:{1}", packInfo.Path, www.error); if (failure != null) { failure(packInfo, www.error); } www.Dispose(); continue; } AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www); // succeed if (ab != null) { br = new BundleRef(packInfo); br.Bundle = ab; _bundleDict.Add(packInfo.Path, br); if (complete != null) { complete(br); } } www.Dispose(); } } }
//异步 #region Load Asynchronize /// <summary> /// 异步加载bundle /// </summary> /// <param name="packInfo">bundle打包信息</param> /// <param name="complete">加载完成回调</param> /// <param name="failure">加载失败回调</param> /// <param name="loading">加载进度回调</param> /// <returns></returns> public IEnumerator LoadAsync(BundlePackInfo packInfo, BundleLoadedDelegate complete, BundleLoadFailedDelegate failure, BundleLoadingDelegate loading = null) { if (packInfo == null) { JW.Common.Log.LogE("Async loading bundle with null pack info."); yield break; } //判断是否已经加载 BundleRef br = null; if (_bundleDict.TryGetValue(packInfo.Path, out br)) { br.RefCnt++; if (_unloadingBundle.ContainsKey(packInfo.Path)) { _unloadingBundle.Remove(packInfo.Path); } if (loading != null) { loading(packInfo, 1f); } if (complete != null) { complete(br); } yield break; } // 正在加载 if (_loadingBundle.Contains(packInfo.Path)) { yield return(null); while (_loadingBundle.Contains(packInfo.Path)) { yield return(null); } if (_bundleDict.TryGetValue(packInfo.Path, out br)) { br.RefCnt++; if (complete != null) { complete(br); } } yield break; } //LoadFormFile 同步加载 if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4)) { LoadSync(packInfo); yield break; } //LZMA _loadingBundle.Add(packInfo.Path); // 依赖 for (int i = 0; packInfo.Dependencies != null && i < packInfo.Dependencies.Count; i++) { yield return(StartCoroutine(LoadAsync(packInfo.Dependencies[i], null, null))); } // 内部含有 if (packInfo.IsNoBundle() && !packInfo.Outside) { _loadingBundle.Remove(packInfo.Path); if (failure != null) { failure(packInfo, ""); } yield break; } { //对于LZMA AssetBundle.LoadFromFileAsync 会占用解压内存 //手册说用UnityWebRequest 可以首次解压 后缓存,但对于本地的包又说用AssetBundle.LoadFromFileAsync string path = GetBundleFullPath(packInfo, false); JW.Common.Log.LogD("Async load bundle path:{0}", path); AssetBundleCreateRequest www = AssetBundle.LoadFromFileAsync(path); float progress = 0.0f; while (!www.isDone) { if (loading != null) { if (www.progress < 0.00001 && www.progress > -0.00001) { progress += 0.01f; loading(packInfo, progress); } else { loading(packInfo, www.progress); } } yield return(null); } _loadingBundle.Remove(packInfo.Path); // failed if (www.assetBundle == null) { JW.Common.Log.LogE("Async loading bundle {0} failed, error:{1}", path, ""); if (failure != null) { failure(packInfo, ""); } www = null; yield break; } if (loading != null) { loading(packInfo, 1f); } // succeed if (www.assetBundle != null) { br = new BundleRef(packInfo); br.Bundle = www.assetBundle; _bundleDict.Add(packInfo.Path, br); if (complete != null) { complete(br); } } www = null; } /*Test2 * { * string path = GetBundleFullPath(packInfo, true); * JW.Common.Log.LogD("Async load bundle use UnityWebRequest path:{0}", path); * UnityWebRequest www = UnityWebRequest.GetAssetBundle(path); * yield return www.SendWebRequest(); * float progress = 0.0f; * while (!www.isDone) * { * if (loading != null) * { * if (www.downloadProgress < 0.00001 && www.downloadProgress > -0.00001) * { * progress += 0.01f; * loading(packInfo, progress); * } * else * { * loading(packInfo, www.downloadProgress); * } * } * yield return null; * } * * _loadingBundle.Remove(packInfo.Path); * // failed * AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www); * if (ab == null) * { * JW.Common.Log.LogE("Async loading bundle use UnityWebRequest {0} failed, error:{1}", path, ""); * if (failure != null) * { * failure(packInfo, ""); * } * www.Dispose(); * www = null; * yield break; * } * * if (loading != null) * { * loading(packInfo, 1f); * } * * // succeed * if (ab != null) * { * br = new BundleRef(packInfo); * br.Bundle = ab; * _bundleDict.Add(packInfo.Path, br); * * if (complete != null) * { * complete(br); * } * } * www.Dispose(); * www = null; * }*/ }
/// <summary> /// 同步加载bundle /// </summary> /// <param name="packInfo">bundle打包信息</param> public void LoadSync(BundlePackInfo packInfo) { if (packInfo == null) { JW.Common.Log.LogE("Sync loading bundle with null pack info."); return; } //是否已经加载 BundleRef br = null; if (_bundleDict.TryGetValue(packInfo.Path, out br)) { br.RefCnt++; if (_unloadingBundle.ContainsKey(packInfo.Path)) { _unloadingBundle.Remove(packInfo.Path); } return; } // 是否正在加载 if (_loadingBundle.Contains(packInfo.Path)) { JW.Common.Log.LogE("Bundle is loading when load sync. bundle:{0}", packInfo.Path); return; } // 依赖 for (int i = 0; packInfo.Dependencies != null && i < packInfo.Dependencies.Count; i++) { LoadSync(packInfo.Dependencies[i]); } //干活 AssetBundle ab = null; string path = GetBundleFullPath(packInfo, false); JW.Common.Log.LogD("Sync load bundle path:{0}", path); // if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4)) { ab = AssetBundle.LoadFromFile(path); } else { //LZMA 压缩的AssetBundle //LZMA压缩的 u2017可以直接通过 LoadFromFile 创建支持StreamingAssetPath 内存占用大 ab = AssetBundle.LoadFromFile(path); /* * byte[] bytes = null; * //优先外围目录 * if (FileUtil.IsExistInIFSExtraFolder(packInfo.Path)) * { * bytes = File.ReadAllBytes(path); * } * else * { * * //StreamingAsset目录的 #if UNITY_ANDROID && !UNITY_EDITOR * bytes = NativeHelper.Android_ReadFileInAssets(packInfo.Path); #else * bytes = File.ReadAllBytes(path); #endif * } * * if (null != bytes) * { * //注意 * //https://unity3d.com/cn/learn/tutorials/topics/best-practices/assetbundle-fundamentals?playlist=30089 * //The peak amount of memory consumed by this API will be at least twice the size of the AssetBundle: * ab = AssetBundle.LoadFromMemory(bytes); * }*/ } // 完成 if (ab != null) { br = new BundleRef(packInfo); br.Bundle = ab; _bundleDict.Add(packInfo.Path, br); } }
/// <summary> /// bundle中资源已被加载 /// </summary> /// <param name="assetPath">资源路径</param> public void OnAssetLoaded(string assetPath) { BundlePackInfo pi = GetPackInfo(assetPath); OnAssetLoaded(pi); }