源码工程路径


这次我们把上节中的立方体Cube从AssetBundle中加载出来,实例化立方体;然后把材质m_Material2从AssetBundle加载出来,并把立方体Cube的材质换成m_Material2

我们新加了两个资源,一个是纹理detail2,一个是材质m_Material2

unity项目运行在mac上 unity build mac_unity项目运行在mac上


先看一下效果

unity项目运行在mac上 unity build mac_bundle_02


讲一下思路,代码不详细讲,太多了,也都挺简单的,原理在上一节都讲过了,原码都会贴出来

首先我们创建了五个脚本

unity项目运行在mac上 unity build mac_加载_03


IABManifestLoader.cs

主要用来加载Manifest文件

IABRelationManager.cs

保存单个AssetBundle和该它所依赖的AssetBundle和依赖他的AssetBundle,对单个AssetBundle进行的操作都在这个文件中(得到单个资源,卸载单个资源等)

 IABManager.cs

对所有的AssetBundle进行管理,主要有三个数据结构


unity项目运行在mac上 unity build mac_bundle_04

正在加载的AssetBundle放在preLoadHelper中避免重复加载

加载完成的AssetBundle放在loadHelper中

已经从ab中load出具体资源放在loadObj中


我们想要加载一个具体资源比如立方体Cube时,先从loadObj中找,如果没有的话再从loadHelper中找,在没有的话再根据AssetBundle名字和地址用WWW下载

当我们把立方体Cube加载出来后用回调的方式返回上层,然后我们就可以得到Cube,然后我们就可以把Cube实例化出来了。

加载具体资源的代码

public void LoadSingleObject(string bundleName, string resName, LoadObjectCallBack callBack)
    {
        //表示 是否已经缓存了物体
        if (loadObj.ContainsKey(bundleName))
        {
            AssetResObj tmpRes = loadObj[bundleName];
            Object tmpObj = tmpRes.GetResObj(resName);
            if (tmpObj != null) {
                callBack(tmpObj);
                return;
            }
        }
        //表示bundle已经加载过
        if (loadHelper.ContainsKey(bundleName))
        {
            LoadObjectFromBundle(bundleName, resName, callBack);
        }
        //加载bundle
        else
        {
            StartCoroutine(LoadAssetBundle(bundleName, (tmpBundleName) => {
                LoadObjectFromBundle(bundleName, resName, callBack);
            }));
        }

    }


其中TestLoad.cs是测试文件,IPathTools是个工具类


完整代码

IABManifestLoader.cs

using UnityEngine;
using System.Collections;

public class IABManifestLoader {
    public AssetBundleManifest assetManifest;
    public string manifesetName;
    private bool isLoadFinish;
    public AssetBundle manifestLoader;

    static IABManifestLoader instance = null;
    public static IABManifestLoader Instance {
        get {
            if (instance == null) {
                instance = new IABManifestLoader();
            }
            return instance;
        }
    }

    public IABManifestLoader()
    {
        assetManifest = null;
        manifestLoader = null;
        isLoadFinish = false;
        manifesetName = IPathTools.GetABManifestName();
    }

    public IEnumerator LoadManifeset() {
        manifesetName = IPathTools.StandardPath(manifesetName);
        Debug.Log("manifesetName "+ manifesetName);
        WWW manifest = new WWW(manifesetName);
        yield return manifest;
        if (!string.IsNullOrEmpty(manifest.error))
        {
            Debug.Log(manifest.error);
        }
        else {
            if (manifest.progress >= 1.0f) {
                manifestLoader = manifest.assetBundle;
                assetManifest = manifestLoader.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
                isLoadFinish = true;
                Debug.Log("manifest load finish");
            }
        }
    }

    public string[] GetDepences(string name) {
        return assetManifest.GetAllDependencies(name);
    }
    public void UnloadManifest() {
        manifestLoader.Unload(false);  //true  
    }
    public bool IsLoadFinish() {
        return isLoadFinish;
    }
}



