Assetbundle的下载这一块,是耗时最久的,也是最复杂的。步骤为对比版本号,不同则对比信息配置文件,然后去差异下载更新。做之前听网友们说已经不用www了,说是besthttp插件,非常好用。然后去试了下,确实好用,但是后面下载写入的时候会偶尔报异常,下载0kb的文件,而且看log根本看不出个所以然,没办法又换回了www。besthttp有个比较好的地方是可以设置下载到一定的量就去触发下载回调,大大的降低了内存,但是游戏没有大资源,也就没什么必要。
首先,对比版本号,即读取本地的版本号文件,与服务端的版本号文件,两个版本号进行对比。
int localVersion = 0;
int webVersion;
public void checkVersion()
{
if (File.Exists(Application.streamingAssetsPath + "/assetVersion.txt"))
{
StreamReader sr = new StreamReader(Application.streamingAssetsPath + "/assetVersion.txt");
string value = sr.ReadToEnd();
Debug.Log(value);
localVersion = int.Parse(value);
}
Debug.Log(localVersion);
StartCoroutine(getWebVersion());
}
IEnumerator getWebVersion()
{
WWW www = new WWW("http://localhost/assetVersion.txt");
yield return www;
if (www.error != null)
{
Debug.Log(www.error);
yield break;
}
if (www.isDone)
{
Debug.Log(www.text);
webVersion = int.Parse(www.text);
}
}
对比结果,如果相等,则直接接入游戏,如果不同,则开始对比创建assetbundle时生成的配置文件,即第一篇那里记录了所有bundle名字、尺寸、md5、后缀的文件。因为是以列表转json的方式写入的,所以这里读取的时候反过来,json转列表即可。注意第一篇的注释,写入文件的时候不要选择utf-8的方式,否则这里转换会有异常(忘记什么异常类型了..)。首先读取本地的配置文件得到一个列表,然后读取服务端的配置文件得到一个列表,对比两个列表,检索出所有md5值发生变化以及新增的bundle。因为列表存的是一个对象,不易对比,所以将本地列表加工成一个字典,key为bundlename。
List<assetBundleMsgInfo> localBundleMsgInfoList;//本地所有bundle信息
Dictionary<String, assetBundleMsgInfo> localBundleMsgDic;//key bundleName
List<assetBundleMsgInfo> webBundleMsgInfoList;
List<assetBundleMsgInfo> needDownLoadBundleList;//对比后需要下载的资源
long needDownLoadSize;//所有资源大小
/// <summary>
/// 解析json文件,对比每个bundle的hash ,本地
/// </summary>
void GetLocalBundlesMsgFile()
{
localBundleMsgInfoList = new List<assetBundleMsgInfo>();
if (!File.Exists(Path.Combine(Application.streamingAssetsPath, "bundleMsg"))) return;
StreamReader sr = new StreamReader(Path.Combine(Application.streamingAssetsPath, "bundleMsg"));
string json = sr.ReadToEnd();
localBundleMsgInfoList = JsonMapper.ToObject<List<assetBundleMsgInfo>>(json);
sr.Close();
}
//服务器
IEnumerator GetWebBundleMsgFile()
{
webBundleMsgInfoList = new List<assetBundleMsgInfo>();
WWW www = new WWW("http://localhost/bundleMsg");
yield return www;
if (www.error != null)
{
Debug.Log(www.error);
yield break;
}
if (www.isDone)
{
Debug.Log(www.text);
webBundleMsgInfoList = JsonMapper.ToObject<List<assetBundleMsgInfo>>(www.text);
}
}
//对比
void CompareFileAndDialogLoad()
{
localBundleMsgDic = new Dictionary<string, assetBundleMsgInfo>();
needDownLoadBundleList = new List<assetBundleMsgInfo>();
needDownLoadSize = 0;
for (int i = 0; i < localBundleMsgInfoList.Count; i++)
{
assetBundleMsgInfo info = localBundleMsgInfoList[i];
localBundleMsgDic.Add(info.bundleName, info);
}
for (int i = 0; i < webBundleMsgInfoList.Count; i++)
{
assetBundleMsgInfo info = webBundleMsgInfoList[i];
if (localBundleMsgDic.ContainsKey(info.bundleName))
{
if (info.hash != localBundleMsgDic[info.bundleName].hash)
{
needDownLoadBundleList.Add(info);
needDownLoadSize += info.size;
}
}
else
{
needDownLoadBundleList.Add(info);
needDownLoadSize += info.size;
}
}
}
经过对比配置之后便可以获取到所有需要下载的bundle,以及资源总大小,然后弹窗提示更新,确认后开始下载,写入本地即可。写入这里用简单来说就几句代码,但是实际上会遇到各种各样的异常,这里就不一一举例了,遇到异常分析不出来问度娘即可,但是插件报的异常就有点令人无可奈何了,插件的好处就在于为你省去了其他繁琐的步骤,但是弊端就是如果有异常发生,查找问题会非常的麻烦,所以慎用。
for (int i = 0; i < needDownLoadBundleList.Count; i++)
{
StartCoroutine(DownLoadFile(needDownLoadBundleList[i].bundleName));
}
StartCoroutine(DownLoadFile("AssetbundleFile"));
StartCoroutine(DownLoadFile("AssetbundleFile.manifest"));
StartCoroutine(DownLoadFile("bundleMsg"));
StartCoroutine(DownLoadFile("assetVersion.txt"));
IEnumerator DownLoadFile(string bundleName)
{
string url = string.Format("http://localhost/{0}", bundleName);
WWW www = new WWW(url);
yield return www;
if (www.error != null)
{
Debug.Log(www.error);
yield break;
}
if (www.isDone)
{
Debug.Log(url + "--- downComplete");
byte[] bytes = www.bytes;
CreateFile(bytes, bundleName);
}
}
void CreateFile(byte[] bytes, string name)
{
Stream stream;
string path = Path.Combine(Application.streamingAssetsPath, name);
if (File.Exists(path)) File.Delete(path);
FileInfo info = new FileInfo(path);
stream = info.Create();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
}
注意这里路径根据自己实际的来传参。文件写入后文件流要及时关闭。
至此下载写入就完成了,但是这里有很大的漏洞,即如果下载过程中,因为某些不可控的原因,导致某个文件下载失败,但是最后的系列配置文件都下载成功,这样会导致本地版本以及配置都是正确的,但是实际文件不同,就没办法检测到差异然后进行更新,所以这里还需要二次校验,我一直在疑问更新之后,本地的bundlemsg配置文件是不是需要在本地自动生成,而不是去下载,还有这个二次校验需要怎么去做。希望有做过或有思路的朋友不吝赐教。