超详细的Unity3D热更新框架,附示例链接,小白也能看的懂

前言:

  不管对于单机还是网络游戏,热更新已经成了标配。所谓热更,指的就是在无需重新打包的情况下完成资源、数据和代码的更新。

  本篇文章主要针对的是Unity3D开发的项目,其热更思路也可以应用到其他引擎诸如Cocos2D中。当然对于网页游戏或者小程序而言,开发语言使用lua、TyppScript、JavaScript等解释性语言,可以边运行边转换,资源和代码放到网络空间实时更新即可。

第一章  设计思路、本地网络空间的部署与资源划分

一,设计思路

热更包含代码热更、表格数据热更和美术资源热更三部分。

使用MD5效验文件版本,删除不在版本控制内的资源,如有变动替换并下载新的资源。

以下热更均在启动界面完成,热更完毕之后再切换到登录界面。

代码热更:
替换lua脚本后,开启lua解释器。

表格数据热更:
热更完毕后,替换表格数据资源。

------------美术资源分为图片资源和模型资源热更------------
图片资源热更:
热更完毕后进入游戏。

模型资源热更:
热更完毕后进入游戏。

以上流程和思路可以根据具体项目自行调整。

二,本地网络空间部署

  这个本来属于运维服务器的事情,但我们在开发的时候可以先用免费的软件来代替。我选用的是hfs网络文件管理器,在我的资源中有相关链接,需要的同学可以自己下载使用。

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第1张图片

  这里对于网络空间的部署,只是为了方便在客户端中进行热更下载。不管使用什么都可以,只要能提供下载就行。

  将链接拷贝下来,留着测试使用。

三,资源划分 

  按照设计思路,我们可以先划分出不同平台的不同目录,把Bytes、Scripts、Module、UI文件夹,在同级目录下补充一个效验用的Bundle.txt文件。各个文件夹下面具体的资源划分,可以根据项目需要调整。

  这里的文件夹划分仅仅是提供一个思路,只需要有相对应的解析方法,资源的划分完全可以使用自己的设计。

第二章  MD5效验与热更

一,MD5效验

  MD5最初是为加密而设计,但由于其存在漏洞而被舍弃。但它可以为我们提供数据完整性的效验,因为可以用来对比和效验需要热更的文件。

  多数脚本语言中,已经为我们提供好了解析MD5的库,在C#中它是:

using System.Security.Cryptography;//包含MD5库

  由此我们可以总结出“遍历该文件夹下所有子文件,并生成相对应的MD5”的需求,从而引申开发如下脚本,记录所有文件的json并存储为Bundle.txt。

1,先申明所需要用的类,这两个类在相应解析的时候也需要使用:

using System;

//MD5信息
[Serializable]
public class MD5Message
{
    public string file;//文件位置及名字
    public string md5;//MD5效验结果
    public string fileLength;//文件长度
}

//MD5全部信息
[Serializable]
public class FileMD5
{
    public string length;//总长度
    public MD5Message[] files;
}

2,通过遍历所有文件,得出正确的MD5值,删除并更新Bundle.txt

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System.Security.Cryptography;//包含MD5库

/// 
/// MD5 效验器
/// 遍历该文件夹下所有子文件,并生成相对应的MD5
/// 
public class MD5ACharm : MonoBehaviour
{
    [MenuItem("MD5效验器/平台/IOS平台")]
    static void BuildReleaseIOSBundle()
    {
        BuildBundleStart("iOS");
    }
    [MenuItem("MD5效验器/平台/Android平台")]
    static void BuildReleaseAndroidBundle()
    {
        BuildBundleStart("Android");
    }

    [MenuItem("MD5效验器/平台/Windows平台")]
    static void BuildReleaseWindowsBundle()
    {
        BuildBundleStart("Win");
    }

    static void BuildBundleStart(string _path)
    {
        ABPath = _path;
        Caching.ClearCache();//清除所有缓存   
        string path = GetTempPath();
        DeleteTempBundles(path);   //删除旧的MD5版本文件
        CreateBundleVersionNumber(path);
        AssetDatabase.Refresh();
    }

    private static Dictionary m_BundleMD5Map = new Dictionary();

