仿LOL项目开发第二天

                                      by草帽

接着上节来讲,上节更新还没开始写代码逻辑,今天我们补充完整。

我们找到VersionManager脚本里面的CheckVersion方法:

首先我们想到检测版本,需要从服务器下载信息,那么肯定要提前检测下网络是否良好,并比较版本信息。

所以,我们写个BeforeCheck方法:

    /// <summary>/// 检测网络状况并对照版本信息是否一致/// </summary>/// <param name="AsynResult">版本信息是否一致的处理委托</param>/// <param name="OnError">错误处理委托</param>public void BeforeCheck(Action<bool> AsynResult, Action OnError){CheckTimeout checkTimeout = new CheckTimeout();checkTimeout.AsynIsNetworkTimeout((success) => {//如果网络良好,开始下载服务器版本xmlif (success){}else {if (OnError != null){OnError();}}});}

在写网络良好判断的带参匿名委托之前,我们先来思考一个问题,网络良好接下来需要做什么?

就是下载服务端的版本信息,但是得需要一个url地址来访问?

那么这个服务端版本信息的URL在哪里?难道要把绝对的URl定死的写入到代码中。

假如我以后服务器的URL换了呢?那么就要重新改写代码。这种方案否决,不可取。

所以呢,这个服务器的URL应该得写入到配置文件中,从服务器那边下载。

非常nice,之前我们不是写个一个SystemConfig,我们就在里面初始化服务器的URL。

所以SystemConfig需要一个Init()的初始方法。

初始配置文件分为两类:

1.从服务器那边获取的配置信息,比如版本信息,游戏服务器各个大区的信息

2.本地配置信息,比如声音大小,屏幕分辨率等等

首先,先来加载服务器相关的配置信息,与服务器相关的,我们需要分类:所以写个CfgInfo类:

    public class CfgInfo{public int id { get; set; }public string name { get; set; }public string url { get; set; }}

比如版本信息类id=0,游戏服务器大区类id=1,然后分别对应着不同的url,这样我们管理起来就清晰多了。

那么这个CfgInfo信息是哪里来的,肯定也需要访问服务器那边的url获取,所以呢,我们在Resources文件夹下面创建一个txt,里面写着这个Url,然后访问URl,读取里面的内容初始化所有的CfgInfo类存到列表中List<CfgInfo>,最后还要把Url文本保存在持久文件夹下,以便以后使用,OK分析完之后。

在SystemConfig类下创建常量:CfgPath持久路径

public readonly static string CfgPath = Application.persistentDataPath + "/cfg.xml";

然后我们在SystemConfig里面创建LoadCfgInfo()方法,在Init()里面调用:

