Notifications
C
Loxodon Framework Bundle(AssetBundle加载和管理)
Showcase
693
0
Loxodon Framework Bundle是一个非常好用的AssetBundle加载器,也是一个AssetBundle冗余分析工具。它能够自动管理AssetBundle之间复杂的依赖关系,它通过引用计数来维护AssetBundle之间的依赖。你既可以预加载一个AssetBundle,自己管理它的释放,也可以直接通过异步的资源加载函数直接加载资源,资源加载函数会自动去查找资源所在的AB包,自动加载AB,使用完后又会自动释放AB。 它还支持弱缓存,如果对象模板已经在缓存中,则不需要重新去打开AB。它支持多种加载方式,WWW加载,UnityWebRequest加载,File方式的加载等等(在Unity5.6以上版本,请不要使用WWW加载器,它会产生内存峰值)。它提供了一个AssetBundle的打包界面,支持加密AB包(只建议加密敏感资源,因为会影响性能)。同时它也绕开了Unity3D早期版本的一些bug,比如多个协程并发加载同一个资源,在android系统会出错。它的冗余分析是通过解包AssetBundle进行的,这比在编辑器模式下分析的冗余更准确。

Key features:

  • Support encryption and decoding of the AssetBundle files(支持AssetBundle文件的加密和解密);
  • Use reference counting to manage the dependencies of AssetBundle(使用引用计数来管理AssetBundle的依赖关系);
  • Automatically unloading AssetBundle with reference count 0 by destructor or "Dispose" method(当引用计数为0时,通过析构函数或者Dispose函数自动卸载AssetBundle);
  • Use weak cache to optimize load performance(使用弱缓存来优化加载性能);
  • Interface-oriented programming, you can customize the loader and file search rules for the AssetBundle(面向接口编程,可以自定义加载器,自定义AssetBundle的查找规则);
  • Support for simulating loading in the editor without building AssetBundles(在编辑器下,支持模拟AssetBundle的方式加载资源以方便测试);
  • Supports the unpacking and the redundancy analysis of the AssetBundle(支持通过AssetBundle包来分析资源冗余,这比在Editor模式分析的冗余更准确);
下载地址:AssetStore 下载
QQ群:622321589
Contact Us at: yangpc.china@gmail.com

使用示例

  • 初始化IResources
