您的位置:首页 > 移动开发 > Unity3D

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 的直接、间接依赖的所有资源名。

上篇分析了 总依类型文件
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立方体是直接创建在场景中的,而不是场景依赖的预设。

说明这种方法打包的场景,没法找到场景的依赖文件,如果场景有依赖其他资源,加载时将会丢失。

解决方案待查
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Unity5 AssetBundle WWW