Unity5 AssetBundle资源管理架构设计

本文介绍两大内容:

1:Unity5 资源管理架构设计(2017.4.22版本)

2:Android 热更新(不考虑IOS)根据C#反射实现的代码全更新方案(网上一大坨,我重新整理一下)。



一:Unity资源管理架构设计

注意:我配置的Bundle资源文件都放在Assets/ResourceABs文件夹下,并且此文件夹下每个文件夹都对应一个Bundle文件,最终这些文件都打包到StreamingAssets流文件夹下。

1:设计一个资源信息管理类,能够反映Assets/ResourceABs文件夹下的全部的资源信息。
生成工具放在Editor文件下, 代码如下:
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEditor;
using xk_System.AssetPackage;

public class ExportAssetInfoEditor : MonoBehaviour
{
    static string extention = AssetBundlePath.ABExtention;
    static string BuildAssetPath = "Assets/ResourceABs";
    static string CsOutPath = "Assets/Scripts/auto";

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~创建AB文件所有的信息~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [MenuItem("UnityEditor/GenerationPackage/Generation AssetInfo Cs File")]
    public static void GenericAssetCSInfo()
    {
        Debug.Log("Start Generation AssetInfo Cs Info");
        CreateABCSFile();
        Debug.Log("Finish Generation AssetInfo Cs Info");
    }

    private static void CreateABCSFile()
    {
        string m = "";
        m += "namespace xk_System.AssetPackage\n{\n";
        DirectoryInfo mDir = new DirectoryInfo(BuildAssetPath);
        m += "\tpublic class " + mDir.Name + "Folder : Singleton<" + mDir.Name + "Folder>\n\t{\n";
        string s = "";
        foreach (var v in mDir.GetDirectories())
        {
            FileInfo[] mFileInfos1 = v.GetFiles();
            int mFilesLength1 = 0;
            foreach (var v1 in mFileInfos1)
            {
                if (v1.Extension != ".meta")
                {
                    mFilesLength1++;
                    break;
                }
            }
            if (mFilesLength1 > 0 || v.GetDirectories().Length > 0)
            {
                string fieldName = v.Name + "Folder";
                m += "\t\t public " + fieldName + " " + v.Name + "=new " + fieldName + "();\n";
               // s += CreateDirClass(v, v.Name.ToLower());
            }
        }
        foreach (var v in mDir.GetDirectories())
        {
            m += CreateDirClass(v, v.Name.ToLower());
        }
        m += "\t}\n";
       // m += s;
        m += "}\n";
        string fileName = CsOutPath + "/" + mDir.Name + ".cs";
        StreamWriter mSw = new StreamWriter(fileName, false);
        mSw.Write(m);
        mSw.Close();
    }

    private static string CreateDirClass(DirectoryInfo mDir, string bundleName)
    {
        string tStr = GetTStr(mDir);
        string m = "";
        string s = "";
        FileInfo[] mFileInfos = mDir.GetFiles();
        int mFilesLength = 0;
        foreach (var v in mFileInfos)
        {
            if (v.Extension != ".meta")
            {
                mFilesLength++;
                break;
            }
        }
        if (mFilesLength > 0)
        {
            string bundleName1 = bundleName+ extention;
            m = tStr+"public class " + mDir.Name + "Folder\n"+tStr+"{\n";
            foreach (var v in mFileInfos)
            {
                if (v.Extension != ".meta")
                {
                    string assetPath = GetAssetPath(v.FullName);
                    string fileName = v.Name.Substring(0, v.Name.LastIndexOf(v.Extension));
                    m += tStr+"\t public AssetInfo m" + fileName + "=new AssetInfo(\""+assetPath+"\",\"" + bundleName1 + "\",\"" + v.Name + "\");\n";
                }
            }
            m += tStr+"}\n";
        }
        else
        {
            if (mDir.GetDirectories().Length > 0)
            {

                m = tStr+"public class " + mDir.Name + "Folder\n"+tStr+"{\n";
                foreach (var v in mDir.GetDirectories())
                {
                    FileInfo[] mFileInfos1 = v.GetFiles();
                    int mFilesLength1 = 0;
                    foreach (var v1 in mFileInfos1)
                    {
                        if (v1.Extension != ".meta")
                        {
                            mFilesLength1++;
                            break;
                        }
                    }
                    if (mFilesLength1 > 0 || v.GetDirectories().Length > 0)
                    {
                        string fieldName = v.Name + "Folder";
                        m += tStr+"\t public " + fieldName + " " + v.Name + "=new " + fieldName + "();\n";
                    }
                }
                foreach (var v in mDir.GetDirectories())
                {
                    m += CreateDirClass(v, bundleName + "_" + v.Name.ToLower());
                }
                m += tStr+"}\n";
               // m += s;
            }
        }
        return m;
    }
    public static string GetTStr(DirectoryInfo mDir)
    {
        int coutT = 0;
        int index = mDir.FullName.IndexOf(@"ResourceABs\");
        if (index >= 0)
        {
            for(int j=0;j index)
                {
                    var v = mDir.FullName[j];
                    if (v.Equals('\\'))
                    {
                        coutT++;
                    }
                }
            }
        }
        coutT++;
        string tStr = "";
        int i = 0;
        while(i= 0)
        {
            assetPath = filePath.Remove(0, index);
            assetPath = assetPath.Replace(@"\","/");
        }
        return assetPath;
    }

}
到这里,这个资源基本信息管理类就处理好了。
2:我们就正式开始写资源管理架构类了
我们首先写一个AssetBundleManager类,这个类的目的专门用来更新完毕后,充当资源加载管理器。代码如下
using UnityEngine;
using System.Collections;
using xk_System.Debug;
using System.Collections.Generic;
using System.Xml;

namespace xk_System.AssetPackage
{
    /// 
    /// 此类的目的就是加载本地的Bundle进行资源读取操作的
    /// 
    public class AssetBundleManager : SingleTonMonoBehaviour
    {
        private ResourcesABManager mResourcesABManager = new ResourcesABManager();
        private Dictionary mBundleDic = new Dictionary();
        private Dictionary> mAssetDic = new Dictionary>();
        private List mBundleLockList = new List();