整个项目面向接口设计,任何组件都是可以自定义或者可选的,下图是我默认的一个示例。
IResources CreateResources() { IResources resources = null; #if UNITY_EDITOR if (SimulationSetting.IsSimulationMode) { Debug.Log("Use SimulationResources. Run In Editor"); /* Create a PathInfoParser. */ //IPathInfoParser pathInfoParser = new SimplePathInfoParser("@"); IPathInfoParser pathInfoParser = new SimulationAutoMappingPathInfoParser(); /* Create a BundleManager */ IBundleManager manager = new SimulationBundleManager(); /* Create a BundleResources */ resources = new SimulationResources(pathInfoParser, manager); } else #endif { /* Create a BundleManifestLoader. */ IBundleManifestLoader manifestLoader = new BundleManifestLoader(); /* Loads BundleManifest. */ BundleManifest manifest = manifestLoader.Load(BundleUtil.GetReadOnlyDirectory() + BundleSetting.ManifestFilename); //manifest.ActiveVariants = new string[] { "", "sd" }; manifest.ActiveVariants = new string[] { "", "hd" }; /* Create a PathInfoParser. */ //IPathInfoParser pathInfoParser = new SimplePathInfoParser("@"); IPathInfoParser pathInfoParser = new AutoMappingPathInfoParser(manifest); /* Create a BundleLoaderBuilder */ //ILoaderBuilder builder = new WWWBundleLoaderBuilder(new Uri(BundleUtil.GetReadOnlyDirectory()), false); /* AES128_CBC_PKCS7 */ RijndaelCryptograph rijndaelCryptograph = new RijndaelCryptograph(128, Encoding.ASCII.GetBytes(this.key), Encoding.ASCII.GetBytes(this.iv)); /* Use a custom BundleLoaderBuilder */ ILoaderBuilder builder = new CustomBundleLoaderBuilder(new Uri(BundleUtil.GetReadOnlyDirectory()), false, rijndaelCryptograph); /* Create a BundleManager */ IBundleManager manager = new BundleManager(manifest, builder); /* Create a BundleResources */ resources = new BundleResources(pathInfoParser, manager); } return resources; }
  • 自定义AB的查询规则
AssetBundle资源可以存在Unity3D的缓存中,也可以存在持久化目录中或者在StreamingAssets目录中,关于如何存储资源,一般和项目怎么更新资源有关系,在我的CustomBundleLoaderBuilder中,你可以自定义自己的加载规则和选择使用自己喜欢的加载器(WWW、UnityWebRequest、File等)。
using System; using Loxodon.Framework.Bundles; namespace Loxodon.Framework.Examples.Bundle { public class CustomBundleLoaderBuilder : AbstractLoaderBuilder { private bool useCache; private IDecryptor decryptor; public CustomBundleLoaderBuilder(Uri baseUri, bool useCache) : this(baseUri, useCache, null) { } public CustomBundleLoaderBuilder(Uri baseUri, bool useCache, IDecryptor decryptor) : base(baseUri) { this.useCache = useCache; this.decryptor = decryptor; } public override BundleLoader Create(BundleManager manager, BundleInfo bundleInfo, BundleLoader[] dependencies) { //Customize the rules for finding assets. Uri loadBaseUri = this.BaseUri; //eg: http://your ip/bundles if (this.useCache && BundleUtil.ExistsInCache(bundleInfo)) { //Load assets from the cache of Unity3d. loadBaseUri = this.BaseUri; #if UNITY_5_4_OR_NEWER return new UnityWebRequestBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager, this.useCache); #else return new WWWBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager, this.useCache); #endif } if (BundleUtil.ExistsInStorableDirectory(bundleInfo)) { //Load assets from the "Application.persistentDataPath/bundles" folder. /* Path: Application.persistentDataPath + "/bundles/" + bundleInfo.Filename */ loadBaseUri = new Uri(BundleUtil.GetStorableDirectory()); } #if !UNITY_WEBGL || UNITY_EDITOR else if (BundleUtil.ExistsInReadOnlyDirectory(bundleInfo)) { //Load assets from the "Application.streamingAssetsPath/bundles" folder. /* Path: Application.streamingAssetsPath + "/bundles/" + bundleInfo.Filename */ loadBaseUri = new Uri(BundleUtil.GetReadOnlyDirectory()); } #endif if (bundleInfo.IsEncrypted) { if (this.decryptor != null && bundleInfo.Encoding.Equals(decryptor.AlgorithmName)) return new CryptographBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager, decryptor); throw new NotSupportedException(string.Format("Not support the encryption algorithm '{0}'.", bundleInfo.Encoding)); } //Loads assets from an Internet address if it does not exist in the local directory. #if UNITY_5_4_OR_NEWER if (this.IsRemoteUri(loadBaseUri)) return new UnityWebRequestBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager, this.useCache); else return new FileAsyncBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager); #else return new WWWBundleLoader(new Uri(loadBaseUri, bundleInfo.Filename), bundleInfo, dependencies, manager, this.useCache); #endif } } }
  • 加载一个资源
加载资源是根据资源的路径来加载的,如果你选择了路径自动映射的路径解析器,那么通过资源的路径,就可以自动找到所在的AssetBundle包。下面是资源加载的示例。
1.通过回调的方式加载一个资源
void Load(string[] names) { IProgressResult<float, GameObject[]> result = resources.LoadAssetsAsync<GameObject>(names); result.Callbackable().OnProgressCallback(p => { Debug.LogFormat("Progress:{0}%", p * 100); }); result.Callbackable().OnCallback((r) => { try { if (r.Exception != null) throw r.Exception; foreach (GameObject template in r.Result) { GameObject.Instantiate(template); } } catch (Exception e) { Debug.LogErrorFormat("Load failure.Error:{0}", e); } }); }
2.通过回调方式加载场景
void LoadSceneByCallback(string sceneName) { ISceneLoadingResult<Scene> result = this.resources.LoadSceneAsync(sceneName); result.AllowSceneActivation = false; result.OnProgressCallback(p => { //Debug.LogFormat("Loading {0}%", (p * 100)); }); result.OnStateChangedCallback(state => { if (state == LoadState.Failed) Debug.LogFormat("Loads scene '{0}' failure.Error:{1}", sceneName, result.Exception); else if (state == LoadState.Completed) Debug.LogFormat("Loads scene '{0}' completed.", sceneName); else if (state == LoadState.AssetBundleLoaded) Debug.LogFormat("The AssetBundle has been loaded."); else if (state == LoadState.SceneActivationReady) { Debug.LogFormat("Ready to activate the scene."); result.AllowSceneActivation = true; } }); }
3.通过协程的方式加载场景
IEnumerator LoadSceneByCoroutine(string sceneName) { ISceneLoadingResult<Scene> result = this.resources.LoadSceneAsync(sceneName); while (!result.IsDone) { //Debug.LogFormat("Loading {0}%", (result.Progress * 100)); yield return null; } if (result.Exception != null) { Debug.LogFormat("Loads scene '{0}' failure.Error:{1}", sceneName, result.Exception); } else { Debug.LogFormat("Loads scene '{0}' completed.", sceneName); } }
  • 下载示例
我提供了一个AssetBundle资源下载的示例,它通过最新版本的资源索引库Manifest.dat ,查找本地不存在的AB资源,然后通过网络下载本地缺失的AB资源。
IEnumerator Download() { this.downloading = true; try { IProgressResult<Progress, BundleManifest> manifestResult = this.downloader.DownloadManifest(BundleSetting.ManifestFilename); yield return manifestResult.WaitForDone(); if (manifestResult.Exception != null) { Debug.LogFormat("Downloads BundleManifest failure.Error:{0}", manifestResult.Exception); yield break; } BundleManifest manifest = manifestResult.Result; IProgressResult<float, List<BundleInfo>> bundlesResult = this.downloader.GetDownloadList(manifest); yield return bundlesResult.WaitForDone(); List<BundleInfo> bundles = bundlesResult.Result; if (bundles == null || bundles.Count <= 0) { Debug.LogFormat("Please clear cache and remove StreamingAssets,try again."); yield break; } IProgressResult<Progress, bool> downloadResult = this.downloader.DownloadBundles(bundles); downloadResult.Callbackable().OnProgressCallback(p => { Debug.LogFormat("Downloading {0:F2}KB/{1:F2}KB {2:F3}KB/S", p.GetCompletedSize(UNIT.KB), p.GetTotalSize(UNIT.KB), p.GetSpeed(UNIT.KB)); }); yield return downloadResult.WaitForDone(); if (downloadResult.Exception != null) { Debug.LogFormat("Downloads AssetBundle failure.Error:{0}", downloadResult.Exception); yield break; } Debug.Log("OK"); if (this.resources != null) { //update BundleManager's manifest BundleManager manager = (this.resources as BundleResources).BundleManager as BundleManager; manager.BundleManifest = manifest; } #if UNITY_EDITOR UnityEditor.EditorUtility.OpenWithDefaultApp(BundleUtil.GetStorableDirectory()); #endif } finally { this.downloading = false; } }
C
Clark
3
Comments