unity 资源管理器设计

发布时间:2020-07-13 23:57:55 作者:chenshulove
来源:网络 阅读:1974

资源管理器,顾名思义,就是管理游戏中的所有资源,包括加载资源,回收资源,销毁资源等等。下面这个资源管理器主要提供了对assetbundle异步加载功能,Resources的加载没有放在里面。


一.使用方法

1.在进入游戏前调用Init(),加载一个资源的名称列表

2.调用AsynGetAsset(string name, Action<UnityEngine.Object> callback)方法(异步)


说明,这里的资源列表是从bundle_list列表中解析出来的,并且有一个manifest文件用来查找加载依赖的资源,所以,如果要使用这个资源管理器需要将您的资源进行依赖打包assetbundle,可以参考我以前的文章。http://chenshuhb.blog.51cto.com/6087203/1836562


using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using System;

using System.IO;

using LitJson;

using UnityEngine.SceneManagement;



/**

 * 资源管理器

 * 

 * 功能:

 * 1.加载基础配置数据

 * 2.为各个模块提供基础数据接口,供各个模块调用

 * 3. assetbundle,prefab资源的加载

 * 

 **/

public class ResourceManager : Singleton<ResourceManager> 

{

    //版本文件名

    private static readonly string VERSION_FILE = "resource_version";

    //assetbundle 路径

    private string BUNDLE_PATH = "";

    //本地Resource路径

    private string LOCAL_RES_PATH = "";


    private const string assetTail = ".unity3d";

    //版本文件

    private JsonData jdVersionFile = null;

    /// <summary>

    /// 资源文件字典

    /// key:资源名,value:资源路径

    /// </summary>

    private Dictionary<string, string> dicRes = new Dictionary<string, string>();

    /// <summary>

    /// 资源目录字典,需要获取目录下的所有资源

    /// </summary>

    private Dictionary<string, string> dicResDir = new Dictionary<string, string>();

    /// <summary>

    /// 用来存放加载出来的依赖资源的AssetBundle

    /// </summary>

    Dictionary<string, AssetBundle> dicDepenceAssetBundles = new Dictionary<string, AssetBundle>();

    /// <summary>

    /// 依赖资源名称(路径)list

    /// </summary>

    List<string> dependenceBundleNames = new List<string>();

    /// <summary>

    /// bundle资源的总manifest

    /// </summary>

    private AssetBundleManifest m_manifest = null;

    /// <summary>

    /// 已经加载完成的依赖资源的数量

    /// </summary>

    int finishedDependenceCount = 0;

    /// <summary>

    /// 所有依赖资源的数量

    /// </summary>

    int allDependenceCount = 0;

    #region LoadAssetBundle

    /// <summary>

    /// 初始化

    /// </summary>

    public void Init()

    {


#if UNITY_EDITOR && UNITY_ANDROID

        BUNDLE_PATH = "file:///" + Application.persistentDataPath + "/res/";

#elif UNITY_EDITOR && UNITY_IOS

        BUNDLE_PATH = Application.streamingAssetsPath + "/ios/";

#elif  UNITY_EDITOR_WIN

        BUNDLE_PATH = "file:///" + Application.streamingAssetsPath + "/windows/";

#elif UNITY_ANDROID

        BUNDLE_PATH = "file://" + Application.persistentDataPath + "/res/";

#elif UNITY_IOS

        BUNDLE_PATH = Application.persistentDataPath + "/res/";

#endif


        LOCAL_RES_PATH = Application.persistentDataPath + "/res/";


        loadResVersion();

    }


    /// <summary>

    /// 加载资源版本文件

    /// </summary>

    private void loadResVersion()

    {

        string version;

        if (File.Exists(LOCAL_RES_PATH + VERSION_FILE))

            version = File.ReadAllText(LOCAL_RES_PATH + VERSION_FILE);

        else

            return;


        jdVersionFile = JsonMapper.ToObject(version);


        if (null == jdVersionFile)

            Debug.LogError("Config file is null");


        parse(jdVersionFile);

    }


    #region 加载资源方法

    /// <summary>

    /// 获取资源

    /// </summary>

    /// <param name="name">资源名</param>

    /// <param name="callback"></param>

