Unity5 AssetBundle 加载
2018-01-22 16:13
381 查看
Unity5 AssetBundle 加载
上一篇说了 AssetBundle 打包
本篇接着做加载
加载需要考虑的两个问题:
一、如何将资源加载
二、资源依赖的其他资源如何处理
理论:一个预设 A 依赖 一个材质球B、一个材质球 C、 材质球 B 和材质球 C 又分别依赖一张贴图 B1、C1。
那么想要加载预设 A 首先要先加载 A 直接依赖的 B和C,还要将 B和C 依赖的资源 B1、C1加载,然后再加载 预设 A。这样才能保证 A 正常使用。
如何获取 A 的直接、间接依赖的所有资源名。
上篇分析了 总依类型文件
注意:
找到和
加载资源前确保
添加
上面用到的
上面两个就可以完成初步的加载了
上测试代码
点击 A 加载资源,没有材质、贴图 可以正常加载
点击 D 加载场景 Two,加载完成从 One 场景切换到 Two 场景
注意:加载场景 Two 没问题, Two 打包时就是这个样子,这个是设置 assetBundleName,通过
重启点击 W,加载场景 Two
场景中只能看到 一个立方体 Cube,在 Hierarchy 面板看,并没有丢失 GameObject。找一个查看
原因是 GameObject 引用的资源没有加载上来。
注意: 这次加载的场景是通过
说明这种方法打包的场景,没法找到场景的依赖文件,如果场景有依赖其他资源,加载时将会丢失。
解决方案待查
上一篇说了 AssetBundle 打包
本篇接着做加载
加载需要考虑的两个问题:
一、如何将资源加载
二、资源依赖的其他资源如何处理
理论:一个预设 A 依赖 一个材质球B、一个材质球 C、 材质球 B 和材质球 C 又分别依赖一张贴图 B1、C1。
那么想要加载预设 A 首先要先加载 A 直接依赖的 B和C,还要将 B和C 依赖的资源 B1、C1加载,然后再加载 预设 A。这样才能保证 A 正常使用。
如何获取 A 的直接、间接依赖的所有资源名。
上篇分析了 总依类型文件
AssetBundleManifest在
AssetBundle.manifest中可以看到所有资源以及资源直接依赖的其他资源。
注意:
AssetBundle.manifest中也是只能看到直接依赖的资源
找到和
*.manifest同名的文件
AssetBundle看不到后缀名
AssetBundle 中可以获取到一个资源的所有依赖(包括直接依赖、间接依赖)
加载资源前确保
AssetBundleManifest已经加载,通过
AssetBundleManifest对象方法
GetAllDependencies(string assetBundleName);从中获取到依赖文件
// 通过 assetBundleName 从 AssetBundleManifest 文件中 获取所有依赖资源数据 string[] depends = assetBundleManifest.GetAllDependencies(assetBundleName); for (int i = 0; i < depends.Length; ++i) { // 加载依赖资源 } // 依赖资源加载完毕,再加载自己
添加
LoadAssetBundle加载类
using UnityEngine; using System.Collections; using System.IO; using System.Collections.Generic; using System; using UnityEngine.SceneManagement; // 回调方法 public delegate void LoadABCallBack(AssetBundle assetBundle); // AB 文件只能加载一次,重复加载出错,两种方案, // 一、加载后卸载,下次需要使用时重新加载, // 二、加载后将AB保存起来,下载加载相同AS时直接取出来使用 public class LoadAssetBundle { //单例 public static readonly LoadAssetBundle Instance = new LoadAssetBundle(); //所有资源总依赖文件 public AssetBundleManifest assetBundleManifest = null; public bool isLoadingAssetBundleMainfest = false; /// <summary> /// 加载 AB 资源 /// </summary> /// <param name="url"></param> /// <param name="abCallBack"></param> /// <returns></returns> private IEnumerator LoadAB(string url, LoadABCallBack loadABCallBack) { if (!url.StartsWith("file://")) { url = "file://" + url; } WWW www = new WWW(url); yield return www; if (!string.IsNullOrEmpty(www.error)) { Debug.LogError(www.error); yield break; } AssetBundle assetBundle = www.assetBundle; if (loadABCallBack != null) { loadABCallBack(assetBundle); } } /// <summary> /// 加载资源 /// </summary> /// <param name="assetPath"></param> /// <param name="callBack"></param> /// <param name="assetBundleName"></param> /// <returns></returns> private IEnumerator LoadABAsset(string assetPath, LoadABCallBack loadABCallBack, string assetBundleName) { // 注意: // 此处需要添加已加载 AB 资源的管理 // AssetBundl 资源只能被加载一次,如果已经加载了并且没有释放掉, // 再次加载同一个 AssetBundle 时将报异常 can't be loaded because another AssetBundle with the same files is already loaded. // 当切换场景时或者确定不使用某个AssetBundle时需要将其释放掉,当再次需要加载该资源时才能重新加载 // 在这个 Demo 中不添加 AB 资源管理了 if (assetBundleManifest == null) { // 加载总依赖文件 yield return Game.Instance.StartCoroutine(LoadManifest()); } // 总依赖文件没加载成功就不再继续执行了 if (assetBundleManifest == null) { yield break; } // 通过 assetBundleName 获取所有依赖资源数据 string[] depends = assetBundleManifest.GetAllDependencies(assetBundleName); // 加载资源前,要先将其所有依赖的资源全部加载 for (int i = 0; i < depends.Length; ++i) { string url = ResourcePathManager.Instance.CheckFilePath(ResourcePathManager.Instance.GetPersistentDataPath, "AssetBundle/" + depends[i]); yield return Game.Instance.StartCoroutine(LoadAB(url, null)); } // 依赖资源加载完成,才能开始加载自己 yield return Game.Instance.StartCoroutine(LoadAB(assetPath, loadABCallBack)); } // 加载资源 public void LoadAsset(string assetPath, LoadABCallBack loadABCallBack, string assetBundleName) { Game.Instance.StartCoroutine(LoadABAsset(assetPath, loadABCallBack, assetBundleName)); } //AB.Unload 卸载 AssetBundl: //参数 false 只卸载 AssetBundl 资源,已经从 AssetBundle 资源中 Load 并且实例化出来的对象不会被释放 //参数 true 卸载 AssetBundl 的同时,也会把从 AssetBundl 资源中Load 并且实例化出来的对象一起销毁(很危险,慎用) // 加载场景 public void LoadScene(string sceneAssetPath, LoadABCallBack loadABCallBack, string assetBundleName) { Game.Instance.StartCoroutine(LoadABAsset(sceneAssetPath, loadABCallBack, assetBundleName)); } //加载总依赖资源文件 AssetBundleManifest public IEnumerator LoadManifest() { // AssetBundleManifest 只加载一次,整个游戏过程中都不释放 if (!isLoadingAssetBundleMainfest) { isLoadingAssetBundleMainfest = true; LoadABCallBack loadABCallBack = delegate (AssetBundle assetBundle) { if (assetBundle != null) { assetBundleManifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); } }; string url = ResourcePathManager.Instance.CheckFilePath(ResourcePathManager.Instance.GetPersistentDataPath, "AssetBundle/AssetBundle"); yield return Game.Instance.StartCoroutine(LoadAB(url, loadABCallBack)); } // 直到加载成功,否则一直在此处等待 while (assetBundleManifest == null) { yield return new WaitForEndOfFrame (); } yield break; } }
上面用到的
ResourcePathManager为资源路径的管理,根据不同平台获取不同路径,
using UnityEngine; using System.IO; /// <summary> /// 管理加载资源路径 /// </summary> public class ResourcePathManager { //单例 public static readonly ResourcePathManager Instance = new ResourcePathManager(); // 根据不同平台获取资源路径,移动平台为沙盒路径, 放在 StreamingAssets 下的文件不在此处 public string GetPersistentDataPath { get { if (Application.isEditor) { //return "file://" + Application.persistentDataPath + "/"; return Application.persistentDataPath + "/"; } //不同平台下 Persistent 的路径是不同的,这里需要注意一下。 #if UNITY_ANDROID return "file://" + Application.persistentDataPath + "/"; #elif UNITY_IPHONE return "file://" + Application.persistentDataPath + "/"; #elif UNITY_STANDALONE_WIN || UNITY_EDITOR return "file://" + Application.persistentDataPath + "/"; #else return string.Empty; #endif } } //优先获取沙盒内的资源,如果沙盒不存在该资源,从本地 StreamingAssets文件夹下获取 public string CheckFilePath(string persistentDataPath, string filePath) { string url = System.IO.Path.Combine(persistentDataPath, filePath); // String.Format("{0}/{1}", persistentDataPath, filePath); // System.IO.Path.Combine(persistentDataPath, filePath); // 沙盒内存在加载文件则返回沙盒内路径 if (File.Exists(url)) { return url; } return GetStreamingAssetsPath(filePath); } // 返回本地 StreamAssetsPath 路径 public string GetStreamingAssetsPath(string filePath) { if (Application.isEditor) { return "file://" + Application.streamingAssetsPath + "/" + filePath; } #if UNITY_ANDROID //return "jar:file:///" + Application.dataPath + "!/assets/"; return Application.streamingAssetsPath + "/" + filePath; #elif UNITY_IPHONE //return Application.dataPath + "/Raw/"; return "file://" + Application.streamingAssetsPath + "/" + filePath; #elif UNITY_STANDALONE_WIN || UNITY_EDITOR //return Application.dataPath + "/StreamingAssets/"; return "file://" + Application.streamingAssetsPath + "/" + filePath; #else return String.Format("{0}/{1}", Application.streamingAssetsPath, filePath); #endif } //AB包资源后缀名 public static readonly string abExtension = "unity3d"; public string GetLoadPath( string path, bool isassetBundle = true) { string persistentPath = GetPersistentDataPath; string combinPath = path; if (isassetBundle) { combinPath = Path.Combine("AssetBundle", path); } string url = CheckFilePath(persistentPath, combinPath); return url; } }
上面两个就可以完成初步的加载了
上测试代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.SceneManagement; public class Game : MonoBehaviour { public static Game Instance = null; protected void Awake() { Instance = this; } private void Update() { if (Input.GetKeyDown(KeyCode.A)) { // 通过 assetBundleName 加载资源 Load("allassets/prefab/building/caishichang.unity3d"); } if (Input.GetKeyDown(KeyCode.D)) { string scenePath = Path.Combine(Application.streamingAssetsPath, "AssetBundle/scenes/two.unity3d"); string assetBundleName = "scenes/two.unity3d"; // 加载设置 assetBundleName 的场景(通过 BuildPipeline.BuildAssetBundles 方法打包的场景) LoadScene(scenePath, assetBundleName); } if (Input.GetKeyDown(KeyCode.W)) { string scenePath = Path.Combine(Application.streamingAssetsPath, "AssetBundle/Scene/two.unity3d"); string assetBundleName = "Scene/two.unity3d"; // 加载通过 BuildPipeline.BuildPlayer 打包的场景 LoadScene(scenePath, assetBundleName); } } private void Load(string name) { LoadABCallBack loadCallBack = delegate (AssetBundle assetBundle) { if (assetBundle == null) { return; } string objName = Path.GetFileNameWithoutExtension(name); GameObject obj = assetBundle.LoadAsset<GameObject>(objName); GameObject go = Instantiate(obj) as GameObject; go.name = Path.GetFileNameWithoutExtension(name); }; // 拼接加载路径 string path = Path.Combine(Application.streamingAssetsPath, "AssetBundle/" + name); LoadAssetBundle.Instance.LoadAsset(path, loadCallBack, name); } private void LoadScene(string scenePath, string assetBundleName) { LoadABCallBack loadCallBack = delegate (AssetBundle assetBundle) { if (assetBundle == null) { return; } SceneManager.LoadScene("Two"); }; LoadAssetBundle.Instance.LoadScene(scenePath, loadCallBack, assetBundleName); } }
点击 A 加载资源,没有材质、贴图 可以正常加载
点击 D 加载场景 Two,加载完成从 One 场景切换到 Two 场景
注意:加载场景 Two 没问题, Two 打包时就是这个样子,这个是设置 assetBundleName,通过
BuildPipeline.BuildAssetBundles打包的场景
重启点击 W,加载场景 Two
场景中只能看到 一个立方体 Cube,在 Hierarchy 面板看,并没有丢失 GameObject。找一个查看
原因是 GameObject 引用的资源没有加载上来。
注意: 这次加载的场景是通过
BuildPipeline.BuildPlayer方法打包的场景,该方法会将场景打包成一个整体,不生成依赖文件,本次丢失的都是场景中预设上依赖的材质、贴图。Cube 立方体并没有丢失,因为Cube立方体是直接创建在场景中的,而不是场景依赖的预设。
说明这种方法打包的场景,没法找到场景的依赖文件,如果场景有依赖其他资源,加载时将会丢失。
解决方案待查
相关文章推荐
- Unity场景打包AssetBundle并加载
- Unity创建Assetbundle与加载
- Unity5.x版本AssetBundle加载研究
- Unity5.3.5加载AssetBundle包及依赖
- 解决Unity 中WWW加载 AssetBundle---中文路径
- Unity的动态加载AssetBundle资源笔记。
- Unity教程之-Unity3d打包Assetbundle并加载(全面)
- Unity 关于AssetBundle(资源束)打包和加载(一)
- unity 加载assetbundle
- Unity教程之-Unity5.x版本AssetBundle加载研究
- unity5.x assetbundle打包和加载
- Unity中AssetBundle的打包和加载
- Unity资源处理机制(Assets/WWW/AssetBundle/...)读取和加载资源方式详解
- Unity游戏开发使用Assetbundle加载场景的原理
- unity动态加载之AssetBundle原理
- Unity游戏开发使用Assetbundle加载场景的实战
- Unity5-ABSystem(三):AssetBundle加载
- Unity5-ABSystem(三):AssetBundle加载
- Unity最新版打包AssetBundle和加载的方法
- unity中ScriptableObject在assetbundle中的加载