05
2017
10

ResourceManager(六)—— ResourcesManager

目录为:Assets/Scripts/ResourceManager/
ResourcesManager.cs

具体怎么用,下一篇详细写写吧。

ResourceManager.cs

using System;
using System.IO;
using System.Xml;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Object = UnityEngine.Object;

//---------------------------------------------------------------
//这个ResourcesManager非常重要 |
//---------------------------------------------------------------

//Resources Manager
//继承Unity单例
//这种一般是添加到GameObject上成为一个component 
public class ResourcesManager: UnitySingleton<ResourcesManager>
{
    //是否通过assetbundle加载资源
    public bool UsedAssetBundle = false;

    //是否已经初始化
    private bool mInit = false;

    //帧数
    private int mFrameCount = 0;

    //当前的请求
    private Request mCurrentRequest = null;

    //请求队列
    private Queue<Request> mAllRequests = new Queue<Request>();

    //保存读取的Resource信息
    private AssetInfoManager mAssetInfoManager = null;

    private Dictionary<string, string> mResources = new Dictionary<string, string>();
    //加载的资源信息
    private Dictionary<string, ResourceUnit> mLoadedResourceUnit = new Dictionary<string, ResourceUnit>();

    public delegate void HandleFinishLoad(ResourceUnit resource);
    public delegate void HandleFinishLoadLevel();
    public delegate void HandleFinishUnLoadLevel();

    //初始化
    //在这里面先读取AssetInfo.bytes和Resource.bytes两个文件
    public void Init()
    {
        //使用assetbundle打包功能
        if (UsedAssetBundle)
        {
            //AssetInfo.bytes
            mAssetInfoManager = new AssetInfoManager ();
            mAssetInfoManager.LoadAssetInfo ();

            //Resources.bytes
            ArchiveManager.Instance.Init ();
        }

        mInit = true;
    }

    //Update函数
    public void Update()
    {
        //检查是否Init过
        if (!mInit)
        {
            return;
        }

        //当前Request已处理完,但请求队列中还有请求
        if (null == mCurrentRequest && mAllRequests.Count > 0)
        {
            handleRequest ();
        }

        ++mFrameCount;
        if (mFrameCount == 300)
        {
            //Resources.UnloadUnusedAssets();
            mFrameCount = 0;
        }
    }