    public void AsynGetAsset(string name, Action<UnityEngine.Object> callback)

    {

        name = name + assetTail;

        if (!dicRes.ContainsKey(name))

        {

            Debug.Log("AsynGetAsset: The asset " + name + " does not exist!");

            if (null != callback)

                callback(null);

        }


        string path = dicRes[name];


        Game.Instance.StartCoroutine(LoadAssetBundle(path, (obj) =>

            {

                callback(obj);

            }));

    }


    /// <summary>

    /// 释放依赖资源

    /// </summary>

    public void UnloadUnusedAssetbundle()

    {

        foreach (KeyValuePair<string,AssetBundle> kvp in dicDepenceAssetBundles)

        {

            AssetBundle ab = kvp.Value;

            if (null != ab)

                ab.Unload(false);

        }

        dicDepenceAssetBundles.Clear();

    }


    #endregion


    /// <summary>

    /// 要加载的资源名称列表

    /// </summary>

    List<string> assetNameList = new List<string>();

    /// <summary>

    /// 资源是否已经加载完成

    /// </summary>

    bool isOk = true;

    /// <summary>

    /// 加载目标资源

    /// </summary>

    /// <param name="name"></param>

    /// <param name="callback"></param>

    IEnumerator LoadAssetBundle(string name, Action<UnityEngine.Object> callback)

    {

        assetNameList.Add(name);

        Action<Dictionary<string, AssetBundle>> action = (depenceAssetBundles) =>

        {

            string realName = name;// this.GetRuntimePlatform() + "/" + name;//eg:Windows/ui/panel.unity3d

            LoadResReturnWWW(realName, (www) =>

            {

                int index = realName.LastIndexOf("/");

                string assetName = realName.Substring(index + 1);

                //去掉".unity3d"后缀

                assetName = assetName.Replace(assetTail, "");


                //去掉扩展名

                //int extIndex = assetName.LastIndexOf(".");

                //string extName = assetName.Substring(extIndex);

                //assetName = assetName.Replace(extName, "");


                AssetBundle assetBundle = www.assetBundle;


                //重新给material 的Shader赋值

                resetShader(assetBundle);

                UnityEngine.Object obj = null;

                if (null != assetBundle)

                    obj = assetBundle.LoadAsset(assetName);//LoadAsset(name),这个name没有后缀,eg:panel

                else

                    Debug.Log("assetBundle is null assetName is " + assetName);

                //重新给material 的Shader赋值

                resetShader(obj);


                if (null != assetBundle)

                    assetBundle.Unload(false);//卸载资源内存

                //加载目标资源完成的回调

                callback(obj);


            });

            isOk = true;

        };


        while (assetNameList.Count > 0)

        {

            if (isOk)

            {

                isOk = false;

                string _name = assetNameList[0];

                assetNameList.RemoveAt(0);

                LoadDependenceAssets(_name, action);

            }


            yield return null;

        }


    }


    /// <summary>

    /// 加载目标资源的依赖资源

    /// </summary>

    /// <param name="targetAssetName"></param>

    /// <param name="action"></param>

    private void LoadDependenceAssets(string targetAssetName, Action<Dictionary<string, AssetBundle>> action)

    {

        //Debug.Log("LoadDependenceAssets : targetAssetName is " + targetAssetName);

        Action<AssetBundleManifest> dependenceAction = (manifest) =>

        {

            if (null == m_manifest)

                m_manifest = manifest;


            excuteLoad(targetAssetName, manifest, action);

        };


        if (null != m_manifest)

            excuteLoad(targetAssetName, m_manifest, action);

        else

            LoadAssetBundleManifest(dependenceAction);

    }


    /// <summary>

    /// 递归加载依赖

    /// </summary>

    void excuteLoad(string targetAssetName, AssetBundleManifest manifest, Action<Dictionary<string, AssetBundle>> action)

    {

        finishedDependenceCount = 0;

        dependenceBundleNames.Clear();

        string[] de = manifest.GetAllDependencies(targetAssetName);

        allDependenceCount = de.Length;


        //如果要加载的资源没有依赖资源,直接回调返回

        if (allDependenceCount == 0)

        {

            action(null);

        }

        else

            loadSubDependence(targetAssetName, manifest, action, allDependenceCount);//, ref depenceAssetBundles, ref dependenceBundleNames);


    }


