声明:本文介绍的热更新方案是我在网上搜索到的,然后自己修改了一下,相当于是借鉴了别人的思路,加工成了自己的,在此感谢无私分享经验的朋友们。

想要使用热更新技术,需要规划设计好资源比较策略,资源版本,确保增加新资源后可以下载到本地,有资源更新的时候可以替换掉本地旧资源。我在前面写了一篇“unity 打包AssetBundle”的文章,里面生成了一个资源版本文件,不多解释了,上图。至于怎么生成这个文件的,可以看一下我前面写的文章。

废话不多说。

先介绍热更新步骤,后上代码

步骤一、在Resources目录下新建一个文本,名称是bundle_list(后缀是.txt),内容如下:

{"id":0,"version":"1.0","manifest":"android","resource":{}},当然您可以根据自己项目

实际情况来设计json格式。资源服务器上也会有一份格式相同的bundle_list

步骤二、如果是第一次进入游戏,Application.persistentDataPath目录下还没有bundle_list文件,这

时候就需要用Resources.Load方法从Resources目录中加载出来。否则

加载Application.persistentDataPath目录下的bundle_list

步骤三、从资源服务器下载bundle_list文件

步骤四、获取本地bundle_list的id和资源服务器下载的bundle_list中的id,做对比,如果前者等于后者,

则不需要更新,如果前者小于后者,则需要更新。

步骤五、分别解析出本地和资源服务器bundle_list中的资源路径名称,名称相同的,对比hash值,相同

则不需要更新,反之,更新。如果资源服务器有的名称本地没有,则表示是新增资源,需要

下载到本地。

步骤六、把资源服务器的bundle_list覆盖本地bundle_list。热更新完成。

代码:

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using System.Text;

using System.IO;

using LitJson;

/**

 * 资源增量更新

 * 1.检查本地Application.persistentDataPath目录中是否有bundle_list文件

 * 2.如果没有,则从Resources目录中读取bundle_list文件

 * 3.从服务器上下载bundle_list文件,判断版本是否一致,如果一致就不用更新

 * 4.版本不一致,需要更新,更新

 * 5.将最新的bundle_list存入Application.persistentDataPath目录中

 **/

public class BundleUpdate : MonoBehaviour

{

    private static readonly string VERSION_FILE = "bundle_list";

    private string SERVER_RES_URL = "";

    private string LOCAL_RES_URL = "";

    private string LOCAL_RES_PATH = "";

    /// <summary>

    /// 本地版本json对象

    /// </summary>

    private JsonData jdLocalFile;

    /// <summary>

    /// 服务端版本json对象

    /// </summary>

    private JsonData jdServerFile;

    /// <summary>

    /// 本地资源名和路径字典

    /// </summary>

    private Dictionary<string, string> LocalBundleVersion;

    /// <summary>

    /// 服务器资源名和路径字典

    /// </summary>

    private Dictionary<string, string> ServerBundleVersion;

    /// <summary>

    /// 需要下载的文件List

    /// </summary>

    private List<string> NeedDownFiles;

    /// <summary>

    /// 是否需要更新本地版本文件

    /// </summary>

    private bool NeedUpdateLocalVersionFile = false;

    /// <summary>

    /// 下载完成委托

    /// </summary>

    /// <param name="www"></param>

    public delegate void HandleFinishDownload(WWW www);

    /// <summary>

    /// 本次一共需要更新的资源数

    /// </summary>

    int totalUpdateFileCount = 0;

    void Start()

    {

#if UNITY_EDITOR && UNITY_ANDROID                                                                         

        SERVER_RES_URL = "file:///" + Application.streamingAssetsPath + "/android/";

        LOCAL_RES_URL = "file:///" + Application.persistentDataPath + "/res/";

        LOCAL_RES_PATH = Application.persistentDataPath + "/res/";

#elif UNITY_EDITOR && UNITY_IOS

        SERVER_RES_URL = "file://" + Application.streamingAssetsPath + "/ios/";

        LOCAL_RES_URL =  "file:///" + Application.persistentDataPath + "/res/";

        LOCAL_RES_PATH =  Application.persistentDataPath + "/res/";

#elif UNITY_ANDROID

        //安卓下需要使用www加载StreamingAssets里的文件,Streaming Assets目录在安卓下的路径为 "jar:file://" + Application.dataPath + "!/assets/"

        SERVER_RES_URL =  "jar:file://" + Application.dataPath + "!/assets/" + "android/";

        LOCAL_RES_URL =  "jar:file://" + Application.persistentDataPath + "!/assets/" + "/res/";

        //LOCAL_RES_URL =  "file://" + Application.persistentDataPath + "/res/";

        LOCAL_RES_PATH =  Application.persistentDataPath + "/res/";

#elif UNITY_IOS

        SERVER_RES_URL = "http://127.0.0.1/resource/ios/"

        LOCAL_RES_URL =  "file:///" + Application.persistentDataPath + "/res/";

        LOCAL_RES_PATH =  Application.persistentDataPath + "/res/";

#endif

        //初始化    

        LocalBundleVersion = new Dictionary<string, string>();

        ServerBundleVersion = new Dictionary<string, string>();

        NeedDownFiles = new List<string>();

        //加载本地version配置    

        string tmpLocalVersion = "";

        if (!File.Exists(LOCAL_RES_PATH + VERSION_FILE))

        {

            TextAsset text = Resources.Load(VERSION_FILE) as TextAsset;

            tmpLocalVersion = text.text;

        }

        else

        {

            tmpLocalVersion = File.ReadAllText(LOCAL_RES_PATH + VERSION_FILE);

        }

        //保存本地的version    

        ParseVersionFile(tmpLocalVersion, LocalBundleVersion, 0);

        //加载服务端version配置    

        StartCoroutine(this.DownLoad(SERVER_RES_URL + VERSION_FILE, delegate (WWW serverVersion)

        {

            //保存服务端version    

            ParseVersionFile(serverVersion.text, ServerBundleVersion, 1);

            //计算出需要重新加载的资源    

            CompareVersion();

            //加载需要更新的资源    

            DownLoadRes();

        }));

    }