    /// 
    /// 删除指定文件
    /// 
    /// 
    static void DeleteTempBundles(string path)
    {
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path);
        string[] bundleFiles = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
        foreach (string s in bundleFiles)
        {
            if(s== "Bundle.txt") File.Delete(s);
        }
    } 
    
    static void CreateBundleVersionNumber(string bundlePath)
    {     
        FileMD5 _file = new FileMD5();
        string[] contents = Directory.GetFiles(bundlePath, "*.*", SearchOption.AllDirectories);
        string extension;
        string fileName = "";
        string fileMD5 = "";
        long allLength = 0;
        int fileLen;
        m_BundleMD5Map.Clear();
        for (int i = 0; i < contents.Length; i++)
        {
            fileName = contents[i].Replace(GetTempPath(), "").Replace("\\", "/");
            extension = Path.GetExtension(contents[i]);
            if (extension != ".meta")
            {
                fileMD5 = GetMD5HashFromFile(contents[i]);
                fileLen = File.ReadAllBytes(contents[i]).Length;
                allLength += fileLen;
                m_BundleMD5Map.Add(fileName, fileMD5 + "+" + fileLen);
            }
        }

        var _list = new List();
        foreach (KeyValuePair kv in m_BundleMD5Map)
        {
            string[] nAndL = kv.Value.Split('+');
            MD5Message _md5 = new MD5Message();
            _md5.file = kv.Key;
            _md5.md5 = nAndL[0];
            _md5.fileLength = nAndL[1];
            _list.Add(_md5);
        }
        var _md5All = new MD5Message[_list.Count];
        for (var _i = 0; _i < _list.Count; _i++)
        {
            _md5All[_i] = _list[_i];
        }

        _file.length = "" + allLength;

        _file.files = _md5All;

        var _filePath = JsonUtility.ToJson(_file);

        Debug.LogError(_filePath);

        File.WriteAllText(GetTempPath() + "Bundle.txt", _filePath);
        m_BundleMD5Map.Clear();

    } 

    /// 获取文件的md5校验码
    static string GetMD5HashFromFile(string fileName)
    {
        if (File.Exists(fileName))
        {
            FileStream file = new FileStream(fileName, FileMode.Open);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(file);
            file.Close();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
                sb.Append(retVal[i].ToString("x2"));
            return sb.ToString();
        }
        return null;

    }

    //指定下载路径
    static string ABPath = "Win";
    static string GetTempPath(string _path="")
    {
        var _str = GetPathName() + "/MD5" + ABPath + "/"+_path;       
        return _str;
    }
   
    //网络空间的位置
    static string GetPathName()
    {
        return "E:/MyServer/Chief";
    }
}

  这里提供了三个常见的IOS、Android和Win平台,以提供不同平台的使用。

  有个知识点就是使用Directory.GetFiles(bundlePath, "*.*", SearchOption.AllDirectories);的方法,可以获取到该文件夹下的所有文件。

  我在需要热更的文件夹下放了一些测试资源,测试步骤及结果如下:

文件目录:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第2张图片

点击生成:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第3张图片

生成结果:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第4张图片

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第5张图片

3,根据MD5效验并进行热更

  根据设计思路,我们需要先下载Bundle.txt,从而获得热更资源的版本信息。首先对于不在版本管理内的资源进行删除,尔后对于不符合MD5效验的旧资源进行删除和替代:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;//包含MD5库
using System;
using UnityEngine.UI;

/// 
/// 获取到更新脚本
/// 
public class Get : MonoBehaviour
{
    private bool IS_ANDROID = false;

    private static Text fillAmountTxt;

    void Start()
    {        
        transform.Find("Button").GetComponent

  这里为了方便阅读,屏蔽了图片加载和模型加载的测试。点击运行结果如下:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第6张图片

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第7张图片

第三章  测试用例

  这里只做简单的引申,在我们项目中,常用的也就是图片、txt文件以及AB包。

  除了第二章中使用txt文件热更,在这里添加了图片与AB包的测试;具体打包方法可以使用官方插件,全部代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;//包含MD5库
using System;
using UnityEngine.UI;

/// 
/// 获取到更新脚本
/// 
public class Get : MonoBehaviour
{
    private bool IS_ANDROID = false;

    private static Text fillAmountTxt;

    void Start()
    {        
        transform.Find("Button").GetComponent

测试结果如下:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第8张图片

真机测试结果如下:

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第9张图片

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第10张图片

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第11张图片

第四章  总结

  在实际测试过程中,AB包可以使用官方插件来处理,但需要注意的是导出的时候,shader要放入设置里,否则容易出现材质球丢失的问题。

超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_第12张图片

  本篇博客的设计思路是,上传效验的文件,本地比对更新。测试用例中引申了图片、模型和txt文本的读取,但就实际代码热更的时候,需要考虑所用的lua框架;这部分暂时不放入热更之中。

  简而言之,本篇博客只提供上传和下载的方法。具体的使用,还需要根据项目实际划分。最后,使用链接已经上传到我的资源中。

第五章  WWW类已过时

  如果现在用新版本的朋友,应该会发现WWW类提示已过时的字样,原因是IOS9.0以上版本已经禁止了Http协议。部分安卓手机如三星高版本,在海外测试的时候也有类似问题。

  所以当Unity官方提示你更新的时候,别犟~按照官方要求去改就行。新的测试方法代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;//包含MD5库
using System;
using UnityEngine.UI;
using UnityEngine.Networking;

/// 
/// 获取到更新脚本
/// 
public class Get : MonoBehaviour
{
    private bool IS_ANDROID = false;

    private static Text fillAmountTxt;

    void Start()
    {
        transform.Find("Button").GetComponent

  这里将所有WWW方法替换为了UnityWebRequest,另外API:DownloadHandlerAssetBundle.GetContent十分好用。

  不过由于项目中代码,现在已经与测试版本相差过大,所以这个测试版本没测试,可能有部分细微差别,请大家自行取用。

你可能感兴趣的:(U3D,android,unity)