    /// <summary>

    /// 加载依赖

    /// </summary>

    /// <param name="targetAssetName"></param>

    /// <param name="manifest"></param>

    /// <param name="action"></param>

    void loadSubDependence(string targetAssetName, AssetBundleManifest manifest, Action<Dictionary<string, AssetBundle>> action,

        int allDependenceCount)//,ref Dictionary<string, AssetBundle> depenceAssetBundles,ref List<string> dependenceBundleNames)

    {

        string[] dependences = manifest.GetDirectDependencies(targetAssetName);


        int length = dependences.Length;

        if (length == 0)

        {

            //没有依赖

            return;

        }

        else

        {

            //有依赖,加载所有依赖资源

            for (int i = 0; i < length; i++)

            {

                string dependenceAssetName = dependences[i];


                //递归                        

                loadSubDependence(dependenceAssetName, manifest, action, allDependenceCount);//,ref depenceAssetBundles,ref dependenceBundleNames);


                //判断资源是否已经被加载过

                if (dependenceBundleNames.Contains(dependenceAssetName))

                    continue;

                dependenceBundleNames.Add(dependenceAssetName);


                if (!dicDepenceAssetBundles.ContainsKey(dependenceAssetName))

                {

                    //加载

                    LoadResReturnWWW(dependenceAssetName, (www) =>

                    {

                        int index = dependenceAssetName.LastIndexOf("/");

                        string assetName = dependenceAssetName.Substring(index + 1);

                        //去掉".unity3d"后缀

                        assetName = assetName.Replace(assetTail, "");


                        //去掉扩展名

                        //int extIndex = assetName.LastIndexOf(".");

                        //string extName = assetName.Substring(extIndex);

                        //assetName = assetName.Replace(extName, "");


                        AssetBundle assetBundle = www.assetBundle;


                        //重新给material 的Shader赋值

                        resetShader(assetBundle);


                        if (null == assetBundle)

                            Debug.LogError("null == assetBundle : " + dependenceAssetName);

                        UnityEngine.Object obj = null;


                        //try

                        //{

                            obj = assetBundle.LoadAsset(assetName);


                        if (dicAtlasObj.ContainsKey(assetName))

                        {

                            if (obj == null)

                            {

#if REALMA_ONGUI_DEBUG

                                OnGUIDebug.AddMsg("atlas null name is " + assetName);

#endif

                                Debug.LogError("atlas null name is " + assetName);


                            }

                            else

                                dicAtlasObj[assetName] = obj;


                        }

                            

                        //重新给material 的Shader赋值

                        resetShader(obj);


                        if (!dicDepenceAssetBundles.ContainsKey(dependenceAssetName))

                            dicDepenceAssetBundles.Add(dependenceAssetName, assetBundle);

                        finishedDependenceCount++;


                        //依赖都加载完了

                        if (finishedDependenceCount == allDependenceCount)

                        {

                            action(dicDepenceAssetBundles);

                        }

                    });

                }

                else

                {

                    finishedDependenceCount++;

                    //依赖都加载完了

                    if (finishedDependenceCount == allDependenceCount)

                    {

                        action(dicDepenceAssetBundles);

                    }

                }

            }

        }

    }


    public UnityEngine.Object GetAtlasObj(string name)

    {

        if (dicAtlasObj.ContainsKey(name))

            return dicAtlasObj[name];

        return null;

    }


    //UnityEditor 在 Android 下打出来的 Android 的 Assetbundle,加载之后 Shader 不能正确的执行!!!

    //解决办法就是,在 Assetbundle Load完之后,遍历 Material ,把所有的 Shader 都重新 Load 赋值一次。

    //通过Assetbundle加载出来的资源有两种类型需要处理:

    //1.如果加载出来的是材质,那么可以对shader进行赋值

    //2.如果加载出来的是GameObject,那么需要获取到Renderer组件下的所有材质,再对每个材质的shader赋值

    /// </summary>

    void resetShader(UnityEngine.Object obj)