    //依次加载需要更新的资源    

    private void DownLoadRes()

    {

        if (NeedDownFiles.Count == 0)

        {

            UpdateLocalVersionFile();

            return;

        }

        string file = NeedDownFiles[0];

        NeedDownFiles.RemoveAt(0);

        StartCoroutine(this.DownLoad(SERVER_RES_URL + file, delegate (WWW w)

        {

            //将下载的资源替换本地就的资源    

            ReplaceLocalRes(file, w.bytes);

            DownLoadRes();

        }));

    }

    private void ReplaceLocalRes(string fileName, byte[] data)

    {

        try

        {

            string filePath = LOCAL_RES_PATH + fileName;

            if (!File.Exists(filePath))

            {

                string p = Path.GetDirectoryName(filePath);

                if (!Directory.Exists(p))

                    Directory.CreateDirectory(p);

            }

            File.WriteAllBytes(filePath, data);

        }

        catch (System.Exception e)

        {

            Debug.Log("e is " + e.Message);

        }

    }

    //更新本地的version配置    

    private void UpdateLocalVersionFile()

    {

        if (NeedUpdateLocalVersionFile)

        {

            if (!Directory.Exists(LOCAL_RES_PATH))

                Directory.CreateDirectory(LOCAL_RES_PATH);

            StringBuilder versions = new StringBuilder(jdServerFile.ToJson());

            FileStream stream = new FileStream(LOCAL_RES_PATH + VERSION_FILE, FileMode.Create);

            byte[] data = Encoding.UTF8.GetBytes(versions.ToString());

            stream.Write(data, 0, data.Length);

            stream.Flush();

            stream.Close();

        }

    }

    private void CompareVersion()

    {

        int localVersionId;

        int serverVersionId;

        if (jdLocalFile != null && jdLocalFile.Keys.Contains("id"))

            localVersionId = (int)jdLocalFile["id"];

        if (jdServerFile != null && jdServerFile.Keys.Contains("id"))

            serverVersionId = (int)jdServerFile["id"];

#if UNITY_ANDROID || UNITY_EDITOR

        NeedDownFiles.Add("android");

#endif

#if UNITY_IOS

#endif

        foreach (var version in ServerBundleVersion)

        {

            string fileName = version.Key;

            string serverHash = version.Value;

            //新增的资源    

            if (!LocalBundleVersion.ContainsKey(fileName))

            {

                NeedDownFiles.Add(fileName);

            }

            else

            {

                //需要替换的资源    

                string localHash;

                LocalBundleVersion.TryGetValue(fileName, out localHash);

                if (!serverHash.Equals(localHash))

                {

                    NeedDownFiles.Add(fileName);

                }

            }

        }

        totalUpdateFileCount = NeedDownFiles.Count;

        //本次有更新,同时更新本地的version.txt    

        NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;

    }

    /// <summary>

    /// 

    /// </summary>

    /// <param name="content"></param>

    /// <param name="dict"></param>

    /// <param name="flag">0表示本地版本文件,1表示服务器版本文件</param>

    private void ParseVersionFile(string content, Dictionary<string, string> dict, int flag)

    {

        if (content == null || content.Length == 0)

        {

            return;

        }

        JsonData jd = null;

        try

        {

            jd = JsonMapper.ToObject(content);

        }

        catch (System.Exception e)

        {

            Debug.LogError(e.Message);

            return;

        }

        if (flag == 0)//本地

        {

            jdLocalFile = jd;

        }

        else if (flag == 1)//服务器

        {

            jdServerFile = jd;

        }

        else

            return;

        //获取资源对象

        JsonData resObjs = null;

        if (jd.Keys.Contains("resource"))

            resObjs = jd["resource"];

        if (resObjs != null && resObjs.IsObject && resObjs.Count > 0)

        {

            string[] resNames = new string[resObjs.Count];

            resObjs.Keys.CopyTo(resNames, 0);

            for (int i = 0; i < resNames.Length; i++)

            {

                if (resObjs.Keys.Contains(resNames[i]))

                    dict.Add(resNames[i], resObjs[resNames[i]].ToString());

            }

        }

    }

