【转】unity 热更新思路和实现
声明:本文介绍的热更新方案是我在网上搜索到的,然后自己修改了一下,相当于是借鉴了别人的思路,加工成了自己的,在此感谢无私分享经验的朋友们。
想要使用热更新技术,需要规划设计好资源比较策略,资源版本,确保增加新资源后可以下载到本地,有资源更新的时候可以替换掉本地旧资源。我在前面写了一篇“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 热更新思路和实现的更多相关文章
- 另类Unity热更新大法:代码注入式补丁热更新
对老项目进行热更新 项目用纯C#开发的? 眼看Unity引擎热火朝天,无数程序猿加入到了Unity开发的大本营. 一些老项目,在当时ulua/slua还不如今天那样的成熟,因此他们选择了全c#开发:也 ...
- unity热更新方案对比
Unity应用的iOS热更新 • 什么是热更新 • 为何要热更新 • 怎样在iOS 上对Unity 应用进行热更新 • 支持Unity iOS 热更新的各种Lua 插件的对照 什么是热更新 • ...
- Unity热更新对比
https://www.jianshu.com/p/f9d90edf4a7c Unity 热更新为啥用Lua 详解 ILRuntime的优势 同市面上的其他热更方案相比,ILRuntime主要有以下优 ...
- Unity 热更新实例一、C#Light 和UI系统使用实例
接下来我会运用热更新的机制,逐步制作一些例子来阐释脚本系统如何和Unity结合. 脚本不限于使用C#Lite,但是C#Lite会有一些便利之处,请往下看. 结合机制也不限于这一种,但是C#Lite的设 ...
- Unity热更新技术整理
一.热更新学习介绍 1.什么是热更新 举例来说: 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新 ...
- [Unity热更新]tolua# & LuaFramework(一):基础
一.tolua# c#调用lua:LuaState[变量名/函数名] 1.LuaState a.执行lua代码段 DoString(string) DoFile(.lua文件名) Require(.l ...
- Unity热更新 xLua
xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能.易用性.扩展性而广受好评.现在,腾讯已经将xLua开源到GitHub. 2016年12 ...
- Unity热更新 AssetBundle
在游戏开发中,常常需要用到热更新技术.比如:一个手机游戏开发好后,用户安装到手机上.如果此时我们要更新一个新的功能,如果没有热更新,那么需要用户卸载掉手机上的游戏,然后安装新的包,这样做十分麻烦,而且 ...
- C#版的eval,C#Light开源嵌入式脚本,unity热更新不再愁
目前最新版本AlphaV0.06 完全的c#语法,可用于一切能运行C#的场合,wp windows xamarin mono asp.net unity3d 内嵌了int uint bool stri ...
随机推荐
- java基本方法
Java 方法 在前面几个章节中我们经常使用到 System.out.println(),那么它是什么呢? println() 是一个方法. System 是系统类. out 是标准输出对象. 这句话 ...
- Jquery获取select标签的值、文本方式
<script> function add(){ var prop_name=$("#prop").find("option:selec ...
- ie浏览器下载文件时文件名乱码
做一个文件下载功能时,用ie浏览器下载时文件名乱码,火狐和谷歌正常,修改后ie显示正常,修改方法如下: @RequestMapping(value = "fileDownload" ...
- UIButtonType各个类型的解释:
UIButtonType各个类型的解释: typedef NS_ENUM(NSInteger, UIButtonType) { UIButtonTypeCustom = , UIButtonTypeS ...
- Springmvc+Spring+Mybatis整合开发(架构搭建)
Springmvc+Spring+Mybatis整合开发(架构搭建) 0.项目结构 Springmvc:web层 Spring:对象的容器 Mybatis:数据库持久化操作 1.导入所有需要的jar包 ...
- free -g 说明
free -g 说明: free -g -/+ buffers/cache 说明: buffer 写缓存,表示脏数据写入磁盘之前缓存一段时间,可以释放.sync命令可以把buffer强制写入硬盘 ca ...
- margin中的bug解决方法
margin bug问题 : 当做子元素中使用margin-top: 50px;父子元素都会跑出50px, 解决方法: 在父元素中使用下面三种任意一种都可以. 方法一:给父元素加边框 border: ...
- php无限级分类----封装函数
public function catetree($cateRes){//传递过来的数据资源 return $this->sort($cateRes); 调用函数 } public functi ...
- Hive(4)-Hive的数据类型
一. 基本数据类型 Hive数据类型 Java数据类型 长度 例子 TINYINT byte 1byte有符号整数 20 SMALINT short 2byte有符号整数 20 INT int 4by ...
- Qt——模态、非模态
模态: 只能操作对话框非模态:要使用 QDialog *_d = new QDialog();_d->setattribute(Qt::WA_DeleteOnClose);_d->show ...