private static bool LoadCfgInfo(){string cfgStr = null;//如果存在持久路径,就直接加载文本if (File.Exists(CfgPath)){cfgStr = UnityTools.LoadFileText(CfgPath);}else {//从Resources从加载配置文本TextAsset cfgUrl = Resources.Load("cfg") as TextAsset;if (cfgUrl){//从网页上下载与服务端有关的所有配置xml字符串cfgStr = DownloadMgr.Instance.DownLoadHtml(cfgUrl.text);}else{cfgStr = null;}}//加载xml内容为列表类CfgInfoList = LoadXMLText<CfgInfo>(cfgStr);return true;}

 

LoadXMLText<T>(string xmlText):

    /// <summary>/// 将xml转换成list<T>列表类/// </summary>/// <typeparam name="T"></typeparam>/// <param name="xmlText"></param>/// <returns></returns>private static List<T> LoadXMLText<T>(string xmlText){List<T> list = new List<T>();try{if (string.IsNullOrEmpty(xmlText)){return list;}Type type = typeof(T);XmlDocument doc = XmlResAdapter.GetXmlDocument(xmlText);Dictionary<int,Dictionary<string,string>> map = XmlResAdapter.LoadXMLToMap(doc,xmlText);var props = type.GetProperties(~System.Reflection.BindingFlags.Static);foreach (var item in map){var obj = type.GetConstructor(Type.EmptyTypes).Invoke(null);foreach (var prop in props){if (prop.Name == "id"){prop.SetValue(obj,item.Key,null);}else{try{if (item.Value.ContainsKey(prop.Name)){var value = UnityTools.GetValue(item.Value[prop.Name],prop.PropertyType);prop.SetValue(obj,value,null);}}catch(Exception e){Debug.LogException(e);}}}list.Add((T)obj);}}catch(Exception e){Debug.LogException(e);}return list;}

  

XmlResAdapter.LoadXMLToMap():

        /// <summary>/// 将xml内容转换成map/// </summary>/// <param name="doc"></param>/// <param name="content"></param>/// <returns></returns>public static Dictionary<int, Dictionary<string, string>> LoadXMLToMap(XmlDocument doc, string content){var result = new Dictionary<int, Dictionary<string, string>>();int index = 0;foreach (XmlNode item in doc.SelectSingleNode("root").ChildNodes){index++;if (item.ChildNodes == null || item.ChildNodes.Count == 0){continue;}int key = int.Parse(item.ChildNodes[0].InnerText);if (result.ContainsKey(key)){continue;}var children = new Dictionary<string, string>();result.Add(key, children);for (int i = 1; i < item.ChildNodes.Count; i++){XmlNode node = item.ChildNodes[i];string tag = null;if (node.Name.Length < 3){tag = node.Name;}else {string tagTial = node.Name.Substring(node.Name.Length - 2, 2);if (tagTial == "_i" || tagTial == "_s" || tagTial == "_f" || tagTial == "_l" || tagTial == "_k" || tagTial == "_m"){tag = node.Name.Substring(0, node.Name.Length - 2);}else {tag = node.Name;}}if (node != null && !children.ContainsKey(tag)){if (string.IsNullOrEmpty(node.InnerText)){children.Add(tag, "");}else{children.Add(tag, node.InnerText.Trim());}}}}return result;}

  

所以这里我们就需要用到第一节提到的准备工具:WampServer集成的网页开发工具:

因为这里我们需要涉及到访问服务器的url,所以我们自己架设一个http服务器站点。

打开WampServer,找到www目录

然后新建一个文件夹,命名为LOLGameDemo

在这个文件夹下面新建一个xml,命名为Cfg.xml

然后回到Unity的Resources文件下面新建一个txt,也命名为Cfg.txt。

编辑txt里面的内容:


然后编辑Cfg.xml的内容:

然后在LOLGameDemo文件夹下,新创建一个ServerVersion.xml文本,为服务器的版本信息:

OK,大功告成。接着我们回到VersionManager里面的BeforeCheck方法里面:

我们要获取到服务器版本的url,所以得回到SystemConfig编写一个GetCfgInfoByName()方法或者GetCfgInfoById()的接口。

GetCfgInfoByName():

    /// <summary>/// 根据名字取得服务端配置信息Url/// </summary>/// <param name="name"></param>/// <returns></returns>public static string GetCfgInfoUrlByName(string name){string result = "";foreach (var item in CfgInfoList){if (item.name == name){result = item.url;break;}}return result;}

GetCfgInfoById():

    /// <summary>/// 根据id取得服务端配置信息Url/// </summary>/// <param name="id"></param>/// <returns></returns>public static string GetCfgInfoUrlById(int id){string result = "";foreach (var item in CfgInfoList){if (item.id == id){result = item.url;break;}}return result;}

因为我们下载的服务器的版本文本也要保存在持久文件目录,所以:

public readonly static string ServerVersionPath = Application.persistentDataPath + "/serverVersion.xml";

还有之前我们只是初始化本地版本信息类的实例,所以现在我们在VersionManager创建一个服务端的版本信息类:

    /// <summary>/// 本地版本信息属性/// </summary>public VersionManagerInfo LocalVersion { get; private set; }/// <summary>/// 服务版本信息属性/// </summary>public VersionManagerInfo ServerVersion { get; private set; }

然后再次回到BeforeCheck方法里面,终于回来了!0.0!

public void BeforeCheck(Action<bool> AsynResult, Action OnError){CheckTimeout checkTimeout = new CheckTimeout();checkTimeout.AsynIsNetworkTimeout((success) => {//如果网络良好,开始下载服务器版本xmlif (success){DownloadMgr.Instance.AsynDownLoadHtml(SystemConfig.GetCfgInfoUrlByName("version"), (serverVersion) => {//如果本地存在服务端的版本信息文本,覆盖下载的服务器文本if (File.Exists(SystemConfig.ServerVersionPath)){serverVersion = UnityTools.LoadFileText(SystemConfig.ServerVersionPath);}//将文本转换成版本信息类ServerVersion = GetVersionInXml(serverVersion);            //开始进行比较版本号bool programVersion = ServerVersion.ProgramVersionCodeInfo.Compare(LocalVersion.ProgramVersionCodeInfo) > 0;bool resourceVersion = ServerVersion.ResourceVersionCodeInfo.Compare(LocalVersion.ResourceVersionCodeInfo) > 0;//执行是否更新的委托AsynResult(programVersion || resourceVersion);},OnError);}else {if (OnError != null){OnError();}}});}

然后回到VersionManager的CheckVersion方法中,将BeforeCheck方法写入到里面。

    public void CheckVersion(Action<bool> fileDecompress,Action<int,int,string> taskProgress,Action<int,long,long> progress,Action finished,Action<Exception> error){BeforeCheck((result) =>{if (result){//需要更新Debug.Log("需要更新");}else{//不需要更新Debug.Log("不需要更新");}}, () => { error(new Exception("下载版本文件超时!")); });}

  

运行程序,发现在打印窗口输出:

不需要更新的字样,如果我们想要需要更新,就需要修改ServerVersion.xml文件:

将Resource资源版本提高一个版本号:这样就比本地LocalVersion高一点。

运行程序,再次看打印窗口:

这次就出现了更新字样。这里我只是打印消息,接下来,我们正式进入开始下载资源包代码编写:

创建一个方法CheckAndDownload():

在写这个方法之前,先分析一下,下载一个需要更新的资源包,我们怎么知道这个资源包得需要更新,还有我们得验证这个资源包的md5码,所以综上,我们得封装一个资源包类:PackageInfo

/// <summary>
/// 资源包
/// </summary>
public class PackageInfo
{/// <summary>/// 包名/// </summary>public string Name;/// <summary>/// 资源适用最低版本号/// </summary>public VersionCodeInfo LowVersion;/// <summary>/// 资源适用最高版本号/// </summary>public VersionCodeInfo HighVersion;/// <summary>/// 资源md5码/// </summary>public string MD5;
}

然后从之前我们下载的服务器版本信息取得PackageMd5List的url,然后访问这个url,根据内容初始化需要下载Pakcage的信息。

DownloadPackageInfoList:

    /// <summary>/// 取得下载包的信息/// </summary>/// <param name="AsynResult"></param>/// <param name="OnError"></param>private void DownloadPackageInfoList(Action<List<PackageInfo>> AsynResult, Action OnError){//从服务器版本信息中取得下载Url,然后初始化下载包的信息DownloadMgr.Instance.AsynDownLoadHtml(ServerVersion.PackageMd5List, (content) => {XmlDocument doc = XmlResAdapter.GetXmlDocument(content);if (null == doc){if (OnError != null){OnError();}}else {List<PackageInfo> packagesList = new List<PackageInfo>();foreach (XmlNode node in doc.SelectSingleNode("root").ChildNodes){PackageInfo package = new PackageInfo();string packagetName = node.Attributes["name"].Value;package.Name = packagetName;//从第7个开始,也就是说前7个是资源包文件名,后4位是包后缀名.zipstring version = packagetName.Substring(7, packagetName.Length - 11);//中间是低版本和高版本===>比如0.0.0.0-0.0.0.3string firstVersion = version.Substring(0,version.IndexOf("-"));package.LowVersion = new VersionCodeInfo(firstVersion);string endVersion = version.Substring(version.Length + 1);package.HighVersion = new VersionCodeInfo(endVersion);//然后内容是md5码package.MD5 = node.InnerText;packagesList.Add(package);ServerVersion.PackageMd5Dic.Add(package.Name, package.MD5);}AsynResult(packagesList);}}, OnError);}

  

然后提供给上层的委托的参数是List<PackageInfo>,上层在取得所有的包,然后判断哪些包是需要下载的,然后进行下载。

AsynDownloadUpdatePackage:

private void AsynDownloadUpdatePackage(Action<bool> fileDecompress, Action<int, int, string> taskProgress, Action<int, long, long> progress, Action finished, Action<Exception> error){DownloadPackageInfoList((packageList) =>{//如果资源包最低版本比游戏的版本高,资源包的最高要求版本比游戏低则需要更新var downloadList = (from packageInfo in packageListwhere packageInfo.LowVersion.Compare(LocalVersion.ResourceVersionCodeInfo) >= 0 && packageInfo.HighVersion.Compare(ServerVersion.ResourceVersionCodeInfo) <= 0select new KeyValuePair<string, string>(packageInfo.HighVersion.ToString(), packageInfo.Name)).ToList();string packageUrl = ServerVersion.PackageUrl;if (downloadList.Count != 0){Debug.Log("开始下载资源包列表");}else {Debug.Log("更新资源包数目为0");if (finished != null){finished();}}}, () => { error(new Exception("下载资源包信息出错")); });}

CheckAndDownload:

public bool CheckAndDownload(Action<bool> fileDecompress, Action<int, int,string> taskProgress, Action<int, long, long> progress, Action finished, Action<Exception> error){//资源需要更新,还有就是程序需要更新我不写了,基本上用不到if (ServerVersion.ResourceVersionCodeInfo.Compare(LocalVersion.ResourceVersionCodeInfo) > 0){//开始下载资源包AsynDownloadUpdatePackage(fileDecompress, taskProgress, progress, finished, error);return true;}if (finished != null){finished();}return false;} 

回到CheckVersion方法:

在需要更新的地方调用:CheckDownload()

public void CheckVersion(Action<bool> fileDecompress,Action<int,int,string> taskProgress,Action<int,long,long> progress,Action finished,Action<Exception> error){BeforeCheck((result) =>{if (result){//需要更新Debug.Log("需要更新");CheckAndDownload(fileDecompress, taskProgress, progress, finished, error);}else{//不需要更新Debug.Log("不需要更新");}}, () => { error(new Exception("下载版本文件超时!")); });}

然后回到ServerVersion.xml修改PackageMd5List标签:

然后去www目录下的LOLGameDemo文件夹下创建PackageMd5List.xml文件:

OK,点击运行程序,观察打印窗口:

程序运行是我们所期望的那样。OK,接着我们就开始写如何下载资源包列表。

首先呢下载一个资源包就相当于一个下载任务,你看迅雷的下载,你每下载一个资源的时候,他都是封装成一个任务来管理,所以呢,这里我们也新建一个任务类来管理:DownloadTask.cs:

using UnityEngine;
using System.Collections;
using System;
/// <summary>
/// 下载任务类
/// </summary>
public class DownloadTask
{public string Url { get; set; }public string FileName { get; set; }public Action<int, long, long> TotalProgress { get; set; }public Action<int> Progress { get; set; }public Action<long> TotalBytesToReceive { get; set; }public Action<long> BytesReceived { get; set; }public String MD5 { get; set; }public Action Finished { get; set; }public Action<Exception> Error { get; set; }public bool bFineshed = false;//文件是否下载完成public bool bDownloadAgain = false;//是否需要从新下载,如果下载出错的时候会从新下public void OnTotalBytesToReceive(long size){if (TotalBytesToReceive != null)TotalBytesToReceive(size);}public void OnBytesReceived(long size){if (BytesReceived != null)BytesReceived(size);}public void OnTotalProgress(int p, long totalSize, long receivedSize){if (TotalProgress != null)TotalProgress(p, totalSize, receivedSize);}public void OnProgress(int p){if (Progress != null)Progress(p);}public void OnFinished(){if (Finished != null)Finished();}public void OnError(Exception ex){if (Error != null)Error(ex);}
}

OK,我们在VersionManager新建一个方法:DownloadPackageList()下载资源包列表的方法,然后在AsynDownloadUpdatePackage方法的开始下载资源包委托里面调用。

private void DownloadPackageList(Action<bool> fileDecompress, string packageUrl, List<KeyValuePair<String, String>> downloadList, Action<int, int, string> taskProgress, Action<int, long, long> progress, Action finished, Action<Exception> error){//下载列表List<DownloadTask> allTasks = new List<DownloadTask>();for (int i = 0; i < downloadList.Count; i++){KeyValuePair<string, string> kvp = downloadList[i];string localFile = string.Concat(SystemConfig.ResourceFolder, kvp.Value);//dataPath+"/Resources/"+文件名//下载完成之后回调委托Action OnDownloadFinished =()=>{//进行解压,以后再来};string fileUrl = string.Concat(packageUrl, kvp.Value);//"http://127.0.0.1/LOLGameDemo/LOLPackage/"+文件名//初始化任务var task = new DownloadTask{FileName = localFile,//dataPath+"/Resources/"+文件名Url = fileUrl,//下载urlFinished = OnDownloadFinished,//解压更新版本信息Error = error,TotalProgress = progress};string fileNameNoExtension = kvp.Value;if (ServerVersion.PackageMd5Dic.ContainsKey(fileNameNoExtension)){task.MD5 = ServerVersion.PackageMd5Dic[fileNameNoExtension];allTasks.Add(task);}else {error(new Exception("下载包不存在:" + fileNameNoExtension));return;}}//全部任务下载完成回调Action AllFinished = () => {Debug.Log("全部下载完成");finished();};//添加taskProgress的回调Action<int, int, string> TaskProgress = (total, current, filename) =>{if (taskProgress != null)taskProgress(total, current, filename);};//添加文件解压的回调函数Action<bool> filedecompress = (decompress) =>{if (fileDecompress != null)fileDecompress(decompress);};DownloadMgr.Instance.Tasks = allTasks;DownloadMgr.Instance.AllDownloadFinished = AllFinished;DownloadMgr.Instance.TaskProgress = TaskProgress;DownloadMgr.Instance.FileDecompress = filedecompress;DownloadMgr.Instance.CheckDownloadList();}

看到了委托的好处了吧,LOLGameDriver里面的委托直接传递到Download里面去执行,真正做到解耦和。符合迪米特法则,不是朋友的类,坚决不要持有他的依赖。

Ok,我们在下载之前先要检测下任务列表,因为可能我们是断点下载,比如说有些文件我们已经下载完成了,放在磁盘上,但是第二天起来又继续更新下载,那么已经下载的这些任务我们得检测下,看是否已经下载过了。

所以在DownloadMgr创建一个CheckDownloadList()方法:

    /// <summary>/// 检测下载列表/// </summary>public void CheckDownloadList(){if (Tasks.Count == 0){//下载列表为空return;}//已经下载完成的数目int finishedCount = 0;foreach (var task in Tasks){if (task.bFineshed && !task.bDownloadAgain){finishedCount++;}else {//是否文件存放的目录已经存在,如果不存在就创建string dir = Path.GetDirectoryName(task.FileName);if (!string.IsNullOrEmpty(dir) && !Directory.Exists(task.FileName)){Directory.CreateDirectory(dir);}//开始断点下载}break;}//说明全部任务下载完成if (finishedCount > m_listTasks.Count - 1){m_listTasks.Clear();m_listTasks = null;if (AllDownloadFinished != null){AllDownloadFinished();AllDownloadFinished = null;}}}

OK,我们开始写断点下载的代码:

之前我们讲过一个类要具有单一职责,但是发现单例模式好像违背了单一职责,单例的好像什么事情都干,就拿这个DownloadMgr来说,我们知道,下载分为两种:

1.断点下载(可继续)

2.非断点下载(重新下载)

那么,因为都是下载,所以我们得吧代码全部写到DownloadMrg里面去,那么这样的职责就分的不清不楚。那么怎么避免呢?

所以我们可以重新封装一个职责类,比如断点下载:ThreadDownloadBreakPoint.cs

using UnityEngine;
using System.Collections;
/// <summary>
/// 断点下载类
/// </summary>
public class ThreadDownloadBreakPoint
{public DownloadMgr Mgr { get; set; }public DownloadTask Task { get; set; }public ThreadDownloadBreakPoint(){}public ThreadDownloadBreakPoint(DownloadMgr mgr, DownloadTask task){Mgr = mgr;Task = task;}public void Download(){Mgr.DownloadFileBreakPoint(Task.Url, Task.FileName);}
}

实则就是将DownloadMgr重新封装一层,但这个非常有用,因为职责变的清晰,我们完全可以不理会DownloadMrg里面是怎么实现断点下载的,我们只知道ThreadDownloadBreakPoint有提供断点下载的方法接口。

因为这个项目是我一个人开发的,所以我们必须得知道底层的实现。

回到DownloadMrg新建一个方法:DownloadFileBreakPoint

    /// <summary>/// 断点下载文件/// </summary>/// <param name="url">网站资源url</param>/// <param name="filePath">保存文件路径</param>public void DownloadFileBreakPoint(string url, string filePath){try{var requestUrl = new Uri(url);var request = (HttpWebRequest)WebRequest.Create(requestUrl);var response = (HttpWebResponse)request.GetResponse();long contentLength = response.ContentLength;response.Close();request.Abort();long leftSize = contentLength;long position = 0;if (File.Exists(filePath)){Debug.Log("需要下载的文件已经存在");using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate,FileAccess.ReadWrite, FileShare.ReadWrite)){leftSize = contentLength - fs.Length;position = fs.Length;}}var partRequest = (HttpWebRequest)WebRequest.Create(requestUrl);if (leftSize > 0){partRequest.AddRange((int)position, (int)(position + leftSize));var partResponse = (HttpWebResponse)partRequest.GetResponse();ReadBytesFromResponseToFile(url, partResponse, position, leftSize, contentLength, filePath);partResponse.Close();}partRequest.Abort();//下载完成Finished(url);}catch (Exception e){Finished(url, e);}}

ReadBytesFromResponseToFile:

   /// <summary>/// 从网上下载资源字节到存到文件中/// </summary>/// <param name="requestUrl"></param>/// <param name="response"></param>/// <param name="allFilePointer"></param>/// <param name="length"></param>/// <param name="totalSize"></param>/// <param name="filePath"></param>private void ReadBytesFromResponseToFile(string requestUrl,WebResponse response,long allFilePointer,long length,long totalSize,string filePath){try{int bufferLength = (int)length;byte[] buffer = new byte[bufferLength];//本块位置指针int currentChunkPointer = 0;//指针偏移量int offset = 0;using (Stream resStream = response.GetResponseStream()){//下载的字节数int receivedBytesCount;do{receivedBytesCount = resStream.Read(buffer, offset, bufferLength - offset);offset += receivedBytesCount;if (receivedBytesCount > 0){byte[] bufferCopyed = new byte[receivedBytesCount];Buffer.BlockCopy(buffer, currentChunkPointer, bufferCopyed, 0, bufferCopyed.Length);using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)){fs.Position = allFilePointer;fs.Write(bufferCopyed,0,bufferCopyed.Length);fs.Close();}float progress = (allFilePointer + bufferCopyed.Length) / totalSize;//执行进度委托Action action = () => {if (m_listTasks.Any(task => task.Url == requestUrl)){DownloadTask task = this.m_listTasks.FirstOrDefault(t => t.Url == requestUrl);task.TotalProgress((int)(progress*100), 0, 0);if (TaskProgress != null){int finishedCount = this.m_listTasks.Count(t => t.bFineshed);string filename = task.FileName.Substring(task.FileName.LastIndexOf("/") + 1);TaskProgress(m_listTasks.Count, finishedCount, filename);}}};action.Invoke();currentChunkPointer += receivedBytesCount;allFilePointer += receivedBytesCount;}} while (receivedBytesCount != 0);}}catch (Exception e){Debug.LogException(e);}}

Finished:

    private void Finished(string url, Exception e = null){Debug.Log("下载完成!");DownloadTask task = this.m_listTasks.FirstOrDefault(t => t.Url == url);if (task != null){if (e != null){Debug.LogWarning("下载出错!" + e.Message);}else {//验证MD5码DownloadFinishedWithMd5(task);}}}

最后进行Md5的验证,成功就修改任务finished布尔为true。

    /// <summary>/// 下载完成之后验证MD5码/// </summary>/// <param name="task"></param>private void DownloadFinishedWithMd5(DownloadTask task){string md5 = UnityTools.BuildFileMd5(task.FileName);if ("123".Trim() != task.MD5.Trim()){//MD5验证失败if (File.Exists(task.FileName)){File.Delete(task.FileName);}task.bDownloadAgain = true;task.bFineshed = false;CheckDownloadList();return;}if (FileDecompress != null){FileDecompress(true);}task.bDownloadAgain = false;task.bFineshed = true;task.Finished();if (FileDecompress != null){FileDecompress(false);}CheckDownloadList();}

因为我做下测试,所以我这里只是用“123”来代替md5,实际上的话,得自己取得下载后文件的MD5码和服务器上的MD5验证,如果通过的话,就完成,没通过的话重新下载。

UnityTools.BuildFileMd5:

        /// <summary>/// 生成文件的md5码/// </summary>/// <param name="filePath"></param>/// <returns></returns>public static string BuildFileMd5(string filePath){string fileMd5 = null;try{using (FileStream fs = File.OpenRead(filePath)){MD5 md5 = MD5.Create();byte[] fileMd5bytes = md5.ComputeHash(fs);fileMd5 = System.BitConverter.ToString(fileMd5bytes).Replace("_", "").ToLower();}}catch (Exception e){Debug.Log(e);}return fileMd5;}

  

最后回到DownloadMgr里面的CheckDownloadList方法里面,在开始断点下载处,添加代码:

    /// <summary>/// 检测下载列表/// </summary>public void CheckDownloadList(){if (Tasks.Count == 0){//下载列表为空return;}//已经下载完成的数目int finishedCount = 0;foreach (var task in Tasks){if (task.bFineshed && !task.bDownloadAgain){finishedCount++;}else {//是否文件存放的目录已经存在,如果不存在就创建string dir = Path.GetDirectoryName(task.FileName);if (!string.IsNullOrEmpty(dir) && !Directory.Exists(task.FileName)){Directory.CreateDirectory(dir);}//开始断点下载ThreadDownloadBreakPoint bpDownload = new ThreadDownloadBreakPoint(this, task);Thread t = new Thread(bpDownload.Download);t.Start();}break;}if (finishedCount > m_listTasks.Count - 1){m_listTasks.Clear();m_listTasks = null;if (AllDownloadFinished != null){AllDownloadFinished();AllDownloadFinished = null;}}}

开启一个线程来断点下载。

OK,欧了。我们来做下测试。

到www目录下的LOLGameDemo文件夹下新建一个文件夹:LOLPackage,跟我们的ServerVersion.xml里面的PackageUrl的文件目录一致:

这个文件主要是来存放需要更新的资源文件包。然后在里面新建一个压缩包,和我们的PackageMd5List的name属性一致:

然后就启动程序,观察unity:

可以看到在Project的Resources文件夹下,多了一个压缩包:就是我们放在Http服务器的压缩包,说明下载成功了。

仿LOL项目开发第三天链接地址

转载于:https://www.cnblogs.com/CaomaoUnity3d/p/5459265.html

仿LOL项目开发第二天相关推荐

  1. 仿LOL项目开发第一天

    ---恢复内容开始--- 仿LOL项目开发第一天 by---草帽 项目源码研究群:539117825 最近看了一个类似LOL的源码,颇有心得,所以今天呢,我们就来自己开发一个类似于LOL的游戏demo ...

  2. 仿LOL项目开发第四天

    ---恢复内容开始--- 仿LOL项目开发第四天 by草帽 上节讲了几乎所有的更新版本的逻辑,那么这节课我们来补充界面框架的搭建的讲解. 我们知道游戏中的每个界面都有自己的一个类型:比如登陆界面,创建 ...

  3. 仿LOL项目开发第六天

    仿LOL项目开发第六天 by草帽 OK,因为更新模块已经处理好了,接着开始登陆的编写.那么我们就需要状态机的管理. 所谓状态机就是在哪个状态执行那个状态的代码逻辑: 那么我们开始编写GameState ...

  4. 仿LOL项目开发第五天

    仿LOL项目开发第五天 by草帽 今天呢,我们看下能开发什么内容,首先上节我们已经讲了UI框架的搭建,上节还遗留下很多问题,比如说消息的字符是代码里面自己赋值的. 那么就比较死板,按照正常的逻辑,那些 ...

  5. 高仿百思不得姐项目开发(粗略笔记,后期规范排版和更新)

    转载于:https://www.cnblogs.com/goodboy-heyang/p/5150102.html

  6. FPGA项目开发:204B实战应用-LMK04821代码详解(二)

    大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术江湖,在"闯荡江湖"."行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢. ...

  7. android app开发混合开发,混合开发入门 Vue结合Android/iOS开发仿京东项目App

    download:混合开发入门 Vue结合Android/iOS开发仿京东项目App 无需原生开发基础,也能完美呈现京东商城.本课程融合vue.Android.IOS等目前流行的前端和移动端技术,混合 ...

  8. ASP.NET3.5 企业级项目开发 -- 第二章(续) 数据访问层(DAL)的开发解决方案提出...

    ASP.NET3.5 企业级项目开发 -- 第二章(续) 数据访问层(DAL)的开发解决方案提出 前言:首先给大家说声"对不起",因为自从打算写这系列的文章以来,得到大家很多的支持 ...

  9. ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发

    为什么80%的码农都做不了架构师?>>>    ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发          前言:本篇主要讲述数据访问层的开发, ...

最新文章

  1. oracle驱动程序包的安装失败,Maven 、oracle的jdbc的jar包下载失败
  2. 后台返回给前端数据拆分成三级菜单
  3. Vue CLI 3 多页应用项目的搭建
  4. MySQL中的日期和时间类型
  5. mysql创建表时遇到的问题_MySQL语言创建表时遇到了问题,请问我错在哪里了?...
  6. cpu和内存之间——地址映射
  7. 18年高考云南628分想学计算机,2018山东高考投档线公布!山大文624理628…快查查你学校多少分进档...
  8. Linux学习笔记(五):less|more的命令使用
  9. 汽车维保反欺诈系统的设计和算法应用
  10. [抽奖解决方案]单位年终搞一次新颖的、与时俱进的抽奖方式,可使大家对这个单位或团队文化的强力认同。
  11. java batik_Java Batik框架画SVG图 JSVGCanvas
  12. caj文件打不开显示内存不足_U盘能显示文件大小但打开没有任何文件 - 卡饭网...
  13. python弹幕点歌_GitHub - smilecc/blive-raspberry: 完全重构的树莓派B站直播弹幕点播台...
  14. 华为m2青春版刷机android6,华为揽阅M2青春版(PLE-703L)一键救砖教程,轻松刷回官方系统...
  15. Java从键盘上输入一个正整数n,然后计算1+2+...+n的结果并输出
  16. Code39码如何批量生成
  17. 开源社区普遍存在的几个观点之我见(1)开源的价值在哪里和(2)开源是不是商业...
  18. unity3d实现LOL中的相机控制功能
  19. 利用jquery实现双向数据绑定,网友:哎呦不错哦!
  20. javascript:dom的变动事件

热门文章

  1. django-admin动态多选框表单实现
  2. 点击链接页面不动,打开链接的实现方法
  3. java trunc函数_Oracle常用函数Trunc及Trunc函数用法讲解
  4. 文字台式计算机组装步骤,2017最新DIY主机教程 组装机图文+文字安装步骤
  5. [附源码]java毕业设计酒店管理系统
  6. python 协程 async/await
  7. Python--turtle之幸运星
  8. ssh研究生毕业环节管理系统 ssh框架 MVC模式MYSQL
  9. 2021-11-11 用eclipse编写摇骰子
  10. Unity 性能优化总结