Unity5 AssetBundle资源管理架构设计
http://blog.csdn.net/qq_19399235/article/details/51702964
1:Unity5 资源管理架构设计(2017.4.22版本)
2:Android 热更新(不考虑IOS)根据C#反射实现的代码全更新方案(网上一大坨,我重新整理一下)。
一:Unity资源管理架构设计
- 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<mDir.FullName.Length;j++)
- {
- if (j > index)
- {
- var v = mDir.FullName[j];
- if (v.Equals('\\'))
- {
- coutT++;
- }
- }
- }
- }
- coutT++;
- string tStr = "";
- int i = 0;
- while(i<coutT)
- {
- tStr += "\t";
- i++;
- }
- return tStr;
- }
- public static string GetAssetPath(string filePath)
- {
- string assetPath = "";
- int index = filePath.IndexOf(@"Assets\");
- if (index >= 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
- {
- /// <summary>
- /// 此类的目的就是加载本地的Bundle进行资源读取操作的
- /// </summary>
- public class AssetBundleManager : SingleTonMonoBehaviour<AssetBundleManager>
- {
- 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 List<string> mBundleLockList = new List<string>();
- /// <summary>
- /// 加载Assetbundle方案1:初始化时,全部加载
- /// </summary>
- /// <returns></returns>
- public IEnumerator InitLoadAllBundleFromLocal()
- {
- yield return mResourcesABManager.InitLoadMainifestFile();
- List<AssetBundleInfo> bundleList = mResourcesABManager.mNeedLoadBundleList;
- List<AssetBundleInfo>.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);
- }
- }
- }
- }
- }
- }
- /// <summary>
- /// 从本地外部存储位置加载单个Bundle资源,全部加载
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <returns></returns>
- 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();
- }
- /// <summary>
- /// 从本地外部存储位置加载单个Bundle资源,全部加载
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <returns></returns>
- 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);
- }
- /// <summary>
- /// 异步从本地外部存储加载单个Asset文件,只加载Bundle中的单个资源
- /// </summary>
- /// <param name="bundle"></param>
- /// <returns></returns>
- 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);
- }
- }
- }
- /// <summary>
- /// 同步从本地外部存储加载单个Bundle文件
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- 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);
- }
- }
- /// <summary>
- /// 同步从本地外部存储加载单个资源文件
- /// </summary>
- /// <param name="BaseBundleInfo"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- 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<string, UnityEngine.Object> mDic = new Dictionary<string, UnityEngine.Object>();
- 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<string, UnityEngine.Object> mEditorAssetDic = new Dictionary<string, UnityEngine.Object>();
- 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<UnityEngine.Object>(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;
- }
- }
- /// <summary>
- /// 这个东西用来在顶层使用
- /// </summary>
- /// <param name="type"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- public UnityEngine.Object LoadAsset(AssetInfo mAssetInfo)
- {
- if (GameConfig.Instance.orUseAssetBundle)
- {
- return GetAssetFromDic(mAssetInfo.bundleName, mAssetInfo.assetName);
- }
- else
- {
- return GetAssetFromEditorDic(mAssetInfo.assetPath);
- }
- }
- /// <summary>
- /// 这个东西用来在专门的管理器中使用(底层封装一下),禁止在顶层使用
- /// </summary>
- /// <param name="type"></param>
- /// <param name="assetName"></param>
- /// <returns></returns>
- 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<AssetBundleInfo> mNeedLoadBundleList = new List<AssetBundleInfo>();
- 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<AssetBundleManifest>(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<TextAsset>(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<AssetBundlePath>
- {
- 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<AssetBundleHotUpdateManager>
- {
- private int mStreamFolderVersionId=-1;
- private int mExternalStoreVersionId=-1;
- private int mWebServerVersionId=-1;
- private List<AssetBundleInfo> mExternalStoreABInfoList = new List<AssetBundleInfo>();
- private List<AssetBundleInfo> mWebServerABInfoList = new List<AssetBundleInfo>();
- private List<AssetBundleInfo> mStreamFolderABInfoList = new List<AssetBundleInfo>();
- 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;
- }
- }
- /// <summary>
- /// 检查版本配置文件
- /// </summary>
- /// <returns></returns>
- 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);
- }
- }
- /// <summary>
- /// 检查资源配置文件
- /// </summary>
- /// <returns></returns>
- private void CheckAssetInfo(string url, List<AssetBundleInfo> 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<TextAsset>(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<TextAsset>(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<TextAsset>(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>("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>("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>("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();
- }
- /// <summary>
- /// 得到版本号
- /// </summary>
- /// <param name="mbytes"></param>
- /// <returns></returns>
- 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<string> bundleList = mDownLoadAssetInfo.mAssetNameList;
- List<string>.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<string> mAssetNameList = new List<string>();
- }
- }
- }
现在 资源管理架构设计就完了。
二: C#反射热更新(网上热更新的传说,多数资料都是简单一笔带过)
1:如何编译Unity代码,生成程序集:
Unity工程本身编译的程序集会放在Project\Library\ScriptAssemblies文件夹下,所以刚开始我是直接拿这个去加载程序集的,后来发现不行。
因为加载的程序集,与本地程序集重名,Unity会认为加载的程序集,还是本地程序集。(测过结果就是这样)
所以后来,通过VS2015编译Unity工程,但遇到一个问题:build的时候报了一大堆错误,错误的原因在于,Proto 生成的cs文件 所用的.net版本过高导致的。
你可以重新新build Protobuf 源码,然后生成.net低版本的程序集,这样做是可以的。
- using UnityEngine;
- using System.Collections;
- using System.Reflection;
- using xk_System.Debug;
- using System;
- namespace xk_System.AssetPackage
- {
- public class AssemblyManager : Singleton<AssemblyManager>
- {
- private Assembly mHotUpdateAssembly;
- private Assembly mCurrentAssembly;
- /// <summary>
- /// 加载程序集
- /// </summary>
- /// <returns></returns>
- 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<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 object CreateInstance(string typeFullName)
- {
- if (mHotUpdateAssembly != null)
- {
- return mHotUpdateAssembly.CreateInstance(typeFullName);
- }
- else
- {
- return mCurrentAssembly.CreateInstance(typeFullName);
- }
- }
- /// <summary>
- /// 仅仅写入口时,调用。(否则,会使程序变得混乱,反正我是搞乱了)
- /// </summary>
- /// <param name="obj"></param>
- /// <param name="typeFullName"></param>
- /// <returns></returns>
- 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):我们前面已经做完了,加载资源和加载程序集的工作,那么我们现在要做的工作,就是实现这个新加载的程序集的入口。切记,这个入口要通过动态添加组件的方式出现。如:
(2):要注意所有的预制件上要动态添加脚本,否则,预制件会去寻找【本地程序集】的脚本添加上去,并且还会导致【本地程序集】与【热更新程序集】相互访问的问题。
在这里要注意一点:因为我们已经提供了【热更新程序集】的入口,所以,接下来程序动态添加脚本就会使用【热更新程序集】里脚本。千万不要再去反射程序集里某某个脚本了,加载脚本,还和以前一样写就好了。如:
obj.AddComponent<T>(); 这里与入口动态添加的方式可不一样啊。(没有反射的)。
Unity5 AssetBundle资源管理架构设计的更多相关文章
- 《炉石传说》架构设计赏析(4):Asset管理
欢迎转载,请注明作者[燕良@游戏开发]及原文地址:http://blog.csdn.net/neil3d/article/details/39580197 另外.欢迎大家来我的QQ群交流各种游戏引擎相 ...
- .NET架构设计、框架设计系列文章总结
从事.NET开发到现在已经有七个年头了.慢慢的可能会很少写.NET文章了.不知不觉竟然走了这么多年,热爱.NET热爱c#.突然想对这一路的经历进行一个总结. 是时候开始下一阶段的旅途,希望这些文章可以 ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...
- Hadoop YARN架构设计要点
YARN是开源项目Hadoop的一个资源管理系统,最初设计是为了解决Hadoop中MapReduce计算框架中的资源管理问题,但是现在它已经是一个更加通用的资源管理系统,可以把MapReduce计算框 ...
- HRMS(人力资源管理系统)-SaaS架构设计-概要设计实践
一.开篇 前期我们针对架构准备阶段及需求分析这块我们写了2篇内容<HRMS(人力资源管理系统)-从单机应用到SaaS应用-架构分析(功能性.非功能性.关键约束)-上篇><HRMS(人 ...
- YARN架构设计详解
一.YARN基本服务组件 YARN是Hadoop 2.0中的资源管理系统,它的基本设计思想是将MRv1中的JobTracker拆分成了两个独立的服务:一个全局的资源管理器ResourceManager ...
- 我读《大数据时代的IT架构设计》
架构设计是一门艺术,对架构的掌握要通过多看,多学,多交流,多积累,从实战架构上总能吸收到很好的营养,这边书虽然 (一).hadoop技术处理电信行业的上网日志 根据上网的url或未知url爬取内容,进 ...
- 读书笔记: Microsot .NET企业级应用架构设计
Microsot .NET企业级应用架构设计 Dino Esposito Andrea Saltarello 编 陈黎夫 译 人民邮电出版社 第一部分 设计原则 第1章 当代架构师和架构 ...
- 架构设计 | 基于电商交易流程,图解TCC事务分段提交
本文源码:GitHub·点这里 || GitEE·点这里 一.场景案例简介 1.场景描述 分布式事务在业务系统中是十分常见的,最经典的场景就是电商架构中的交易业务,如图: 客户端通过请求订单服务,执行 ...
随机推荐
- fusioncharts 用法实例
支持xml格式和json格式的数据. 用法很简单. 1.需要引入FusionCharts.js. 2.html中定义个id="chart"的div <div id=" ...
- Machine Learning No.9: Dimensionality reduction
1. Principal component analysis algorithm data preprocessing 2. choosing the number of principal com ...
- 【转载】帧缓冲驱动程序分析及其在BSP上的添加
原文地址:(四)帧缓冲驱动程序分析及其在BSP上的添加 作者:gfvvz 一.BSP修改及其分析 1. BSP中直接配置的四个寄存器 S3C6410数据手册的第14.5部分是显示控制器的编程模型部 ...
- Warning: Cannot send session cookie – headers already sent…
相信大多数人在写PHP代码的时候,都遇到过类似 "Warning: Cannot send session cookie – headers already sent…“或者”Cannot ...
- CentOS 7编译安装Tengine+PHP+MariaDB全程笔记
安装环境:CentOS7 3.10.0-693.5.2.el7.x86_64 准备源码包: pcre-8.41.tar.gz openssl-1.0.1h.tar.gz zlib-1.2.11.tar ...
- IntelliJ IDEA 同时启动多个Tomcat实例端口是会冲突
- L94
Early-morning births are genetically programmed THE notion that nothing good happens after midnight ...
- 华为USG6500系列
华为USG6500: ssh 登录配置 time-range 相关配置:<USG6000V1>system-view Enter system view, return user view ...
- ACM学习历程—HDU2222 Keywords Search(字典树)
Keywords Search Description In the modern time, Search engine came into the life of everybody like G ...
- AtCoder Grand Contest 028 A:Two Abbreviations
题目传送门:https://agc028.contest.atcoder.jp/tasks/agc028_a 题目翻译 给你两个串\(s\)与\(t\),长度分别为\(n,m\).问你存不存在一个串长 ...