    //主要函数
    //处理资源相关请求
    private void handleRequest()
    {
        //使用assetbundle打包功能
        if (UsedAssetBundle)
        {
            //获取当前的请求
            mCurrentRequest = mAllRequests.Dequeue ();

            //相对Asset的完整资源路径
            string fileName = mCurrentRequest.mFileName;

            //这里是 RequestType
            switch (mCurrentRequest.mRequestType)
            {
            //这个分支里面只检查是否加载过资源,而并不负责加载资源
            case RequestType.LOAD:
                {
                    //这里是 ResourceType
                    switch (mCurrentRequest.mResourceType)
                    {
                    //资源为ASSET和PREFAB时
                    case ResourceType.ASSET:
                    case ResourceType.PREFAB:
                        {
                            if (mLoadedResourceUnit.ContainsKey (fileName))
                            {
                                //已经加载过了,所以completed
                                mCurrentRequest.mResourceAsyncOperation.mComplete = true;
                                mCurrentRequest.mResourceAsyncOperation.mResource = mLoadedResourceUnit [fileName] as ResourceUnit;

                                if (null != mCurrentRequest.mHandle)
                                {
                                    mCurrentRequest.mHandle (mLoadedResourceUnit [fileName] as ResourceUnit);
                                }

                                handleResponse ();
                            }
                            //没加载过
                            else
                            {
                                //传入相对路径名称
                                //StartCoroutine(_load(fileName, mCurrentRequest.mHandle, mCurrentRequest.mResourceType, mCurrentRequest.mResourceAsyncOperation));

                                //啥也没做
                            }
                        }
                        break;
                    //-------------------------------
                    case ResourceType.LEVELASSET:
                        {
                            Debug.LogError ("do real need a single level asset??? this is have not decide!!!", ResourceCommon.DEBUGTYPENAME);
                        }
                        break;
                    //--------------------------------
                    case ResourceType.LEVEL:
                        {
                            Debug.LogError ("this is impossible!!!", ResourceCommon.DEBUGTYPENAME);
                        }
                        break;
                    }
                }
                break;
                //卸载资源
            case RequestType.UNLOAD:
                {
                    if (!mLoadedResourceUnit.ContainsKey (fileName))
                    {
                        Debug.LogError ("can not find " + fileName);
                    }
                    else
                    {
                        //(mLoadedResourceUnit[fileName] as ResourceUnit).reduceReferenceCount();
                    }

                    handleResponse ();
                }
                break;
                //加载场景
            case RequestType.LOADLEVEL:
                {
                    //加载场景,会先加载场景所用到的所以依赖资源,再异步加载场景
                    StartCoroutine (_loadLevel (fileName, mCurrentRequest.mHandleLevel, ResourceType.LEVEL, mCurrentRequest.mResourceAsyncOperation));
                }
                break;
                //卸载场景
            case RequestType.UNLOADLEVEL:
                {
                    if (!mLoadedResourceUnit.ContainsKey (fileName))
                    {
                        Debug.LogError ("can not find level" + fileName, ResourceCommon.DEBUGTYPENAME);
                    }
                    else
                    {
                        //(mLoadedResourceUnit[fileName] as ResourceUnit).reduceReferenceCount();

                        if (null != mCurrentRequest.mHandleUnloadLevel)
                        {
                            mCurrentRequest.mHandleUnloadLevel ();
                        }
                    }

                    handleResponse ();
                }
                break;
            }
        }
        //不使用AssetBundle打包
        //这个分支其实几乎就没什么用,因为一般是会用AssetBundle打包
        else
        {
            //获取Request
            mCurrentRequest = mAllRequests.Dequeue ();

            switch (mCurrentRequest.mRequestType)
            {
            case RequestType.LOAD:
                {
                    switch (mCurrentRequest.mResourceType)
                    {
                    case ResourceType.ASSET:
                    case ResourceType.PREFAB:
                        {
                            //暂时不处理,直接使用资源相对路径
                            //StartCoroutine(_load(mCurrentRequest.mFileName, mCurrentRequest.mHandle, mCurrentRequest.mResourceType, mCurrentRequest.mResourceAsyncOperation));
                        }
                        break;
                    //-----------------------------------
                    case ResourceType.LEVELASSET:
                        {
                            Debug.LogError ("do you real need a single level asset??? this is have not decide!!!", ResourceCommon.DEBUGTYPENAME);
                        }
                        break;
                    //-----------------------------------
                    case ResourceType.LEVEL:
                        {
                            Debug.LogError ("this is impossible!!!", ResourceCommon.DEBUGTYPENAME);
                        }
                        break;
                    }
                }
                break;
            case RequestType.UNLOAD:
                {
                    handleResponse ();
                }
                break;
            case RequestType.LOADLEVEL:
                {
                    StartCoroutine (_loadLevel (mCurrentRequest.mFileName, mCurrentRequest.mHandleLevel, ResourceType.LEVEL, mCurrentRequest.mResourceAsyncOperation));
                }
                break;
            case RequestType.UNLOADLEVEL:
                {
                    if (null != mCurrentRequest.mHandleUnloadLevel)
                    {
                        mCurrentRequest.mHandleUnloadLevel ();
                    }

                    handleResponse ();
                }
                break;
            }
        }
    }

    //表示当前Request已经完成
    private void handleResponse()
    {
        mCurrentRequest = null;
    }