    {

        List<Material> listMat = new List<Material>();

        listMat.Clear();


        if (obj is Material)

        {

            Material m = obj as Material;

            listMat.Add(m);

        }

        else if (obj is GameObject)

        {

            GameObject go = obj as GameObject;

            Renderer rend = go.GetComponent<Renderer>();

            if (null != rend)

            {

                Material[] materialsArr = rend.sharedMaterials;

                foreach (Material m in materialsArr)

                    listMat.Add(m);

            }

        }


        for (int i = 0; i < listMat.Count; i++)

        {

            Material m = listMat[i];

            if (null == m)

                continue;

            var shaderName = m.shader.name;

            var newShader = Shader.Find(shaderName);

            if (newShader != null)

                m.shader = newShader;

            else

                Debug.LogWarning("unable to refresh shader: " + shaderName + " in material " + m.name);

        }

    }


    /// <summary>

    /// 给assetBundle中的材质的shader重新赋值

    /// </summary>

    /// <param name="assetBundle"></param>

    void resetShader(AssetBundle assetBundle)

    {

        if (null == assetBundle)

            return;


        var materials = assetBundle.LoadAllAssets(typeof(Material));

        foreach (Material m in materials)

        {

            var shaderName = m.shader.name;

            var newShader = Shader.Find(shaderName);

            if (newShader != null)

                m.shader = newShader;

            else

                Debug.LogWarning("unable to refresh shader: " + shaderName + " in material " + m.name);

        }


    }

    /// <summary>

    /// 加载AssetBundleManifest

    /// </summary>

    /// <param name="action"></param>

    private void LoadAssetBundleManifest(Action<AssetBundleManifest> action)

    {

        string manifestName = this.GetRuntimePlatform();

        LoadResReturnWWW(manifestName, (www) =>

        {

            AssetBundle assetBundle = www.assetBundle;

            UnityEngine.Object obj = assetBundle.LoadAsset("AssetBundleManifest");

            assetBundle.Unload(false);

            AssetBundleManifest manif = obj as AssetBundleManifest;

            action(manif);

        });

    }

#endregion


#region ExcuteLoader

    public void LoadResReturnWWW(string name, Action<WWW> callback)

    {


        string path = "";

#if UNITY_EDITOR_WIN

        path = BUNDLE_PATH + name;

#elif UNITY_ANDROID

        path = BUNDLE_PATH + name;

#endif

        Game.Instance.StartCoroutine(LoaderRes(path, callback));

    }


    IEnumerator LoaderRes(string path, Action<WWW> callback)

    {

        WWW www = new WWW(path);

        yield return www;

        if (!string.IsNullOrEmpty(www.error))

        {

            Debug.Log("www.error is " + www.error);

        }

        if (www.isDone)

        {

            callback(www);

        }

    }

#endregion


    /// <summary>

    /// 解析版本文件

    /// </summary>

    void parse(JsonData jd)

    {

        JsonData resInfos = null;


        if (jd.Keys.Contains("resource"))

            resInfos = jd["resource"];


        if (null == resInfos)

        {

            Debug.LogError("解析资源版本文件出错");


            return;

        }


        string[] resNames = new string[resInfos.Count];

        resInfos.Keys.CopyTo(resNames, 0);


        for (int i = 0; i < resNames.Length; i++)

        {

            string name = resNames[i].Substring(resNames[i].LastIndexOf("/") + 1);

            if (!dicRes.ContainsKey(name))

                dicRes.Add(name, resNames[i]);


        }

    }


#region Util

    /// <summary>

    /// 平台对应文件夹

    /// </summary>

    /// <returns></returns>

    private string GetRuntimePlatform()

    {

        string platform = "";

        if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)

        {

            //platform = "Windows";

            platform = "android";


        }

        else if (Application.platform == RuntimePlatform.Android)

        {

            platform = "android";

        }

        else if (Application.platform == RuntimePlatform.IPhonePlayer)

        {

            platform = "ios";

        }

        else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)

        {

            platform = "osx";

        }

        return platform;

    }

#endregion

}


肯定还有更好的和更完整的资源管理方案,在此跪求大神分享。

推荐阅读:
  1. [Unity3d]单例模式
  2. UNITY如何动态加载R素材?

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

资源 unity 异步加载

上一篇:Oracle锁表故障处理一例

下一篇: 安卓通讯录怎么导入iphone

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》