zoukankan      html  css  js  c++  java
  • unity3d 自己主动文件更新系统

             游戏内容变更之后。一般而言不会想让玩家下载整个游戏包又一次安装,由于这样会流失大量玩家。全部游戏更新是必须的。

    更新的内容包含 数据、资源、代码。

             基本原理:

    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<string,long> filehash_server=new Dictionary<string, long>();

            Dictionary<string,long>  filehash_local=new Dictionary<string, long>();

            List<string> needUpdateFile = new List<string>();

            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<string,long> data in filehash_server)

            {

                //更新须要的文件

                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<string> loadRefCount = new HashSet<string>();

    public delegate void delegateLoadFinish(GameObject go);

    public static IEnumerator LoadModel(string res,delegateLoadFinish onLoadFinish)

        {

            GameObject resObject = Resources.Load<GameObject>("cards/"+res);

            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);

        }

  • 相关阅读:
    安装composer后报错proc_open(): fork failed
    ZOJ4063 Tournament [The 2018 ACM-ICPC Asia Qingdao Regional Contest]
    BZOJ1191: [HNOI2006]超级英雄Hero
    BZOJ1270: [BeijingWc2008]雷涛的小猫
    BZOJ1303 [CQOI2009]中位数图
    BZOJ1192 [HNOI2006]鬼谷子的钱袋
    BZOJ1003 [ZJOI2006]物流运输 最短路+DP
    牛客国庆集训派对Day6 E-Growth
    BZOJ2208 [Jsoi2010]连通数
    BZOJ2761 [JLOI2011]不重复数字
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6963923.html
Copyright © 2011-2022 走看看