您的位置:首页 > 移动开发 > Unity3D

Unity5 AssetBundle资源管理架构设计

2017-12-22 09:33 393 查看

http://blog.csdn.net/qq_19399235/article/details/51702964


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


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.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类,这个类的目的专门用来更新完毕后,充当资源加载管理器。代码如下

[csharp] view
plain copy

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服务器资源代码如下:

[csharp] view
plain copy

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低版本的程序集,这样做是可以的。

2:加载程序集,代码如下

[csharp] view
plain copy

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):我们前面已经做完了,加载资源和加载程序集的工作,那么我们现在要做的工作,就是实现这个新加载的程序集的入口。切记,这个入口要通过动态添加组件的方式出现。如:

Type mType = mHotUpdateAssembly.GetType(typeFullName);obj.AddComponent(mType);

(2):要注意所有的预制件上要动态添加脚本,否则,预制件会去寻找【本地程序集】的脚本添加上去,并且还会导致【本地程序集】与【热更新程序集】相互访问的问题。

在这里要注意一点:因为我们已经提供了【热更新程序集】的入口,所以,接下来程序动态添加脚本就会使用【热更新程序集】里脚本。千万不要再去反射程序集里某某个脚本了,加载脚本,还和以前一样写就好了。如:

obj.AddComponent<T>(); 这里与入口动态添加的方式可不一样啊。(没有反射的)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: