游戏内容变更之后。一般而言不会想让玩家下载整个游戏包又一次安装,由于这样会流失大量玩家。全部游戏更新是必须的。
更新的内容包含 数据、资源、代码。
基本原理:
1、将须要更新的文件打包成AssetBundle文件,并计算各个文件的crc值。
以下代码将选择的文件分别导出为AssetBundle文件,并将每一个文件的crc值写入到crc.txt文件里,在Editor文件夹建立一个类,并复制以下代码。
能够在Project文件夹导出选择的文件。
public class ExportAssetBundles {
[MenuItem("Assets/Build AssetBundle From Selection Respective - Track dependencies")]
static void ExportResourceRespective()
{
// Bring up save panel
string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
string dirpath = path.Substring(0,path.LastIndexOf("/")+1);
string filename = path.Substring(path.LastIndexOf("/")+1);
if (path.Length != 0) {
#if UNITY_ANDROID
string targetDir = "Android/";
BuildTarget targetBuild = BuildTarget.Android;
#elif UNITY_IPHONE
string targetDir = "iPhone/";
BuildTarget targetBuild = BuildTarget.iPhone;
#elif UNITY_STANDALONE_WIN
string targetDir = "StandaloneWindows/";
BuildTarget targetBuild = BuildTarget.StandaloneWindows;
#endif
JSDocument.JSNode node = new JSDocument.JSNode("root");
Document.SNode[] nodes = node.putChildren("filehash",Selection.objects.Length);
for(int i=0;i<Selection.objects.Length;i++)
{
string name = Selection.objects[i].name+".unity3d";
uint crc = 0;
if(!Directory.Exists(dirpath+targetDir))
Directory.CreateDirectory(dirpath+targetDir);
BuildPipeline.BuildAssetBundle(Selection.objects[i],new Object[]{ Selection.objects[i]}, dirpath+targetDir+name,out crc,BuildAssetBundleOptions.CollectDependencies,targetBuild);
nodes[i].put("name",name);
nodes[i].put("crc",crc);
}
System.IO.File.WriteAllText(dirpath+targetDir+filename+".crc.txt",node.toJSONString());
}
}
}
2.须要一个资源更新server,将导出的AssetBundle文件和crc文件上传到资源更新server。能够用一个简单的httpserver。比如nginx。
3.client进入游戏之前。首先向更新server请求crc.txt文件。然后从本地的磁盘文件夹中查找crc.txt文件,检查须要更新的文件列表。然后从server下载须要更新的文件。这样,假设server没有更改文件,则仅仅须要下载一次。
4.最新的crc.txt文件到本地,以便下次查询。
以下代码演示 3,4 步骤,当中
Engine.Instance.server_datapath = 服务器下载地址。
Engine.Instance.local_datapath = Application.persistentDataPath+"/;
public static IEnumerator UpdateDataFromServer(UpdateProgress up)
{
string server_datapath = Engine.Instance.server_datapath;
string local_datapath = Engine.Instance.local_datapath;
byte[] server_crc_data = null;
Dictionary
Dictionary
List
Debug.Log("Load Server FileHash");
using(WWW www = new WWW(server_datapath+"crc.txt"))
{
yield return www;
if (!String.IsNullOrEmpty(www.error))
{
Debug.Log("Load Filehash Failed");
up(1.0f);
yield break;
}
JSDocument.JSNode node = new JSDocument.JSNode("filehash",www.text);
Document.SNode[] data = node.getChildren("filehash");
for(int i=0;i<data.Length;i++)
{
filehash_server[data[i].get("name","")] = data[i].get("crc",(long)0);
}
server_crc_data = www.bytes;
}
Debug.Log("Load Local FileHash");
//从本地载入文件MD5表。可能没有
try
{
JSDocument.JSNode node = new JSDocument.JSNode("filehash",System.IO.File.ReadAllText(local_datapath+"crc.txt"));
Document.SNode[] data = node.getChildren("filehash");
for(int i=0;i<data.Length;i++)
{
filehash_local[data[i].get("name","")] = data[i].get("crc",(long)0);
}
}
catch(Exception e)
{
Debug.Log(e.Message);
}
Debug.Log("Check FileHash");
//计算须要更新的文件
foreach(KeyValuePair
{
//更新须要的文件
if(!filehash_local.ContainsKey(data.Key) || filehash_local[data.Key] != data.Value)
{
needUpdateFile.Add(data.Key);
}
}
Debug.Log("Update File");
//下载并存储
for(int i=0;i<needUpdateFile.Count;i++)
{
using(WWW www = new WWW(server_datapath+needUpdateFile[i]))
{
yield return www;
byte[] bytes = null;
if (!String.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
yield break;
}
else
{
bytes = www.bytes;
}
up((float)i/needUpdateFile.Count);
string path = local_datapath+ needUpdateFile[i];
Debug.Log(path);
FileStream fs = new FileStream(path,FileMode.Create);
fs.Write(bytes,0,bytes.Length);
fs.Flush();
fs.Close();
// //保存
// BinaryWriter writer;
// FileInfo t =new FileInfo(local_datapath+ needUpdateFile[i]);
// if(!t.Exists)
// {
// writer = new BinaryWriter(t.Open(FileMode.OpenOrCreate));
// }
// else
// {
// t.Delete();
// writer = new BinaryWriter(t.Open(FileMode.Create));
// }
// writer.Write(bytes);
// writer.Close();
}
}
Debug.Log("Save FileHash");
if(needUpdateFile.Count>0)
{
//保存最新的文件MD5值表
// FileStream fs = new FileStream(local_datapath+"crc.txt",FileMode.Create);
// fs.Write(server_crc_data,0,server_crc_data.Length);
// fs.Flush();
// fs.Close();
BinaryWriter writer;
FileInfo t =new FileInfo(local_datapath+"crc.txt");
if(!t.Exists)
{
writer = new BinaryWriter(t.Open(FileMode.Create));
}
else
{
t.Delete();
writer = new BinaryWriter(t.Open(FileMode.Create));
}
writer.Write(server_crc_data);
writer.Close();
Debug.Log(local_datapath+"crc.txt");
}
}
5.载入已经更新完毕的存储在本地的AssetBundle,须要注意的是。Unity3d同样的文件同一时候仅仅能有一个AssetBundle在内存中,所以我们对于同样文件的载入做了同步。
(loadRefCount)。
static HashSet
public delegate void delegateLoadFinish(GameObject go);
public static IEnumerator LoadModel(string res,delegateLoadFinish onLoadFinish)
{
GameObject resObject = Resources.Load
if(resObject !=null)
{
onLoadFinish((GameObject)GameObject.Instantiate(resObject));
yield break;
}
//假设包括资源。返回
while(loadRefCount.Contains(res))
{
yield return true;
}
loadRefCount.Add(res);
Debug.Log(Engine.Instance.local_datapath+res+".unity3d");
AssetBundleCreateRequest crcLocalBundle = AssetBundle.CreateFromMemory(
System.IO.File.ReadAllBytes(Engine.Instance.local_datapath+res+".unity3d"));
yield return crcLocalBundle;
{
GameObject cardObject = GameObject.Instantiate(crcLocalBundle.assetBundle.mainAsset)as GameObject;
crcLocalBundle.assetBundle.Unload(false);
onLoadFinish(cardObject);
}
loadRefCount.Remove(res);
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章