    //传入Resources下相对路径名称 例如:Resources/Game/Effect1 传入GameEffect1
    public ResourceUnit loadImmediate(string filePathName, ResourceType resourceType, string archiveName = "Resources")
    {
        //使用assetbundle打包
        if (UsedAssetBundle)
        {
            //添加Resource
            string completePath = "Resources/" + filePathName;

            string completeName = ArchiveManager.Instance.GetPath ("Resources", completePath);

            //根据场景名称获取asset信息
            AssetInfo sceneAssetInfo = mAssetInfoManager.GetAssetInfo (completeName);

            //获取依赖的asset索引
            foreach(int index in sceneAssetInfo.mDependencies)
            {
                //根据索引获取依赖的Asset
                AssetInfo depencyAsset = mAssetInfoManager.GetAssetInfo (index);
                string depencyAssetName = depencyAsset.mName;

                //加载asset
                _LoadImmediate (depencyAssetName, ResourceType.ASSET);
            }

            //加载自己本身
            ResourceUnit unit = _LoadImmediate (completeName, resourceType);

            return unit;
        }
        //不使用assetbundle
        else
        {
            Object asset = Resources.Load (filePathName);
            ResourceUnit resource = new ResourceUnit (null, 0, asset, null, resourceType);

            return resource;
        }
    }

    //-------------------------------------------------------------------
    //这个函数是主要的函数,
    //调用这个函数,然后会将加载scene的Request加入RequestQueue,
    //然后在ResourcesManager的Update函数中会处理这个Request,真正地加载Scene
    //-------------------------------------------------------------------
    public ResourceAsyncOperation loadLevel(string fileName, HandleFinishLoadLevel handle, string archiveName = "Level")
    {
        //使用assetbundle打包
        if (UsedAssetBundle)
        {
            //ResourceCommon.Log("loadLevel : " + fileName + " and the archiveName is : " + archiveName);

            //获取完整路径
            string completeName = ArchiveManager.Instance.GetPath(archiveName, fileName);
            if (mLoadedResourceUnit.ContainsKey(completeName))
            {
                Debug.LogError ("why you load same level twice, maybe you have not unload last time!!!", ResourceCommon.DEBUGTYPENAME);
                return null;
            }
            else
            {
                ResourceAsyncOperation operation = new ResourceAsyncOperation (RequestType.LOADLEVEL);
                mAllRequests.Enqueue (new Request (completeName, ResourceType.LEVEL, handle, RequestType.LOADLEVEL, operation));

                return operation;
            }
        }
        //不使用assetbundle
        else
        {
            ResourceAsyncOperation operation = new ResourceAsyncOperation (RequestType.LOADLEVEL);
            mAllRequests.Enqueue (new Request (fileName, ResourceType.LEVEL, handle, RequestType.LOADLEVEL, operation));

            return operation;
        }
    }