    private IEnumerator DownLoad(string url, HandleFinishDownload finishFun)

    {

        WWW www = new WWW(url);

        yield return www;

        if (!string.IsNullOrEmpty(www.error))

        {

            Debug.LogError("www.error is " + www.error);

            yield break;

        }

        if (finishFun != null)

        {

            finishFun(www);

        }

        www.Dispose();

    }

}

  

【转】unity 热更新思路和实现的更多相关文章

  1. 另类Unity热更新大法:代码注入式补丁热更新

    对老项目进行热更新 项目用纯C#开发的? 眼看Unity引擎热火朝天,无数程序猿加入到了Unity开发的大本营. 一些老项目,在当时ulua/slua还不如今天那样的成熟,因此他们选择了全c#开发:也 ...

  2. unity热更新方案对比

    Unity应用的iOS热更新 •  什么是热更新 •  为何要热更新 •  怎样在iOS 上对Unity 应用进行热更新 •  支持Unity iOS 热更新的各种Lua 插件的对照 什么是热更新 • ...

  3. Unity热更新对比

    https://www.jianshu.com/p/f9d90edf4a7c Unity 热更新为啥用Lua 详解 ILRuntime的优势 同市面上的其他热更方案相比,ILRuntime主要有以下优 ...

  4. Unity 热更新实例一、C#Light 和UI系统使用实例

    接下来我会运用热更新的机制,逐步制作一些例子来阐释脚本系统如何和Unity结合. 脚本不限于使用C#Lite,但是C#Lite会有一些便利之处,请往下看. 结合机制也不限于这一种,但是C#Lite的设 ...

  5. Unity热更新技术整理

    一.热更新学习介绍 1.什么是热更新 举例来说: 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新 ...

  6. [Unity热更新]tolua# & LuaFramework(一):基础

    一.tolua# c#调用lua:LuaState[变量名/函数名] 1.LuaState a.执行lua代码段 DoString(string) DoFile(.lua文件名) Require(.l ...

  7. Unity热更新 xLua

    xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能.易用性.扩展性而广受好评.现在,腾讯已经将xLua开源到GitHub. 2016年12 ...

  8. Unity热更新 AssetBundle

    在游戏开发中,常常需要用到热更新技术.比如:一个手机游戏开发好后,用户安装到手机上.如果此时我们要更新一个新的功能,如果没有热更新,那么需要用户卸载掉手机上的游戏,然后安装新的包,这样做十分麻烦,而且 ...

  9. C#版的eval,C#Light开源嵌入式脚本,unity热更新不再愁

    目前最新版本AlphaV0.06 完全的c#语法,可用于一切能运行C#的场合,wp windows xamarin mono asp.net unity3d 内嵌了int uint bool stri ...

随机推荐

  1. HTML5零散知识点总结

    1.产生ioc图标的网站: http://www.bitbug.net/ 链接ioc图标: <link rel="shortcut icon" type="imag ...

  2. C# 5.0中使用CallerMemberName、CallerFilePath和CallerLineNumber获取代码的调用方信息(转载)

    很多时候,我们需要在运行过程中记录一些调测的日志信息,如下所示: public void DoProcessing() { TraceMessage("DoProcessing()被XXX调 ...

  3. c++构造函数详解(转)

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  4. ios下引用MUI后input不能输入,Android端正常

    原因是mui框架的有个css样式 *{ -webkit-user-select: none; } 其作用是禁掉用户可以选中页面中的内容. 添加以下style样式即可 input{ -webkit-us ...

  5. Bash游戏(51Nod - 1046)

    20180603 有一堆石子共有N个.A B两个人轮流拿,A先拿.每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜.假设A B都非常聪明,拿石子的过程中不会出现失误.给出N和K,问最后谁能赢得比赛 ...

  6. shell习题第1题:每日一文件

    [题目要求] 请按照这样的日期格式(xxxx-xx-xx)每日生成一个文件 例如生成的文件为2019-04-25.log,并且把磁盘使用情况写入到这个文件中 不用考虑cron,仅仅写脚本即可 [核心要 ...

  7. redis集群部署步骤

    1.yum 安装依赖 yum install gcc unzip wget 2.编译安装redis,编译安装的目的是源码包内包含了接下来创建redis集群所需要的 redis-trib.rb脚本 ma ...

  8. thinkphp 5.1/tp5.1 route路由bug

    tp5.1下面RuleItem类中,match方法. 如果同一个控制器下面,写了两个路由,后一个路由比包含前一个路由,则访问后一个路由地址的时候,会跳转到前面定义的那个路由

  9. excel 开头 结尾,中间 类似 SQL like ab% ,%ab ,%ab%

    excel  开头 结尾,中间  类似 SQL like 'ab%'  ,'%ab'  ,'%ab%' 在R2 单元格  查找   ab开头,ab结尾 =Search("ab",R ...

  10. CentOS6升级Python2.6到3.7,错误处理[No module named '_ctypes']

    CentOS6升级Python2.6到3.7,错误处理[No module named '_ctypes'] 因开发需要,在CentOS 6 服务器将Python2进行升级到Python3.由于工作中 ...