        /// 
        /// 加载Assetbundle方案1:初始化时,全部加载
        /// 
        /// 
        public IEnumerator InitLoadAllBundleFromLocal()
        {
            yield return mResourcesABManager.InitLoadMainifestFile();
            List bundleList = mResourcesABManager.mNeedLoadBundleList;
            List.Enumerator mIter = bundleList.GetEnumerator();
            while (mIter.MoveNext())
            {
                yield return AsyncLoadFromLoaclSingleBundle(mIter.Current);
            }
        }
        public IEnumerator InitAssetBundleManager()
        {
            yield return mResourcesABManager.InitLoadMainifestFile();
        }

        private IEnumerator CheckBundleDependentBundle(AssetBundleInfo mBundle)
        {
            if (mBundle != null)
            {
                string[] mdependentBundles = mBundle.mDependentBundleList;
                foreach (string s in mdependentBundles)
                {
                    AssetBundleInfo mBundleInfo = mResourcesABManager.GetBundleInfo(s);
                    if (mBundleInfo != null)
                    {
                        AssetBundle mAB = null;
                        if (!mBundleDic.TryGetValue(mBundleInfo.bundleName, out mAB))
                        {
                            yield return AsyncLoadFromLoaclSingleBundle(mBundleInfo);
                        }
                        else
                        {
                            if (mAB == null)
                            {
                                yield return AsyncLoadFromLoaclSingleBundle(mBundleInfo);
                            }
                        }
                    }
                }
            }
        }
        /// 
        /// 从本地外部存储位置加载单个Bundle资源,全部加载
        /// 
        /// 
        /// 
        private IEnumerator AsyncLoadFromLoaclSingleBundle1(AssetBundleInfo BaseBundleInfo)
        {
            if(mBundleLockList.Contains(BaseBundleInfo.bundleName))
            {
                while(mBundleLockList.Contains(BaseBundleInfo.bundleName))
                {
                    yield return null;
                }
                yield break;
            }     
            mBundleLockList.Add(BaseBundleInfo.bundleName);
            yield return CheckBundleDependentBundle(BaseBundleInfo);
            string path = AssetBundlePath.Instance.ExternalStorePathUrl;
            string url = path + "/" + BaseBundleInfo.bundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    mBundleLockList.Remove(BaseBundleInfo.bundleName);
                    yield break;
                }
            }
            AssetBundle asset = www.assetBundle;
            SaveBundleToDic(BaseBundleInfo.bundleName, asset);
            mBundleLockList.Remove(BaseBundleInfo.bundleName);
            www.Dispose();
        }

