http://blog.csdn.net/qq_19399235/article/details/51702964
本文参考的AssetBundle技术文章:http://liweizhaolili.blog.163.com/blog/static/16230744201541410275298/
本文介绍两大内容:
1:Unity5 资源管理架构设计
2:Android 热更新(不考虑IOS)根据C#反射实现的代码全更新方案(网上一大坨,我重新整理一下)。
一:Unity资源管理架构设计
注意:我配置的Bundle资源文件都放在Assets/ResourceABs文件夹下,并且此文件夹下每个文件夹都对应一个Bundle文件,最终这些文件都打包到StreamingAssets流文件夹下。
1:设计一个资源信息管理类,能够反映Assets/ResourceABs文件夹下的全部的资源信息。
生成工具放在Editor文件下, 代码如下:
[csharp] view plain copy
- using UnityEngine;
- using System.Collections;
- using System.IO;
- using UnityEditor;
- using xk_System.AssetPackage;
- public class ExportAssetInfoEditor : MonoBehaviour
- {
- static string extention = AssetBundlePath.getSingle().ABExtention;
- static string BuildAssetPath = "Assets/ResourceABs";
- static string CsOutPath = "Assets/Scripts/auto";
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~创建AB文件所有的信息~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [MenuItem("UnityEditor/Asset/Auto Generic Cs File")]
- public static void GenericABInfo()
- {
- CreateABCSFile();
- Debug.Log("创建资源cs文件完成");
- }
- public static void CreateABCSFile()
- {
- string m = "using UnityEngine;\nusing System.Collections;\nusing xk_System.BaseClass;\n";
- m += "namespace xk_System.AssetPackage\n{\n";
- DirectoryInfo mDir = new DirectoryInfo(BuildAssetPath);
- m += "\tpublic class " + mDir.Name + "Folder : Single<" + 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());
- }
- m += "\t}\n";
- m += s;
- m += "}\n";
- string fileName = CsOutPath + "/" + mDir.Name + ".cs";
- StreamWriter mSw = new StreamWriter(fileName, false);
- mSw.Write(m);
- mSw.Close();
- }
- public static string CreateDirClass(DirectoryInfo mDir, string bundleName)
- {
- 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 = "\tpublic class " + mDir.Name + "Folder\n\t{\n";
- foreach (var v in mFileInfos)
- {
- if (v.Extension != ".meta")
- {
- string fileName = v.Name.Substring(0, v.Name.LastIndexOf(v.Extension));
- m += "\t\t public AssetInfo m" + fileName + "=new AssetInfo(\"" + bundleName1 + "\",\"" + v.Name + "\");\n";
- }
- }
- m += "\t}\n";
- }
- else
- {
- if (mDir.GetDirectories().Length > 0)
- {
- m = "\tpublic class " + mDir.Name + "Folder\n\t{\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 += "\t\t public " + fieldName + " " + v.Name + "=new " + fieldName + "();\n";
- }
- s += CreateDirClass(v, bundleName+"_"+v.Name.ToLower());
- }
- m += "\t}\n";
- m += s;
- }
- }
- return m;
- }
- }
生成的资源信息管理类的代码样式如下:
[csharp] view plain copy
- using UnityEngine;
- using System.Collections;
- using xk_System.BaseClass;
- namespace xk_System.AssetPackage
- {
- public class ResourceABsFolder : Single<ResourceABsFolder>
- {
- public atlasFolder atlas=new atlasFolder();
- public managerFolder manager=new managerFolder();
- public scriptsFolder scripts=new scriptsFolder();
- public sheetFolder sheet=new sheetFolder();
- public viewFolder view=new viewFolder();
- }
- public class atlasFolder
- {
- public loginFolder login=new loginFolder();
- }
- public class loginFolder
- {
- public AssetInfo m111=new AssetInfo("atlas_login.xk_unity3d","111.jpg");
- public AssetInfo m222=new AssetInfo("atlas_login.xk_unity3d","222.jpg");
- public AssetInfo m333=new AssetInfo("atlas_login.xk_unity3d","333.jpg");
- public AssetInfo m444=new AssetInfo("atlas_login.xk_unity3d","444.jpg");
- public AssetInfo mbackground_fwqgx_00=new AssetInfo("atlas_login.xk_unity3d","background_fwqgx_00.png");
- public AssetInfo mbackground_fwqgx_1=new AssetInfo("atlas_login.xk_unity3d","background_fwqgx_1.png");
- public AssetInfo mbackground_gx_00=new AssetInfo("atlas_login.xk_unity3d","background_gx_00.png");
- public AssetInfo mbutton_close_00=new AssetInfo("atlas_login.xk_unity3d","button_close_00.png");
- public AssetInfo mbutton_close_01=new AssetInfo("atlas_login.xk_unity3d","button_close_01.png");
- public AssetInfo mbutton_close_02=new AssetInfo("atlas_login.xk_unity3d","button_close_02.png");
- public AssetInfo mbutton_close_03=new AssetInfo("atlas_login.xk_unity3d","button_close_03.png");
- public AssetInfo mcs_bg_champions=new AssetInfo("atlas_login.xk_unity3d","cs_bg_champions.png");
- public AssetInfo mLOL_qqlogin=new AssetInfo("atlas_login.xk_unity3d","LOL_qqlogin.png");
- public AssetInfo mtips=new AssetInfo("atlas_login.xk_unity3d","tips.png");
- public AssetInfo mUdyr_bear=new AssetInfo("atlas_login.xk_unity3d","Udyr_bear.png");
- public AssetInfo mUdyr_phoenix=new AssetInfo("atlas_login.xk_unity3d","Udyr_phoenix.png");
- public AssetInfo mUdyr_tiger=new AssetInfo("atlas_login.xk_unity3d","Udyr_tiger.png");
- public AssetInfo mUdyr_turtle=new AssetInfo("atlas_login.xk_unity3d","Udyr_turtle.png");
- }
- public class managerFolder
- {
- public AssetInfo mEventSystem=new AssetInfo("manager.xk_unity3d","EventSystem.prefab");
- public AssetInfo mGlobalWindowManager=new AssetInfo("manager.xk_unity3d","GlobalWindowManager.prefab");
- public AssetInfo mModulesWindowManager=new AssetInfo("manager.xk_unity3d","ModulesWindowManager.prefab");
- }
- public class scriptsFolder
- {
- public AssetInfo mtest=new AssetInfo("scripts.xk_unity3d","test.bytes");
- }
- public class sheetFolder
- {
- public AssetInfo mDB=new AssetInfo("sheet.xk_unity3d","DB.xml");
- }
- public class viewFolder
- {
- public AssetInfo mChatView=new AssetInfo("view.xk_unity3d","ChatView.prefab");
- public AssetInfo mHotUpdateView=new AssetInfo("view.xk_unity3d","HotUpdateView.prefab");
- public AssetInfo mLoginView=new AssetInfo("view.xk_unity3d","LoginView.prefab");
- public AssetInfo mMainView=new AssetInfo("view.xk_unity3d","MainView.prefab");
- public AssetInfo mSelectServerView=new AssetInfo("view.xk_unity3d","SelectServerView.prefab");
- public AssetInfo mShareView=new AssetInfo("view.xk_unity3d","ShareView.prefab");
- }
- }
到这里,这个资源基本信息管理类就处理好了。
2:我们就正式开始写资源管理架构类了
我们首先写一个AssetBundleManager类,这个类的目的专门用来更新完毕后,充当资源加载管理器。代码如下
- using UnityEngine;
- using System.Collections;
- using xk_System.Debug;
- using xk_System.BaseClass;
- using System.Collections.Generic;
- using System.IO;
- using System.Xml;
- using System;
- namespace xk_System.AssetPackage
- {
- /// <summary>
- /// 此类的目的就是加载本地的Bundle进行资源读取操作的
- /// </summary>
- public class AssetBundleManager : MonoBehaviour
- {
- private static AssetBundleManager single=null;
- private ResourcesABManager mResourcesABManager = new ResourcesABManager();
- private Dictionary<string, AssetBundle> mBundleDic = new Dictionary<string, AssetBundle>();
- private Dictionary<string, Dictionary<string, UnityEngine.Object>> mAssetDic = new Dictionary<string, Dictionary<string, UnityEngine.Object>>();
- private void Awake()
- {
- single = this;
- }
- public static AssetBundleManager getSingle()
- {
- return single;
- }
- /// <summary>
- /// 加载Assetbundle方案1:初始化时,全部加载
- /// </summary>
- /// <returns></returns>
- public IEnumerator InitLoadAllBundleFromLocal()
- {
- DebugSystem.Log("开始初始化本地资源");
- yield return StartCoroutine(mResourcesABManager.InitLoadMainifestFile());
- List<AssetBundleInfo> bundleList = mResourcesABManager.mNeedLoadBundleList;
- List<AssetBundleInfo>.Enumerator mIter = bundleList.GetEnumerator();
- while (mIter.MoveNext())
- {
- yield return StartCoroutine(AsyncLoadFromLoaclSingleBundle(mIter.Current));
- }
- DebugSystem.Log("初始化本地资源完成");
- }
- private IEnumerator CheckBundleDependentBundle(AssetBundleInfo mBundle)
- {
- 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 StartCoroutine(AsyncLoadFromLoaclSingleBundle(mBundleInfo));
- }else
- {
- if(mAB==null)
- {
- yield return StartCoroutine(AsyncLoadFromLoaclSingleBundle(mBundleInfo));
- }
- }
- }
- }
- }
- /// <summary>
- /// 从本地外部存储位置加载单个Bundle资源,全部加载
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <returns></returns>
- private IEnumerator AsyncLoadFromLoaclSingleBundle(AssetBundleInfo BaseBundleInfo)
- {
- yield return StartCoroutine(CheckBundleDependentBundle(BaseBundleInfo));
- string path = AssetBundlePath.getSingle().ExternalStorePathUrl;
- string url = path + "/" + BaseBundleInfo.bundleName;
- DebugSystem.Log("url path: " + url);
- 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 asset = www.assetBundle;
- www.Dispose();
- SaveBundleToDic(BaseBundleInfo.bundleName, asset);
- }
- /// <summary>
- /// 异步从本地外部存储加载单个Asset文件,只加载Bundle中的单个资源
- /// </summary>
- /// <param name="bundle"></param>
- /// <returns></returns>
- private IEnumerator AsyncLoadFromLocalSingleAsset(AssetBundleInfo bundle,string assetName)
- {
- yield return StartCoroutine(CheckBundleDependentBundle(bundle));
- string path = AssetBundlePath.getSingle().ExternalStorePathUrl;
- string url = path + "/" + bundle.bundleName;
- 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 asset = www.assetBundle;
- www.Dispose();
- UnityEngine.Object Obj = asset.LoadAsset(assetName);
- if (Obj != null)
- {
- DebugSystem.Log("Async Load Asset Success:" + Obj.name);
- SaveAssetToDic(bundle.bundleName, assetName, Obj);
- }
- asset.Unload(false);
- }
- /// <summary>
- /// 同步从本地外部存储加载单个Bundle文件
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- private UnityEngine.Object SyncLoadFromLocalSingleAsset(AssetBundleInfo BaseBundleInfo,string assetName)
- {
- string path = AssetBundlePath.getSingle().ExternalStorePathUrl;
- string url = path + "/" + BaseBundleInfo;
- AssetBundle asset= AssetBundle.CreateFromFile(url);
- return asset.LoadAsset(assetName);
- }
- private void SaveBundleToDic(string bundleName,AssetBundle bundle)
- {
- if(bundle==null)
- {
- DebugSystem.LogError("保存的Bundle为空");
- return;
- }
- mBundleDic[bundleName] = bundle;
- }
- private void SaveAssetToDic(string bundleName, string assetName,UnityEngine.Object asset)
- {
- if(asset==null)
- {
- DebugSystem.LogError("保存的资源为空");
- return;
- }
- if(!mAssetDic.ContainsKey(bundleName))
- {
- Dictionary<string, UnityEngine.Object> mDic = new Dictionary<string, UnityEngine.Object>();
- mAssetDic.Add(bundleName,mDic);
- }
- mAssetDic[bundleName][assetName] = asset;
- }
- private bool JudgeOrExistAsset(string bundleName, string asstName)
- {
- if (!mAssetDic.ContainsKey(bundleName) || mAssetDic[bundleName] == null || !mAssetDic[bundleName].ContainsKey(asstName) || mAssetDic[bundleName][asstName] == null)
- {
- if (mBundleDic.ContainsKey(bundleName) && mBundleDic[bundleName] != null)
- {
- UnityEngine.Object mm = mBundleDic[bundleName].LoadAsset(asstName);
- if (mm != null)
- {
- SaveAssetToDic(bundleName, asstName, mm);
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
- return true;
- }
- private UnityEngine.Object GetAssetFromDic(string bundleName,string asstName)
- {
- if (JudgeOrExistAsset(bundleName, asstName))
- {
- if (mAssetDic.ContainsKey(bundleName) && mAssetDic[bundleName] != null && mAssetDic[bundleName].ContainsKey(asstName) && mAssetDic[bundleName][asstName] != null)
- {
- UnityEngine.Object mAsset1 = mAssetDic[bundleName][asstName];
- mAsset1 = Instantiate(mAsset1);
- return mAsset1;
- }
- }else
- {
- DebugSystem.LogError("Asset is NUll");
- }
- return null;
- }
- /// <summary>
- /// 这个东西用来在顶层使用
- /// </summary>
- /// <param name="type"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- public UnityEngine.Object LoadAsset(AssetInfo mAssetInfo)
- {
- return GetAssetFromDic(mAssetInfo.bundleName, mAssetInfo.assetName);
- }
- /// <summary>
- /// 这个东西用来在专门的管理器中使用(底层封装一下),禁止在顶层使用
- /// </summary>
- /// <param name="type"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- public IEnumerator AsyncLoadAsset(AssetInfo mAssetInfo)
- {
- string bundleName = mAssetInfo.bundleName;
- string asstName = mAssetInfo.assetName;
- if(!JudgeOrExistAsset(bundleName,asstName))
- {
- yield return StartCoroutine(AsyncLoadFromLocalSingleAsset(mResourcesABManager.GetBundleInfo(bundleName), asstName));
- }
- }
- }
- public class ResourcesABManager
- {
- public List<AssetBundleInfo> mNeedLoadBundleList = new List<AssetBundleInfo>();
- public AssetBundleInfo GetBundleInfo(string bundleName)
- {
- AssetBundleInfo mBundleInfo = mNeedLoadBundleList.Find((x) =>
- {
- return x.bundleName == bundleName;
- });
- return mBundleInfo;
- }
- private void ParseAssetBundleManifest(AssetBundle mConfigBundle)
- {
- AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
- if (mAllBundleMainifest == null)
- {
- DebugSystem.LogError("Mainifest is Null");
- return;
- }
- 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");
- }
- }
- public IEnumerator InitLoadMainifestFile()
- {
- if(mNeedLoadBundleList.Count>0)
- {
- yield break;
- }
- string path = AssetBundlePath.getSingle().ExternalStorePathUrl;
- string url = path + "/" + AssetBundlePath.getSingle().AssetDependentFile;
- DebugSystem.Log("url path: " + url);
- 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 asset = www.assetBundle;
- www.Dispose();
- if (asset == null)
- {
- DebugSystem.LogError("MainifestFile Bundle is Null");
- yield break;
- }
- ParseAssetBundleManifest(asset);
- asset.Unload(false);
- }
- }
- 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 AssetInfo(string bundleName, string assetName)
- {
- this.bundleName = bundleName;
- this.assetName = assetName;
- }
- }
- public class AssetBundlePath : Single<AssetBundlePath>
- {
- public readonly string WebServerPathUrl;
- public readonly string versionConfig = "version.xml";
- //public readonly string ABConfig = "abconfig.xml";
- public readonly string AssetDependentFile = "StreamingAssets";
- public readonly string ABExtention = ".xk_unity3d";
- public readonly string StreamingAssetPathUrl;
- public readonly string ExternalStorePathUrl;
- public readonly string StreamingAssetPath;
- public readonly string ExternalStorePath;
- public AssetBundlePath()
- {
- #if UNITY_EDITOR || UNITY_STANDALONE_WIN
- WebServerPathUrl = "file:///F:/WebServer";
- StreamingAssetPathUrl = "file:///" + Application.streamingAssetsPath;
- ExternalStorePathUrl = "file:///" + Application.persistentDataPath;
- ExternalStorePath = Application.persistentDataPath;
- StreamingAssetPath = Application.streamingAssetsPath;
- #elif UNITY_ANDROID
- WebServerPathUrl = "file:///F:/WebServer";
- StreamingAssetsFolder ="jar:file://"+Application.streamingAssetsPath;
- ExternalStoreFolder ="file://"+Application.persistentDataPath;
- ExternalStorePath = Application.persistentDataPath;
- StreamingAssetPath = Application.streamingAssetsPath;
- #elif UNITY_IPHONE
- WebServerPathUrl = "file:///F:/WebServer";
- StreamingAssetsFolder =Application.streamingAssetsPath;
- ExternalStoreFolder =Application.persistentDataPath;
- ExternalStorePath = Application.persistentDataPath;
- StreamingAssetPath = Application.streamingAssetsPath;
- #endif
- //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 System.Collections.Generic;
- using System.IO;
- using xk_System.Debug;
- using System.Xml;
- using xk_System.AssetPackage;
- namespace xk_System.HotUpdate
- {
- public class AssetBundleHotUpdateManager : MonoBehaviour
- {
- private List<AssetBundleInfo> mNowABInfoList = new List<AssetBundleInfo>();
- private List<AssetBundleInfo> mUpdateABInfoList = new List<AssetBundleInfo>();
- private List<AssetBundleInfo> mNeedUpdateList = new List<AssetBundleInfo>();
- private static AssetBundleHotUpdateManager single=null;
- private void Awake()
- {
- single = this;
- }
- public static AssetBundleHotUpdateManager getSingle()
- {
- return single;
- }
- public IEnumerator CheckUpdate()
- {
- DebugSystem.Log("开始校对资源");
- yield return StartCoroutine(CheckOrCopyFileToExternalStore());
- yield return StartCoroutine(CheckVersionConfig());
- yield return StartCoroutine(CheckABConfig());
- yield return StartCoroutine(DownLoadAllBundleFromWebServer());
- DebugSystem.Log("热更新资源下载完毕");
- }
- /// <summary>
- /// 检查版本配置文件
- /// </summary>
- /// <returns></returns>
- private IEnumerator CheckVersionConfig()
- {
- yield return 0;
- }
- /// <summary>
- /// 检查资源配置文件
- /// </summary>
- /// <returns></returns>
- private IEnumerator CheckABConfig()
- {
- yield return StartCoroutine(InitLoadLocalABConfig());
- yield return StartCoroutine(InitLoadWebServerABConfig());
- foreach (AssetBundleInfo k in mUpdateABInfoList)
- {
- AssetBundleInfo mBundleInfo = mNowABInfoList.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)
- {
- mNeedUpdateList.Add(k);
- }
- }
- DebugSystem.Log("需要下载更新的个数:" + mNeedUpdateList.Count);
- }
- private IEnumerator CheckOrCopyFileToExternalStore()
- {
- /* string path = AssetBundlePath.getSingle().ExternalStorePath;
- string StreamPath = AssetBundlePath.getSingle().StreamingAssetPath;
- List<AssetBundleInfo> bundleList = ResourcesABManager.getSingle().mNeedLoadBundleList;
- List<AssetBundleInfo>.Enumerator mIter = bundleList.GetEnumerator();
- while (mIter.MoveNext())
- {
- string bundleExternalPath = path + "/" + mIter.Current.bundleName;
- if (!File.Exists(bundleExternalPath))
- {
- string bundleStreamPath = StreamPath + "/" + mIter.Current.bundleName;
- if (File.Exists(bundleStreamPath))
- {
- File.Copy(bundleStreamPath, bundleExternalPath);
- }
- }
- else
- {
- // yield return StartCoroutine(DownLoadAllBundleFromWebServer());
- }
- }*/
- yield return 0;
- }
- private IEnumerator InitLoadWebServerABConfig()
- {
- string path = AssetBundlePath.getSingle().WebServerPathUrl;
- string bundleName1 = AssetBundlePath.getSingle().AssetDependentFile;
- 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;
- AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
- if (mAllBundleMainifest == null)
- {
- DebugSystem.LogError("Mainifest is Null");
- 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);
- mUpdateABInfoList.Add(mABInfo);
- }
- }
- else
- {
- DebugSystem.Log("初始化资源依赖文件: Null");
- }
- mConfigBundle.Unload(false);
- www.Dispose();
- yield return 0;
- }
- private IEnumerator InitLoadLocalABConfig()
- {
- string path = AssetBundlePath.getSingle().ExternalStorePathUrl;
- string bundleName1 = AssetBundlePath.getSingle().AssetDependentFile;
- 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;
- AssetBundleManifest mAllBundleMainifest = mConfigBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
- if (mAllBundleMainifest == null)
- {
- DebugSystem.LogError("Mainifest is Null");
- 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);
- mNowABInfoList.Add(mABInfo);
- }
- }
- else
- {
- DebugSystem.Log("初始化资源依赖文件: Null");
- }
- mConfigBundle.Unload(false);
- www.Dispose();
- yield return 0;
- }
- /// <summary>
- /// 用于自己定义的配置文件解析,不要删除
- /// </summary>
- /// <param name="mbytes"></param>
- /// <returns></returns>
- public Dictionary<AssetBundleInfo, string> ParseXML(byte[] mbytes)
- {
- Dictionary<AssetBundleInfo, string> mDic = new Dictionary<AssetBundleInfo, string>();
- /* XmlDocument mdoc = new XmlDocument();
- MemoryStream mStream = new MemoryStream(mbytes);
- mdoc.Load(mStream);
- foreach (XmlNode v in mdoc.ChildNodes)
- {
- if (v.Name == "root")
- {
- foreach (XmlNode x in v.ChildNodes)
- {
- AssetBundleInfo info = ResourcesABManager.GetBundleInfo(x.Name);
- if (info != null)
- {
- mDic.Add(info, x.InnerText);
- }
- }
- }
- } */
- return mDic;
- }
- private IEnumerator DownLoadAllBundleFromWebServer()
- {
- yield return StartCoroutine(DownLoadMainifestFromWebServer());
- List<AssetBundleInfo> bundleList = mNeedUpdateList;
- List<AssetBundleInfo>.Enumerator mIter = bundleList.GetEnumerator();
- while (mIter.MoveNext())
- {
- DebugSystem.LogError("下载的文件:" + mIter.Current.bundleName);
- yield return StartCoroutine(DownLoadSingleBundleFromWebServer(mIter.Current));
- }
- }
- private IEnumerator DownLoadMainifestFromWebServer()
- {
- string path = AssetBundlePath.getSingle().WebServerPathUrl;
- string url = path + "/" + AssetBundlePath.getSingle().AssetDependentFile;
- DebugSystem.Log("url path: " + url);
- 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;
- }
- }
- yield return 0;
- string savePath = AssetBundlePath.getSingle().ExternalStorePath + "/" + AssetBundlePath.getSingle().AssetDependentFile;
- SaveDownLoadedFile(savePath, www.bytes);
- www.Dispose();
- yield return 0;
- }
- //下载单个Bundle文件
- private IEnumerator DownLoadSingleBundleFromWebServer(AssetBundleInfo bundleBaseInfo)
- {
- string path = AssetBundlePath.getSingle().WebServerPathUrl;
- string url = path + "/" + bundleBaseInfo.bundleName;
- 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;
- }
- }
- yield return 0;
- string savePath = AssetBundlePath.getSingle().ExternalStorePath + "/" + bundleBaseInfo.bundleName;
- SaveDownLoadedFile(savePath, www.bytes);
- www.Dispose();
- yield return 0;
- }
- 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();
- }
- }
- }
现在 资源管理架构设计就完了。
二: 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;
- using xk_System.BaseClass;
- using xk_System.View;
- namespace xk_System.AssetPackage
- {
- public class AssemblyManager : Single<AssemblyManager>
- {
- private Assembly mHotUpdateAssembly;
- private Assembly mCurrentAssembly;
- public IEnumerator LoadAssembly()
- {
- AssetInfo mAssetInfo = ResourceABsFolder.getSingle().scripts.mtest;
- string path = AssetBundlePath.getSingle().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<TextAsset>(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 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<T>(); 这里与入口动态添加的方式可不一样啊。(没有反射的)。