    //path:相对Assets的完整资源路径
    //加载一个level,然后也把所有dependencies都加载了
    //先加载场景依赖的所有资源,再加载场景本身
    private IEnumerator _loadLevel(string path, HandleFinishLoadLevel handle, ResourceType resourceType, ResourceAsyncOperation operation)
    {
        //使用assetbundle打包
        if (UsedAssetBundle)
        {
            //根据场景名称获取asset信息
            AssetInfo sceneAssetInfo = mAssetInfoManager.GetAssetInfo (path);
            //获取该包总大小
            operation.mAllDependenciesAssetSize = mAssetInfoManager.GetAllAssetSize (sceneAssetInfo);

            //获取依赖的asset的索引
            foreach (int index in sceneAssetInfo.mDependencies)
            {
                //根据索引获取依赖的asset
                AssetInfo depencyAsset = mAssetInfoManager.GetAssetInfo (index);
                string depencyAssetName = depencyAsset.mName;

                //加载场景依赖assetbundle
                //每一个AssetBundle文件都应当有一个ResourceUnit,这里加载了然后mLoadedResourceUnit
                ResourceUnit unit = _LoadImmediate (depencyAssetName, ResourceType.LEVEL);
                operation.mLoadDependenciesAssetSize += unit.AssetBundleSize;
            }

            //加载场景
            int sceneAssetBundleSize = 0;
            //加载其他那些小资源也是这么加载的
            byte[] binary = ResourceCommon.getAssetBundleFileBytes (path, ref sceneAssetBundleSize);
            AssetBundle assetBundle = AssetBundle.CreateFromMemoryImmediate (binary);
            if (!assetBundle)
            {
                Debug.LogError ("Create scene assetbundle " + path + "in _LoadImmediate failed");
            }

            //添加场景大小
            operation.mLoadDependenciesAssetSize += sceneAssetBundleSize;

            //path:相对Assets的完整路径
            //这里异步加载场景Level
            AsyncOperation asyncOperation = Application.LoadLevelAsync (ResourceCommon.getFileName (path, false));
            operation.asyncOperation = asyncOperation;
            yield return asyncOperation;

            handleResponse ();

            operation.asyncOperation = null;
            operation.mComplete = true;
            operation.mResource = null;

            if (null != handle)
            {
                handle ();
            }
        }
        //不使用assetbundle
        else
        {
            ResourceUnit level = new ResourceUnit (null, 0, null, path, resourceType);

            //获取加载场景名称
            string sceneName = ResourceCommon.getFileName (path, true);
            AsyncOperation asyncOperation = Application.LoadLevelAsync (sceneName);
            operation.asyncOperation = asyncOperation;
            yield return asyncOperation;

            handleResponse ();

            operation.asyncOperation = null;
            operation.mComplete = true;

            if (null != handle)
            {
                handle ();
            }
        }
    }

    //单个资源加载
    ResourceUnit _LoadImmediate(string fileName, ResourceType resourceType)
    {
        //没有该资源,加载
        if (!mLoadedResourceUnit.ContainsKey(fileName))
        {
            //资源大小
            int assetBundleSize = 0;

            //加载Assetbundle文件,获取bytes
            byte[] binary = ResourceCommon.getAssetBundleFileBytes (fileName, ref assetBundleSize);
            //bytes加载成AssetBundle
            AssetBundle assetBundle = AssetBundle.CreateFromMemoryImmediate (binary);

            if (!assetBundle)
            {
                Debug.LogError ("create assetbundle " + fileName + " in _LoadImmediate failed");
            }

            Object asset = assetBundle.Load (fileName);

            if (!asset)
            {
                Debug.LogError ("load assetbundle " + fileName + "in _LoadImmediate failed");
            }

            ResourceUnit ru = new ResourceUnit (assetBundle, assetBundleSize, asset, fileName, resourceType);

            //添加到资源中
            mLoadedResourceUnit.Add (fileName, ru);

            return ru;
        }
        else
        {
            return mLoadedResourceUnit [fileName];
        }
    }

    //这个目录是当前运行环境的沙箱目录的相对目录
    public static Stream Open(string path)
    {
        string localPath;
        //Android跟IOS环境使用沙箱目录
        if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
        {
            //啥项目录 + "path" + ".bytes"
            localPath = string.Format ("{0}/{1}", Application.persistentDataPath, path + ResourceCommon.assetbundleFileSuffix);
        }
        //Windows下使用assetbundle资源目录
        else
        {
            //Application.dataPath + "/assetbundles/" + "path" + ".bytes"
            localPath = ResourceCommon.assetbundleFilePath + path + ResourceCommon.assetbundleFileSuffix;
        }

        //首先检查沙箱中是否有更新资源
        if (File.Exists(localPath))
        {
            return File.Open (localPath, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
        //没有的话原始包中查找
        else
        {
            //Resources.Load的相对目录是:Resources目录下
            TextAsset text = Resources.Load (path) as TextAsset;
            if (null == text)
            {
                Debug.LogError ("can not find : " + path + " in OpengText", ResourceCommon.DEBUGTYPENAME);
            }

            return new MemoryStream (text.bytes);
        }
    }

    public static StreamReader OpenText(string path)
    {
        return new StreamReader (Open (path), System.Text.Encoding.Default);
    }

}
上一篇:Android应用层通信机制 下一篇:React Native项目结构