        /// 
        /// 从本地外部存储位置加载单个Bundle资源,全部加载
        /// 
        /// 
        /// 
        private IEnumerator AsyncLoadFromLoaclSingleBundle(AssetBundleInfo BaseBundleInfo)
        {
            if (mBundleLockList.Contains(BaseBundleInfo.bundleName))
            {
                while (mBundleLockList.Contains(BaseBundleInfo.bundleName))
                {
                    yield return null;
                }
                yield break;
            }
            mBundleLockList.Add(BaseBundleInfo.bundleName);
            yield return CheckBundleDependentBundle(BaseBundleInfo);
            string path = AssetBundlePath.Instance.ExternalStorePath+"/"+BaseBundleInfo.bundleName;
            AssetBundleCreateRequest www= AssetBundle.LoadFromFileAsync(path);
            www.allowSceneActivation = true;
            yield return www;
            AssetBundle asset = www.assetBundle;
            SaveBundleToDic(BaseBundleInfo.bundleName, asset);
            mBundleLockList.Remove(BaseBundleInfo.bundleName);
        }
        /// 
        /// 异步从本地外部存储加载单个Asset文件,只加载Bundle中的单个资源
        /// 
        /// 
        /// 
        private IEnumerator AsyncLoadFromLocalSingleAsset(AssetBundleInfo bundle, string assetName)
        {
            if (bundle != null)
            {
                yield return AsyncLoadFromLoaclSingleBundle(bundle);
                UnityEngine.Object Obj = mBundleDic[bundle.bundleName].LoadAsset(assetName);
                if (Obj != null)
                {
                    DebugSystem.Log("Async Load Asset Success:" + Obj.name);
                    SaveAssetToDic(bundle.bundleName, assetName, Obj);
                }
            }
        }
        /// 
        /// 同步从本地外部存储加载单个Bundle文件
        /// 
        /// 
        /// 
        /// 
        private void SyncLoadFromLocalSingleBundle(string bundleName)
        {
            if (!JudegeOrExistBundle(bundleName))
            {
                string path = AssetBundlePath.Instance.ExternalStorePath + "/" + bundleName;
                AssetBundle asset = AssetBundle.LoadFromFile(path);
                SaveBundleToDic(bundleName, asset);
            }else
            {
                DebugSystem.LogError("Bundle 已存在:"+bundleName);
            }
        }

        /// 
        /// 同步从本地外部存储加载单个资源文件
        /// 
        /// 
        /// 
        /// 
        public UnityEngine.Object SyncLoadFromLocalSingleAsset(AssetInfo mAssetInfo)
        {
            if (!JudgeOrExistAsset(mAssetInfo.bundleName, mAssetInfo.assetName))
            {
                string path = AssetBundlePath.Instance.ExternalStorePath+"/"+mAssetInfo.bundleName;
                AssetBundle asset = AssetBundle.LoadFromFile(path);
                SaveBundleToDic(mAssetInfo.bundleName,asset);
            }
            return GetAssetFromDic(mAssetInfo.bundleName,mAssetInfo.assetName);
        }


        private void SaveBundleToDic(string bundleName, AssetBundle bundle)
        {
            if (bundle == null)
            {
                DebugSystem.LogError("未保存的Bundle为空:"+bundleName);
                return;
            }
            if (!mBundleDic.ContainsKey(bundleName))
            {
                mBundleDic[bundleName] = bundle;
            }else
            {
                DebugSystem.LogError("Bundle资源 重复:"+bundleName);
            }
        }

        private void SaveAssetToDic(string bundleName, string assetName, UnityEngine.Object asset)
        {
            if (asset == null)
            {
                DebugSystem.LogError("未保存的资源为空:"+assetName);
                return;
            }
            if(asset is GameObject)
            {
                GameObject obj = asset as GameObject;
                obj.SetActive(false);
            }
            if (!mAssetDic.ContainsKey(bundleName))
            {
                Dictionary mDic = new Dictionary();
                mAssetDic.Add(bundleName, mDic);
            }
            mAssetDic[bundleName][assetName] = asset;
        }

        private bool JudgeOrBundelIsLoading(string bundleName)
        {
            if (mBundleLockList.Contains(bundleName))
            {
                return true;
            }else
            {
                return false;
            }
        }