IABRelationManager.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class IABRelationManager {
    //依赖关系
    List<string> depedenceBundle;
    //被依赖关系
    List<string> referBundle;
    public AssetBundle assetBundle;
    string theBundleName;
    bool isLoadFinish;
    bool canAutoUnload;

    public string GetBundleName() {
        return theBundleName;
    }

    public IABRelationManager()
    {
        depedenceBundle = new List<string>();
        referBundle = new List<string>();
    }

    public bool IsBundleLoadFinish()
    {
        return isLoadFinish;
    }
    public void Initial(string bundleName, bool autoUnload = true)
    {
        isLoadFinish = false;
        theBundleName = bundleName;
        canAutoUnload = autoUnload;
    }
    public void SetAssetBundle(AssetBundle bundle) {
        assetBundle = bundle;
        isLoadFinish = true;
    }
    public void SetDependences(string[] dependence)
    {
        if (dependence.Length > 0)
        {
            depedenceBundle.AddRange(dependence);
        }
    }

    public List<string> GetDependences()
    {
        return depedenceBundle;
    }
    //添加被依赖关系
    public void AddRefference(string bundleNmae) {
        referBundle.Add(bundleNmae);
    }
    //获取被依赖关系
    public List<string> GetRefference() {
        return referBundle;
    }
    public Object GetSingleResource(string resName)
    {
        return assetBundle.LoadAsset(resName);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="bundleName"></param>
    /// <returns>表示是否释放了自己</returns>
    public bool RemoveReference(string bundleName) {
        int index = -1;
        for (int i = 0; i < referBundle.Count; i++) {
            if (bundleName.Equals(referBundle[i])) {
                index = i;
                break;
            }
        }
        if (index > -1) {
            referBundle.RemoveAt(index);
        }
        if (referBundle.Count <= 0)
        {
            return Dispose();
        }
        return true;
    }

    


   

    #region 有下层提供API
    /// <summary>
    /// 释放过程 
    /// </summary>
    public bool Dispose() {
        if (canAutoUnload)
        {
            if (assetBundle != null) {
                assetBundle.Unload(false);
                IABManager.instance.RemoveBundle(theBundleName);
            }
            return true;
        }
        else {
            return false;
        }
    }
    #endregion
}


IABManager.cs


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public delegate void LoadAssetBundleCallBack(string bundleName);
public delegate void LoadObjectCallBack(Object o);

//单个物体 存取
public class AssetObj {
    public Object obj;
    public AssetObj(Object tmpObj) {
        obj = tmpObj;
    }
    public void ReleaseObj() {
        Resources.UnloadAsset(obj);
    }
}
//相同bundle中的资源都存在这
public class AssetResObj {
    public Dictionary<string, AssetObj> resObjs;
    public AssetResObj(string name, AssetObj tmp) {
        resObjs = new Dictionary<string, AssetObj>();
        resObjs.Add(name,tmp);
    }
    public void AddResObj(string name, AssetObj tmpObj) {
        resObjs.Add(name,tmpObj);
    }
    //释放多个
    public void ReleaseAllResObj() {
        List<string> keys = new List<string>();
        keys.AddRange(resObjs.Keys);
        for (int i = 0; i < keys.Count; i++) {
            ReleaseResObj(keys[i]);

        }
    }
    //释放单个
    public void ReleaseResObj(string name) {
        if (resObjs.ContainsKey(name))
        {
            AssetObj tmpObj = resObjs[name];
            tmpObj.ReleaseObj();
        }
        else {
            Debug.Log("release object name is not exit == "+name);
        }
    }
    public Object GetResObj(string name) {
        if (resObjs.ContainsKey(name))
        {
            AssetObj tmpObj = resObjs[name];
            return tmpObj.obj;
        }
        else
        {
            return null;
        }
    }
}
//ab加载完成后的回调
public class LoadAssetBundleCallBackManager {
    string theBundleName;
    List<LoadAssetBundleCallBack> callBacks;
    public LoadAssetBundleCallBackManager(string bundleName) {
        theBundleName = bundleName;
        callBacks = new List<LoadAssetBundleCallBack>();
    }
    public void AddCallBack(LoadAssetBundleCallBack callBack) {
        callBacks.Add(callBack);
    }
    public void CallBack(string bundleName) {
        for (int i = 0; i < callBacks.Count; i++) {
            callBacks[i](bundleName);
        }
    }
}

public class IABManager:MonoBehaviour {
    //正在加载的ab
    Dictionary<string, IABRelationManager> preLoadHelper = new Dictionary<string, IABRelationManager>();
    //已经加载完成的ab
    Dictionary<string, IABRelationManager> loadHelper = new Dictionary<string, IABRelationManager>();
    //已经从ab中load出具体资源
    Dictionary<string, AssetResObj> loadObj = new Dictionary<string, AssetResObj>();//key是bundleName
    //ab加载完成后的回调
    Dictionary<string, LoadAssetBundleCallBackManager> loadBundleCallBack = new Dictionary<string, LoadAssetBundleCallBackManager>();

    public static IABManager instance;
    void Awake() {
        instance = this;
        StartCoroutine(IABManifestLoader.Instance.LoadManifeset());
    }
    //表示是否加载了AssetBundle
    public bool IsLoadingAssetBundle(string bundleName) {
        if (!loadHelper.ContainsKey(bundleName)){
            return false;
        } else {
            return true;
        }
    }
    
    #region  由下层提供API

    public bool IsLoadFinish(string bundleName) {
        return loadHelper.ContainsKey(bundleName);
    }

    public void RemoveBundle(string bundleName) {
        if (loadHelper.ContainsKey(bundleName)) {
            loadHelper.Remove(bundleName);
        }
    }

    string GetBundlePath(string bundleName) {
        string path = Path.Combine(IPathTools.GetAssetBundlePath(),bundleName);
        path = IPathTools.StandardPath(path);
        Debug.Log(path);
        return path;
    }
    public void LoadSingleObject(string bundleName, string resName, LoadObjectCallBack callBack)
    {
        //表示 是否已经缓存了物体
        if (loadObj.ContainsKey(bundleName))
        {
            AssetResObj tmpRes = loadObj[bundleName];
            Object tmpObj = tmpRes.GetResObj(resName);
            if (tmpObj != null) {
                callBack(tmpObj);
                return;
            }
        }
        //表示bundle已经加载过
        if (loadHelper.ContainsKey(bundleName))
        {
            LoadObjectFromBundle(bundleName, resName, callBack);
        }
        //加载bundle
        else
        {
            StartCoroutine(LoadAssetBundle(bundleName, (tmpBundleName) => {
                LoadObjectFromBundle(bundleName, resName, callBack);
            }));
        }

    }
    void LoadObjectFromBundle(string bundleName, string resName, LoadObjectCallBack callBack)
    {
        if (loadHelper.ContainsKey(bundleName))
        {
            IABRelationManager relation = loadHelper[bundleName];
            Object tmpObj = relation.GetSingleResource(resName);
            //把Object存缓存
            AssetObj tmpAssetObj = new AssetObj(tmpObj);
            if (!loadObj.ContainsKey(bundleName))
            {
                AssetResObj tmpRes = new AssetResObj(resName, tmpAssetObj);
                loadObj.Add(bundleName, tmpRes);
            }
            else {
                AssetResObj resObj = loadObj[bundleName];
                resObj.AddResObj(resName, tmpAssetObj);
            }
            if (callBack != null)
            {
                callBack(tmpObj);
            }
        }
        else {
            if (callBack != null)
            {
                callBack(null);
            }
        }
    }
    IEnumerator LoadAssetBundleDependences(string bundleName, string refName)
    {
        if (!loadHelper.ContainsKey(bundleName))
        {
            yield return LoadAssetBundle(bundleName);
        }
        if (refName != null)
        {
            IABRelationManager loader = loadHelper[bundleName];
            loader.AddRefference(refName);
        }
    }

    public IEnumerator LoadAssetBundle(string bundleName, LoadAssetBundleCallBack callBack = null)
    {
        if (!IABManifestLoader.Instance.IsLoadFinish())
        {
            yield return null;
        }
        //管理ab加载完成后的回调函数
        if (callBack != null) {
            if (loadBundleCallBack.ContainsKey(bundleName))
            {
                LoadAssetBundleCallBackManager callBackManager = loadBundleCallBack[bundleName];
                callBackManager.AddCallBack(callBack);
            }
            else {
                LoadAssetBundleCallBackManager callBackManager = new LoadAssetBundleCallBackManager(bundleName);
                callBackManager.AddCallBack(callBack);
                loadBundleCallBack.Add(bundleName, callBackManager);
            }
           
        }
        
        if (preLoadHelper.ContainsKey(bundleName))
        {
            yield return null;
        }
        else {
            IABRelationManager relation = new IABRelationManager();
            relation.Initial(bundleName);
            preLoadHelper.Add(bundleName, relation);
            string bundlePath = GetBundlePath(bundleName);
            WWW www = new WWW(bundlePath);
            yield return www;
            Debug.Log("bundlePath " + bundlePath);
            if (!string.IsNullOrEmpty(www.error))
            {
                Debug.Log(www.error);
                preLoadHelper.Remove(bundleName);
            }
            else
            {
                AssetBundle ab = www.assetBundle;
                string[] dependencies = IABManifestLoader.Instance.assetManifest.GetAllDependencies(ab.name);
                IABRelationManager tmpRelation = preLoadHelper[ab.name];
                tmpRelation.SetAssetBundle(ab);
                tmpRelation.SetDependences(dependencies);

                if (!loadHelper.ContainsKey(ab.name))
                {
                    loadHelper.Add(ab.name, relation);
                    preLoadHelper.Remove(bundleName);
                    Debug.Log("abName " + ab.name);
                }
                //加载依赖
                for (int i = 0; i < dependencies.Length; i++)
                {
                    if (!loadHelper.ContainsKey(dependencies[i]))
                    {
                        yield return LoadAssetBundleDependences(dependencies[i], ab.name);
                    }
                }
                if (loadBundleCallBack.ContainsKey(ab.name)) {
                    loadBundleCallBack[ab.name].CallBack(ab.name);
                }
            }
            www.Dispose();
        }
        
    }

    #endregion

    //对外接口
    //释放一个bundle中的一个资源
    public void DisposeResObj(string bundleName, string resName)
    {
        if (loadObj.ContainsKey(bundleName))
        {
            AssetResObj tmpObj = loadObj[bundleName];
            tmpObj.ReleaseResObj(resName);
        }
    }
    //释放整个bundle中的资源
    public void DisposeResObj(string bundleName)
    {
        if (loadObj.ContainsKey(bundleName))
        {
            AssetResObj tmpObj = loadObj[bundleName];
            tmpObj.ReleaseAllResObj();
            Resources.UnloadUnusedAssets();
        }
    }
    //释放所有的bundle加载出来的资源
    public void DisposeAllObj()
    {
        List<string> keys = new List<string>();
        keys.AddRange(loadObj.Keys);

        for (int i = 0; i < loadObj.Count; i++)
        {
            DisposeResObj(keys[i]);
        }
        loadObj.Clear();
    }
    public void DisposeBundle(string bundleName)
    {
        if (loadHelper.ContainsKey(bundleName))
        {
            IABRelationManager loader = loadHelper[bundleName];
            List<string> dependences = loader.GetDependences();
            for (int i = 0; i < dependences.Count; i++)
            {
                if (loadHelper.ContainsKey(dependences[i]))
                {
                    IABRelationManager dependence = loadHelper[dependences[i]];
                    if (dependence.RemoveReference(bundleName))
                    {
                        DisposeBundle(dependence.GetBundleName());
                    }
                    dependence.RemoveReference(bundleName);
                }
            }
            if (loader.GetRefference().Count <= 0)
            {
                loader.Dispose();
                loadHelper.Remove(bundleName);
            }
        }
    }

    public void DisposeAllBundle()
    {
        List<string> keys = new List<string>();
        keys.AddRange(loadHelper.Keys);
        for (int i = 0; i < loadHelper.Count; i++)
        {
            IABRelationManager loader = loadHelper[keys[i]];
            loader.Dispose();
        }
        loadHelper.Clear();
    }

    public void DisposeAllBundleAndRes()
    {
        DisposeAllObj();
        DisposeAllBundle();
    }
}


IPathTools.cs


using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Collections.Generic;

public class IPathTools {
    public static string GetPlatformFolderName(RuntimePlatform platform) {
        switch (platform) { 
            case RuntimePlatform.Android:
                return "Android";
            case RuntimePlatform.IPhonePlayer:
                return "IOS";
            case RuntimePlatform.WindowsPlayer:
            case RuntimePlatform.WindowsEditor:
                return "windows";
            case RuntimePlatform.OSXPlayer:
            case RuntimePlatform.OSXEditor:
                return "OSX";
            default:
                return null;
        }
    }
    public static string GetAppFilePath() {

        string tmpPath = "";
#if UNITY_ANDROID
            tmpPath = "jar:file://" + Application.streamingAssetsPath;
#elif UNITY_IPHONE
            tmpPath = Application.streamingAssetsPath;
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
        tmpPath = "file://" + Application.streamingAssetsPath;
#else
            tmpPath = string.Empty;
#endif
        return tmpPath;
    }
    public static string GetAssetBundlePath() {
        string platFolder = GetPlatformFolderName(Application.platform);
        string allPath = Path.Combine(GetAppFilePath(), platFolder);
        allPath = StandardPath(allPath);
        return allPath;
    }
    public static string GetEditeABOutPath() {
        string tmpPath = "";
        if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.OSXEditor)
        {
            tmpPath =  Application.streamingAssetsPath;
        }
        else
        {
            tmpPath = Application.persistentDataPath;
        }
        return Path.Combine(tmpPath, GetPlatformFolderName(Application.platform));
    }
    public static string GetABManifestName() {
        string name = GetPlatformFolderName(Application.platform) ;
        string path = GetAssetBundlePath();
        return StandardPath(Path.Combine(path, name));
    }
    public static string StandardPath(string oldPath) {
        string newPath = oldPath;
        if (oldPath.Contains("\\")) {
            newPath = oldPath.Replace("\\","/");
        }
        return newPath;
    }
}



TestLoad.cs


using UnityEngine;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class TestLoad : MonoBehaviour {

    // Use this for initialization
    public GameObject go;
    public void Load() {
        IABManager.instance.LoadSingleObject("load.ab", "Cube", (o) =>
        {
            if (o != null)
            {
                go = Instantiate(o, Vector3.zero, Quaternion.identity) as GameObject;
            }
        });
    }

    public void ChangeMaterial() {
        MeshRenderer mr = go.GetComponent<MeshRenderer>();
        IABManager.instance.LoadSingleObject("material.ab", "m_Material2", (o)=>{
            Debug.Log("o"+o.name);

            if (o != null)
            {
                Material m = o as Material;
                mr.material = m;
            }
        });
    }

    public void DisposeBundle() {
        IABManager.instance.DisposeBundle("load.ab");
    }

}