目录:
1、有资源更新需求时,资源的存储方式
2、下载资源的两种方式
3、断点续传的方式
在项目中。资源的更新是网络游戏中必不可少的一个环节。本文谈的是如何管理和下载资源
1、有资源更新需求时,资源的存储方式和读取方式
Unity提供了两个目录来存放资源:
streamingAssetsPath:此路径下的文件只能读取。不能写入。文件随包的生成打入包中。不能修改
persistentDataPath:一个持久化路径,可以读取和写入
在有自动更新的需求时,需要决定哪些文件放入streamingAssetsPath里,另外一下用于自动更新,存在persistentDataPath里面。1)全部放到streamingAssetsPath文件夹里,打出的包就会大,而且更新频繁的话,资源就会存在两份,persistentDataPath也会有一份资源,这样就会形成一个特别大的包。(刚开始做游戏的时候。做过一个特别傻的事,就是为了加载的时候方便一些。开始的时候把streamingAssetsPath里面的文件拷贝到persistentDataPath里。这样一开始就出现特别大的包)
2)只放自动更新的UI资源。其他的资源要从网上下载。这样第一次开游戏的时候。会更新很久。
3)还有一种方式就是放入初始一部分资源。到游戏进行的某个阶段的时候,再进行阶段性的更新。这样做的方式就是操作复杂。
根据项目的需求。一般都会选中1和3这两种方式。2在第一次开始游戏的时候。等待长时间的下载。体验实在太差,开始游戏的前几分钟。是留存的关键。所以不建议用2的方式。先说说方式1如何获取需要更新的文件,方式3只是1的变种
在更新的时,需要判断哪些文件需要自动更新。不可能把每个文件都下载下来之后。再比对MD5码,然后才能确定是不是更新文件。要不要保存。这个时候,在服务器端需要有一个Index文件。里面保存了文件名称、MD5码、文件长度。在客户端也需要一个相应的文件在persistentDataPath中(需要修改,只能在这个文件夹里),就叫local.version。该文件保存了文件名、MD5码、文件是否在streamingAssetsPath的标记(1:文件在streamingAssetsPath,0:文件在persistentDataPath),这个标记用于资源加载的时候。觉得从哪个文件夹去加载文件
在第一次启动游戏时,需要把streamingAssetsPath文件夹里面的文件的数据。写入到local.version中。在local.version中就有记录客户端最新的资源是哪个版本。再和服务器上面的Index文件对比之后。就能找出需要更新的文件
方式3就是在服务器端有多个Index文件。比如Index1、Index2等等,后面的Index数据包含前面的Index文件数据。这样每次只要对比相应的Index文件就能找到需要更新的文件,通过游戏进程来判断找哪个Index文件做比较
要读取streamingAssetsPath里面的文件,正常的IO操作是不读取不了的。得要用www的方式来读取文件,不同的平台,需要添加不同的后缀
string getStreamingPath_for_www()
{
string pre = "file://";
#if UNITY_EDITOR
pre = "file://";
#elif UNITY_ANDROID
pre = "";
#elif UNITY_IPHONE
pre = "file://";
#endif
string path = pre + Application.streamingAssetsPath + "/";
return path;
}
以拷贝数据为例,拷贝streamingAssetsPath文件夹下面名为fileName的文件拷贝到persistentDataPath中
IEnumerator copy(string fileName)
{
string src = getStreamingPath_for_www() + fileName;
string des = Application.persistentDataPath + "/" + fileName;
WWW www = new WWW(src);
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("www.error:" + www.error);
}
else
{
if (File.Exists(des))
{
File.Delete(des);
}
FileStream fsDes = File.Create(des);
fsDes.Write(www.bytes, 0, www.bytes.Length);
fsDes.Flush();
fsDes.Close();
}
www.Dispose();
}
确定了哪些需要更新的文件。接下来要做的就是更新操作
2、下载的接口有两种,一种是Unity自动的API:UnityWebRequest,另外一个是C#的HttpWebRequest功能
1)UnityWebRequest:Unity用于处理Web服务器Http通信流,包含上传和下载的功能,这里只用其中的下载功能。如示例所示,简单的下载只要提供一个Url就行
m_aUnityWebRequest = UnityWebRequest.Get(_url); //下载地址
m_aUnityWebRequest.SendWebRequest(); //请求下载
yield return m_aUnityWebRequest
byte[] arrData = m_aUnityWebRequest.downloadHandler.data //下载成功的二进制数据
2)HttpWebRequest:C#用于Http通信的类。这个接口是同步读取。会阻塞主线程,所以要新开线程来做资源下载
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_strUrl); //创建一个HttpWebRequest类,并且传入URL
Stream stream = request.GetResponse().GetResponseStream(); //向服务器请求数据,同步的
int length = stream.Read(buffer, 0, buffer.Length); //以下是读取数据
while(length > 0)
{
if (IsStop)
break;
fileSteam.Write(buffer, 0, length);
m_currentLength += length;
Debug.Log("GetProcess = " + GetProcess());
length = stream.Read(buffer, 0, buffer.Length);
}
stream.Close();
stream.Dispose();
request.GetResponse().Close();
3、断点续传:有些文件特别大,一次性下载下来会特别慢,需要分批下载,或者下载中途退出,下次再开始游戏的时候下载过的资源不再重新下载。这样就需要断点续传功能。
断点续传的核心操作是要保存一个临时文件和临时文件的MD5码文件,在重新下载的时候,先检查有没有临时文件以及临时文件的MD5码是否和网上的一直。如果不一致。则证明文件有更新,删除旧的临时文件重新下载。如果一致,则从临时文件的长度从网上获取数据。请求一个区间的数据。需要涉及到Http的报头,Http报头是个哈希表,想知道更多内容自己去查,这里用到的是一个“Range”的Key,Value是一个区间,例如("Range","bytes=50-100")表示请求的数据区间是从50-100
1)UnityWebRequest方式
m_aUnityWebRequest = UnityWebRequest.Get(_url); //下载地址
m_aUnityWebRequest.SetRequestHeader("Range", "bytes=" + nStartPos.ToString()+ "-" + (nStartPos + CHUNK_SIZE).ToString()); //这个是新增的,请求一个区间的数据
m_aUnityWebRequest.SendWebRequest(); //请求下载
yield return m_aUnityWebRequest
byte[] arrData = m_aUnityWebRequest.downloadHandler.data //下载成功的二进制数据
2)HttpWebRequest:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_strUrl); //创建一个HttpWebRequest类,并且传入URL
request.AddRange((int)m_currentLength); //这个就是请求的报头接口
Stream stream = request.GetResponse().GetResponseStream(); //向服务器请求数据,同步的
int length = stream.Read(buffer, 0, buffer.Length); //以下是读取数据
while(length > 0)
{
if (IsStop)
break;
fileSteam.Write(buffer, 0, length);
m_currentLength += length;
Debug.Log("GetProcess = " + GetProcess());
length = stream.Read(buffer, 0, buffer.Length);
}
stream.Close();
stream.Dispose();
request.GetResponse().Close();
和上面正常下载不同的是,断点续传只是多了一个请求区间的API调用。其他的都是一样