        private bool JudegeOrExistBundle(string bundleName)
        {
            if (mBundleDic.ContainsKey(bundleName) && mBundleDic[bundleName] != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool JudgeOrExistAsset(string bundleName, string asstName)
        {
            if (JudegeOrExistBundle(bundleName))
            {
                if (!mAssetDic.ContainsKey(bundleName) || mAssetDic[bundleName] == null || !mAssetDic[bundleName].ContainsKey(asstName) || mAssetDic[bundleName][asstName] == null)
                {
                    UnityEngine.Object mm = mBundleDic[bundleName].LoadAsset(asstName);
                    if (mm != null)
                    {
                        SaveAssetToDic(bundleName, asstName, mm);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return false;
            }
        }

        private UnityEngine.Object GetAssetFromDic(string bundleName, string asstName)
        {
            if (JudgeOrExistAsset(bundleName, asstName))
            {
                UnityEngine.Object mAsset1 = mAssetDic[bundleName][asstName];
                if (mAsset1 is GameObject)
                {
                    GameObject obj = Instantiate(mAsset1) as GameObject;
                    return obj;
                }
                else
                {
                    return mAsset1;
                }
            }
            else
            {
                DebugSystem.LogError("Asset is NUll:" + asstName);
            }
            return null;
        }
#if UNITY_EDITOR
        private Dictionary mEditorAssetDic = new Dictionary();

        private UnityEngine.Object GetAssetFromEditorDic(string assetPath)
        {
            if (string.IsNullOrEmpty(assetPath))
            {
                DebugSystem.LogError("Editor AssetPath is Empty");
                return null;
            }
            UnityEngine.Object asset = null;
            if (!mEditorAssetDic.TryGetValue(assetPath, out asset))
            {
                asset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath);
                if (asset != null)
                {
                    if (asset is GameObject)
                    {
                        GameObject obj = asset as GameObject;
                        obj.SetActive(false);
                    }
                    mEditorAssetDic.Add(assetPath, asset);
                }
                else
                {
                    DebugSystem.LogError("找不到资源:" + assetPath);
                }
            }
            if (asset is GameObject)
            {
                GameObject obj = Instantiate(asset) as GameObject;
                return obj;
            }
            else
            {
                return asset;
            }
        }
#endif
        public IEnumerator AsyncLoadBundle(string bundleName)
        {
            if (!JudegeOrExistBundle(bundleName))
            {
                string path = AssetBundlePath.Instance.ExternalStorePath + "/" + bundleName;
                AssetBundle asset = AssetBundle.LoadFromFile(path);
                SaveBundleToDic(bundleName, asset);
                yield return null;
            }          
        }
        /// 
        /// 这个东西用来在顶层使用
        /// 
        /// 
        /// 
        /// 
        public UnityEngine.Object LoadAsset(AssetInfo mAssetInfo)
        {
            if (GameConfig.Instance.orUseAssetBundle)
            {
                return GetAssetFromDic(mAssetInfo.bundleName, mAssetInfo.assetName);
            }
            else
            {
               return GetAssetFromEditorDic(mAssetInfo.assetPath);
            }
        }

        /// 
        /// 这个东西用来在专门的管理器中使用(底层封装一下),禁止在顶层使用
        /// 
        /// 
        /// 
        /// 
        public IEnumerator AsyncLoadAsset(AssetInfo mAssetInfo)
        {
            if (GameConfig.Instance.orUseAssetBundle)
            {
                string bundleName = mAssetInfo.bundleName;
                string asstName = mAssetInfo.assetPath;
                if (!JudgeOrExistAsset(bundleName, asstName))
                {
                    yield return AsyncLoadFromLocalSingleAsset(mResourcesABManager.GetBundleInfo(bundleName), asstName);
                }
            }
        }
    }

    public class ResourcesABManager
    {
        public int VersionId = -1;
        public List mNeedLoadBundleList = new List();
        public AssetBundleInfo GetBundleInfo(string bundleName)
        {
            AssetBundleInfo mBundleInfo = mNeedLoadBundleList.Find((x) =>
            {
                return x.bundleName == bundleName;
            });
            return mBundleInfo;
        }

        public IEnumerator InitLoadMainifestFile()
        {
            if (mNeedLoadBundleList.Count == 0)
            {
                string path = AssetBundlePath.Instance.ExternalStorePathUrl;
                string url = path + "/" + AssetBundlePath.AssetDependentFileBundleName;
                WWW www = new WWW(url);
                yield return www;
                if (www.isDone)
                {
                    if (!string.IsNullOrEmpty(www.error))
                    {
                        DebugSystem.LogError("初始化 MainifestFile 失败:" + www.error);
                        www.Dispose();
                        yield break;
                    }
                }
                AssetBundle asset = www.assetBundle;
                www.Dispose();
                if (asset == null)
                {
                    DebugSystem.LogError("MainifestFile Bundle is Null");
                    www.Dispose();
                    yield break;
                }

                AssetBundleManifest mAllBundleMainifest = asset.LoadAsset(AssetBundlePath.AssetDependentFileAssetName);
                if (mAllBundleMainifest == null)
                {
                    DebugSystem.LogError("Mainifest is Null");
                    www.Dispose();
                    yield break;
                }
                string[] mAssetNames = mAllBundleMainifest.GetAllAssetBundles();
                if (mAssetNames != null)
                {
                    foreach (var v in mAssetNames)
                    {

                        string bundleName = v;
                        string[] bundleDependentList = mAllBundleMainifest.GetAllDependencies(v);
                        Hash128 mHash = mAllBundleMainifest.GetAssetBundleHash(v);
                        AssetBundleInfo mABInfo = new AssetBundleInfo(bundleName, mHash, bundleDependentList);
                        mNeedLoadBundleList.Add(mABInfo);
                    }
                }
                else
                {
                    DebugSystem.Log("初始化资源依赖文件: Null");
                }
                asset.Unload(false);
                DebugSystem.Log("初始化资源管理器全局Bundle信息成功");
                www.Dispose();

                yield return InitLoadExternalStoreVersionConfig();
            }
        }

        private IEnumerator InitLoadExternalStoreVersionConfig()
        {
            string url = AssetBundlePath.Instance.ExternalStorePathUrl + "/" + AssetBundlePath.versionConfigBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            TextAsset mVersionConfig = mConfigBundle.LoadAsset(AssetBundlePath.versionConfigAssetName);
            VersionId = GetVersionIdByParseXML(mVersionConfig);
            DebugSystem.Log("当前版本号:"+VersionId);
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private int GetVersionIdByParseXML(TextAsset mTextAsset)
        {
            XmlDocument mdoc = new XmlDocument();
            mdoc.LoadXml(mTextAsset.text);
            foreach (XmlNode v in mdoc.ChildNodes)
            {
                if (v.Name == "root")
                {
                    foreach (XmlNode x in v.ChildNodes)
                    {
                        if (x.Name.Contains("versionId"))
                        {
                            return int.Parse(x.InnerText);
                        }
                    }
                }
            }
            return 0;
        }
    }

    public class AssetBundleInfo
    {
        public string bundleName;
        public Hash128 mHash;
        public string[] mDependentBundleList;

        public AssetBundleInfo(string bundleName, Hash128 mHash128, string[] mDependentBundleList)
        {
            this.bundleName = bundleName;
            this.mHash = mHash128;
            this.mDependentBundleList = mDependentBundleList;
        }

    }

    public class AssetInfo
    {
        public string bundleName;
        public string assetName;
        public string assetPath;

        public AssetInfo(string assetPath,string bundleName, string assetName)
        {
            this.assetPath = assetPath;
            this.bundleName = bundleName;
            this.assetName = assetName;
        }

        public AssetInfo(string bundleName, string assetName)
        {
            this.bundleName = bundleName;
            this.assetName = assetName;
        }
    }

    public class AssetBundlePath : Singleton
    {
        public const string versionConfigBundleName = "version.xk_unity3d";
        public const string versionConfigAssetName = "version.xml";
        public const string AssetDependentFileBundleName = "StreamingAssets";
        public const string AssetDependentFileAssetName = "AssetBundleManifest";
        public const string ABExtention = ".xk_unity3d";

        public readonly string StreamingAssetPathUrl;
        public readonly string ExternalStorePathUrl;
        public readonly string WebServerPathUrl;

        public readonly string ExternalStorePath;
        public AssetBundlePath()
        {
            if (Application.platform == RuntimePlatform.WindowsEditor)
            {
                WebServerPathUrl = "file:///F:/WebServer";
                StreamingAssetPathUrl = "file:///" + Application.streamingAssetsPath;
                ExternalStorePathUrl = "file:///" + Application.persistentDataPath;
                ExternalStorePath = Application.persistentDataPath;
            } else if (Application.platform == RuntimePlatform.WindowsPlayer)
            {
                WebServerPathUrl = "file:///F:/WebServer";
                StreamingAssetPathUrl = "file:///" + Application.streamingAssetsPath;
                ExternalStorePathUrl = "file:///" + Application.persistentDataPath;
                ExternalStorePath = Application.persistentDataPath;
            }else if(Application.platform == RuntimePlatform.Android)
            {
                WebServerPathUrl = "file:///F:/WebServer";
                StreamingAssetPathUrl = "jar:file://" + Application.dataPath + "!/assets";
                ExternalStorePathUrl = "file://" + Application.persistentDataPath;
                ExternalStorePath = Application.persistentDataPath;
            }
            DebugSystem.LogError("www server path: " + WebServerPathUrl);
            DebugSystem.LogError("www local Stream Path: " + StreamingAssetPathUrl);
            DebugSystem.LogError("www local external Path: " + ExternalStorePathUrl);
        }
    }

}
3:加载完本地的Bundle文件,那么现在我们开始下载Web服务器上的Bundle文件:
注意:本来我是自己定义一个md5配置文件专门用来比对资源,后来发现,Unity已经帮我们实现了这个功能。这个关键点就在于AssetBundleManifest类,具体请参考这篇文章末尾所讲的资源依赖配置文件:http://liweizhaolili.blog.163.com/blog/static/16230744201541410275298/
下载Web服务器资源代码如下:
using UnityEngine;
using System.Collections;
using xk_System.Debug;
using System.Collections.Generic;
using xk_System.AssetPackage;
using System.IO;
using System.Xml;

namespace xk_System.HotUpdate
{
    public class AssetBundleHotUpdateManager : Singleton
    {
        private int mStreamFolderVersionId=-1;
        private int mExternalStoreVersionId=-1;
        private int mWebServerVersionId=-1;

        private List mExternalStoreABInfoList = new List();
        private List mWebServerABInfoList = new List();
        private List mStreamFolderABInfoList = new List();

        private DownLoadAssetInfo mDownLoadAssetInfo = new DownLoadAssetInfo();

        public LoadProgressInfo mTask = new LoadProgressInfo();

        public IEnumerator CheckUpdate()
        {
            mTask.progress = 0;
            mTask.Des = "正在检查资源";
            yield return CheckVersionConfig();
            if (mDownLoadAssetInfo.mAssetNameList.Count > 0)
            {
                mTask.progress += 10;
                mTask.Des = "正在下载资源";
                yield return DownLoadAllNeedUpdateBundle();
            }
            else
            {
                mTask.progress = 100;
            }
        }

        /// 
        /// 检查版本配置文件
        /// 
        /// 
        private IEnumerator CheckVersionConfig()
        {
            yield return InitLoadExternalStoreVersionConfig();
            yield return InitLoadStreamFolderVersionConfig();
            yield return InitLoadWebServerVersionConfig();

            DebugSystem.Log("本地版本号:" + mExternalStoreVersionId);
            DebugSystem.Log("WebServer版本号:" + mWebServerVersionId);
            DebugSystem.Log("StreamFolder版本号:" + mStreamFolderVersionId);
            if (mWebServerVersionId > mExternalStoreVersionId)
            {
                yield return InitLoadExternalStoreABConfig();
                if (mWebServerVersionId > mStreamFolderVersionId)
                {
                    yield return InitLoadWebServerABConfig();
                    CheckAssetInfo(AssetBundlePath.Instance.WebServerPathUrl, mWebServerABInfoList);
                }
                else
                {
                    yield return InitLoadStreamFolderABConfig();
                    CheckAssetInfo(AssetBundlePath.Instance.StreamingAssetPathUrl, mStreamFolderABInfoList);
                }
            }
            else if (mStreamFolderVersionId > mExternalStoreVersionId)
            {
                yield return InitLoadExternalStoreABConfig();
                yield return InitLoadStreamFolderABConfig();
                CheckAssetInfo(AssetBundlePath.Instance.StreamingAssetPathUrl, mStreamFolderABInfoList);
            }
        }

        /// 
        /// 检查资源配置文件
        /// 
        /// 
        private void CheckAssetInfo(string url, List mUpdateABInfoList)
        {
            mDownLoadAssetInfo.url = url;
            foreach (AssetBundleInfo k in mUpdateABInfoList)
            {
                AssetBundleInfo mBundleInfo = mExternalStoreABInfoList.Find((x) =>
                {
                    if (x.mHash.isValid && k.mHash.isValid)
                    {
                        return x.mHash.Equals(k.mHash);
                    }
                    else
                    {
                        DebugSystem.LogError("Hash is no Valid");
                        return false;
                    }
                });
                if (mBundleInfo == null)
                {
                    mDownLoadAssetInfo.mAssetNameList.Add(k.bundleName);
                }
            }
            if (mDownLoadAssetInfo.mAssetNameList.Count > 0)
            {
                mDownLoadAssetInfo.mAssetNameList.Add(AssetBundlePath.AssetDependentFileBundleName);
            }
            DebugSystem.Log("需要下载更新的个数:" + mDownLoadAssetInfo.mAssetNameList.Count);
        }

        private IEnumerator InitLoadWebServerVersionConfig()
        {
            string url = AssetBundlePath.Instance.WebServerPathUrl + "/" + AssetBundlePath.versionConfigBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }

            AssetBundle mConfigBundle = www.assetBundle;
            TextAsset mVersionConfig = mConfigBundle.LoadAsset(AssetBundlePath.versionConfigAssetName);
            mWebServerVersionId = ParseXML(mVersionConfig);
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private IEnumerator InitLoadExternalStoreVersionConfig()
        {
            string url = AssetBundlePath.Instance.ExternalStorePathUrl + "/" + AssetBundlePath.versionConfigBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            TextAsset mVersionConfig = mConfigBundle.LoadAsset(AssetBundlePath.versionConfigAssetName);
            mExternalStoreVersionId = ParseXML(mVersionConfig);
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private IEnumerator InitLoadStreamFolderVersionConfig()
        {
            string url = AssetBundlePath.Instance.StreamingAssetPathUrl + "/" + AssetBundlePath.versionConfigBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            TextAsset mVersionConfig = mConfigBundle.LoadAsset(AssetBundlePath.versionConfigAssetName);
            mStreamFolderVersionId = ParseXML(mVersionConfig);
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private IEnumerator InitLoadWebServerABConfig()
        {
            string url = AssetBundlePath.Instance.WebServerPathUrl + "/" + AssetBundlePath.AssetDependentFileBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset("AssetBundleManifest");
            if (mAllBundleMainifest == null)
            {
                DebugSystem.LogError("Mainifest is Null");
                www.Dispose();
                yield break;
            }
            string[] mAssetNames = mAllBundleMainifest.GetAllAssetBundles();
            if (mAssetNames != null)
            {
                foreach (var v in mAssetNames)
                {
                    string bundleName = v;
                    string[] bundleDependentList = mAllBundleMainifest.GetAllDependencies(v);
                    Hash128 mHash = mAllBundleMainifest.GetAssetBundleHash(v);
                    AssetBundleInfo mABInfo = new AssetBundleInfo(bundleName, mHash, bundleDependentList);
                    mWebServerABInfoList.Add(mABInfo);
                }
            }
            else
            {
                DebugSystem.Log("初始化资源依赖文件: Null");
            }
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private IEnumerator InitLoadExternalStoreABConfig()
        {
            string url = AssetBundlePath.Instance.ExternalStorePathUrl + "/" + AssetBundlePath.AssetDependentFileBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset("AssetBundleManifest");
            if (mAllBundleMainifest == null)
            {
                DebugSystem.LogError("Mainifest is Null");
                www.Dispose();
                yield break;
            }
            string[] mAssetNames = mAllBundleMainifest.GetAllAssetBundles();
            if (mAssetNames != null)
            {
                foreach (var v in mAssetNames)
                {
                    string bundleName = v;
                    string[] bundleDependentList = mAllBundleMainifest.GetAllDependencies(v);
                    Hash128 mHash = mAllBundleMainifest.GetAssetBundleHash(v);
                    AssetBundleInfo mABInfo = new AssetBundleInfo(bundleName, mHash, bundleDependentList);
                    mExternalStoreABInfoList.Add(mABInfo);
                }
            }
            else
            {
                DebugSystem.Log("初始化资源依赖文件: Null");
            }
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        private IEnumerator InitLoadStreamFolderABConfig()
        {
            string url = AssetBundlePath.Instance.StreamingAssetPathUrl + "/" + AssetBundlePath.AssetDependentFileBundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset("AssetBundleManifest");
            if (mAllBundleMainifest == null)
            {
                DebugSystem.LogError("Mainifest is Null");
                www.Dispose();
                yield break;
            }
            string[] mAssetNames = mAllBundleMainifest.GetAllAssetBundles();
            if (mAssetNames != null)
            {
                foreach (var v in mAssetNames)
                {
                    string bundleName = v;
                    string[] bundleDependentList = mAllBundleMainifest.GetAllDependencies(v);
                    Hash128 mHash = mAllBundleMainifest.GetAssetBundleHash(v);
                    AssetBundleInfo mABInfo = new AssetBundleInfo(bundleName, mHash, bundleDependentList);
                    mStreamFolderABInfoList.Add(mABInfo);
                }
            }
            else
            {
                DebugSystem.Log("初始化资源依赖文件: Null");
            }
            mConfigBundle.Unload(false);
            www.Dispose();
        }

        /// 
        /// 得到版本号
        /// 
        /// 
        /// 
        public int ParseXML(TextAsset mTextAsset)
        {
            XmlDocument mdoc = new XmlDocument();
            mdoc.LoadXml(mTextAsset.text);
            foreach (XmlNode v in mdoc.ChildNodes)
            {
                if (v.Name == "root")
                {
                    foreach (XmlNode x in v.ChildNodes)
                    {
                        if (x.Name.Contains("versionId"))
                        {
                            return int.Parse(x.InnerText);
                        }
                    }
                }
            }
            return 0;
        }

        private IEnumerator DownLoadAllNeedUpdateBundle()
        {
            List bundleList = mDownLoadAssetInfo.mAssetNameList;
            List.Enumerator mIter = bundleList.GetEnumerator();

            uint addPro = (uint)Mathf.CeilToInt((LoadProgressInfo.MaxProgress - mTask.progress)/(float)bundleList.Count);
            while (mIter.MoveNext())
            {
                DebugSystem.LogError("下载的文件:" + mDownLoadAssetInfo.url + " | " + mIter.Current);
                yield return DownLoadSingleBundle(mDownLoadAssetInfo.url, mIter.Current);
                mTask.progress+=addPro;
            }
        }

        private IEnumerator DownLoadSingleBundle(string path, string bundleName)
        {
            string url = path + "/" + bundleName;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    www.Dispose();
                    yield break;
                }
            }
            string savePath = AssetBundlePath.Instance.ExternalStorePath + "/" + bundleName;
            SaveDownLoadedFile(savePath, www.bytes);
            www.Dispose();
        }

        private void SaveDownLoadedFile(string path, byte[] mdata)
        {
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            FileInfo mFileInfo = new FileInfo(path);
            FileStream mFileStream = mFileInfo.OpenWrite();
            mFileStream.Write(mdata, 0, mdata.Length);
            mFileStream.Flush();
            mFileStream.Close();
        }

        private class DownLoadAssetInfo
        {
            public string url;
            public List mAssetNameList = new List();
        }


    }
}

现在 资源管理架构设计就完了。

二: C#反射热更新(网上热更新的传说,多数资料都是简单一笔带过)

1:如何编译Unity代码,生成程序集:
Unity工程本身编译的程序集会放在Project\Library\ScriptAssemblies文件夹下,所以刚开始我是直接拿这个去加载程序集的,后来发现不行。
因为加载的程序集,与本地程序集重名,Unity会认为加载的程序集,还是本地程序集。(测过结果就是这样)
所以后来,通过VS2015编译Unity工程,但遇到一个问题:build的时候报了一大堆错误,错误的原因在于,Proto 生成的cs文件 所用的.net版本过高导致的。
你可以重新新build Protobuf 源码,然后生成.net低版本的程序集,这样做是可以的。

2:加载程序集,代码如下
using UnityEngine;
using System.Collections;
using System.Reflection;
using xk_System.Debug;
using System;

namespace xk_System.AssetPackage
{
    public class AssemblyManager : Singleton
    {
        private Assembly mHotUpdateAssembly;
        private Assembly mCurrentAssembly;
        /// 
        /// 加载程序集
        /// 
        /// 
        public IEnumerator LoadAssembly()
        {
            AssetInfo mAssetInfo = ResourceABsFolder.Instance.scripts.mtest;
            string path = AssetBundlePath.Instance.ExternalStorePathUrl;

            string bundleName1 = mAssetInfo.bundleName;
            string url = path + "/" + bundleName1;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone)
            {
                if (!string.IsNullOrEmpty(www.error))
                {
                    DebugSystem.LogError("www Load Error:" + www.error);
                    yield break;
                }
            }
            AssetBundle mConfigBundle = www.assetBundle;
            TextAsset mAsset = mConfigBundle.LoadAsset(mAssetInfo.assetName);
            mHotUpdateAssembly = Assembly.Load(mAsset.bytes);
            if (mHotUpdateAssembly != null)
            {
                DebugSystem.Log("加载程序集:" + mHotUpdateAssembly.FullName);
            }
            else
            {
                DebugSystem.Log("加载程序集: null");
            }
            mCurrentAssembly = this.GetType().Assembly;
            DebugSystem.Log("当前程序集:" + mCurrentAssembly.FullName);
            if (mCurrentAssembly.FullName.Equals(mHotUpdateAssembly.FullName))
            {
                DebugSystem.LogError("加载程序集名字有误");
            }
            mConfigBundle.Unload(false);
        }

        public object CreateInstance(string typeFullName)
        {
            if (mHotUpdateAssembly != null)
            {
                return mHotUpdateAssembly.CreateInstance(typeFullName);
            }
            else
            {
               return mCurrentAssembly.CreateInstance(typeFullName);
            }
        }

        /// 
        /// 仅仅写入口时,调用。(否则,会使程序变得混乱,反正我是搞乱了)
        /// 
        /// 
        /// 
        /// 
        public Component AddComponent(GameObject obj, string typeFullName)
        {
            DebugSystem.Log("Type: " + typeFullName);
            if (mHotUpdateAssembly != null)
            {
                Type mType = mHotUpdateAssembly.GetType(typeFullName);
                return obj.AddComponent(mType);
            }
            else
            {
                Type mType = typeFullName.GetType();
                return obj.AddComponent(mType);
            }
        }
    }
}

3:加载完程序集后该如何使用这个程序集就是重点了。
刚开始想这个问题的时候感觉无非反射了这么简单的问题,后来越想感觉越复杂,幸好,崩溃完了之后,发现其实,你只要遵守2点即可实现游戏代码全部更新。(1):我们前面已经做完了,加载资源和加载程序集的工作,那么我们现在要做的工作,就是实现这个新加载的程序集的入口。切记,这个入口要通过动态添加组件的方式出现。如:  
              Type mType = mHotUpdateAssembly.GetType(typeFullName);obj.AddComponent(mType);
(2):要注意所有的预制件上要动态添加脚本,否则,预制件会去寻找【本地程序集】的脚本添加上去,并且还会导致【本地程序集】与【热更新程序集】相互访问的问题。
在这里要注意一点:因为我们已经提供了【热更新程序集】的入口,所以,接下来程序动态添加脚本就会使用【热更新程序集】里脚本。千万不要再去反射程序集里某某个脚本了,加载脚本,还和以前一样写就好了。如:

obj.AddComponent(); 这里与入口动态添加的方式可不一样啊。(没有反射的)。













你可能感兴趣的:(Unity,Game开发)