Unity 热更原理(个人笔记版)

转自https://blog.csdn.net/qq_33537945/article/details/79326799

 想要使用热更新技术,需要规划设计好资源比较策略,资源版本,确保增加新资源后可以下载到本地,有资源更新的时候可以替换掉本地旧资源。

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

步骤一、在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 = "";

   /// 本地版本json对象

  private JsonData jdLocalFile;

  /// 服务端版本json对象

  private JsonData jdServerFile;

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

 private Dictionary<stringstring> LocalBundleVersion;

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

private Dictionary<stringstring> ServerBundleVersion;

/// 需要下载的文件List

private List<string> NeedDownFiles;

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

private bool NeedUpdateLocalVersionFile = false;

/// 下载完成委托

public delegate void HandleFinishDownload(WWW www);

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

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

ServerBundleVersion = new Dictionary<stringstring>();

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.Flush();

}

}

  }

 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;

}

 

//0表示本地版本文件,1表示服务器版本文件

private void ParseVersionFile(string content, Dictionary<stringstring> 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();

}

}

}

                                                              

 

 

 

 

 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值