最新完整热更新实战案例学习,包括资源热更新及代码热更新文末送书

Posted 呆呆敲代码的小Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最新完整热更新实战案例学习,包括资源热更新及代码热更新文末送书相关的知识,希望对你有一定的参考价值。

  • 🎬 博客主页:https://xiaoy.blog.csdn.net

  • 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉

  • 🎄 学习专栏推荐:Unity系统学习专栏

  • 🌲 游戏制作专栏推荐:游戏制作

  • 🌲Unity实战100例专栏推荐:Unity 实战100例 教程

  • 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

  • 📆 未来很长,值得我们全力奔赴更美好的生活✨

  • ------------------❤️分割线❤️-------------------------

Unity 热更新技术最新系列全面教程


Unity 热更新技术 |(七)完整热更新实战案例学习使用,包括资源热更新及代码热更新

前言

  • 开始学习热更新方面系列知识,就从这一篇开始吧!
  • 本系列教程 会从热更新基本概念出发,详细介绍热更新相关的全套知识点。
  • 如热更新技术基本原理、热更新主流方案、AssetBundle系列教程、Lua语言编程、Lua与Unity交互教程、Xlua框架使用、热更新实战教程等方面。
  • 热更新作为一个开发者必备技能,如果想学习的话,可以从现在开始了哦~
  • 接下来从热更新实战案例学习使用 开始学习吧!

一、xLua基础配置及使用方法

本篇文章会正式使用xLua完成一个简单的热更新实战案例,关于xLua的基本配置及导入的详细信息可以参考下面这篇文章:
Unity 热更新技术 |(六)xLua框架学习最新系列完整教程


二、Unity中的基本配置

2.1 开启宏 HOTFIX_ENABLE

File -> Bulid Setting -> Player Settings -> Player -> Other Setting -> Scripting Define Symbols下添加 HOTFIX_ENABLE

添加完之后点击Apply,菜单栏中会出现一个新的选项Hotfix Injece In Editor。
若是未出现该选项,则重启该Unity 项目即可。

2.2 将xLua 中的Tools 工具文件夹 复制到热更新工程中Assets同级目录

若是会看到报错,原因是因为有重复文件导致的,删除下图里的xlua,Xlua.Mini3个文件即可,Plugins/x86_64路径下的xlua也删掉即可


三、xLua在Unity中使用步骤

  1. 每次修改需要更新的脚本,需要运行Generate Code
  2. 执行Hotfix Inject In Editor,进行Lua注入。

检查Unity 中XLua热更新环境

  1. 引入命名空间 using XLua
  2. 在需要更新的类上方加入标签 [Hotfix]
  3. 在需要更新的方法上方加入 [LuaCallCSharp]
  4. 创建 LuaEnv
  5. 将 测试脚本HotfixTest 挂载到场景中。
  6. 执行 Generate Code
  7. 执行 Hotfix Inject In Editor ,进行Lua注入。
  8. 运行结果,lua虚拟环境搭建完毕。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;

    [Hotfix]
    public class HotfixTest : MonoBehaviour
    
        public LuaEnv luaEnv;
        // Start is called before the first frame update
        void Start()
        
            luaEnv = new LuaEnv();
            luaEnv.DoString("CS.UnityEngine.Debug.Log('hello world')");
            Debug.Log("Unity_Hello World!");
            luaEnv.Dispose();
        
    

运行结果如下:


四、AB包资源打包

关于AB包更多使用详情信息可查看文章:Unity 热更新技术 | (二) AssetBundle - 完整系列教程学习

记得要将AssetBundles-Browser解压后的Editor文件夹Copy到Unity的项目中。目录结构如下图所示。

此处直接介绍具体步骤,不再对AssetsBundle做过多赘述,有不明白的可以参考上述文章。

4.1实战场景构建

本次实战场景为玩家在场景中可以四处移动和旋转,按住鼠标左键可以向前方发射子弹。

先搭建一个简易场景并编写相关功能代码,场景及脚本如下:

public class PlayerController : MonoBehaviour

    public float moveSpeed = 10f;
    public float turnSpeed = 100f;
    public float fireTime = 0.5f;

    private float ver;
    private float hor;
    private float timer;
    
    void Update()
    
        InputControl();
    
    private void InputControl()
    
        ver = Input.GetAxis("Vertical");
        hor = Input.GetAxis("Horizontal");

        transform.position += transform.forward * ver * Time.deltaTime * moveSpeed;
        transform.Rotate(transform.up * hor * Time.deltaTime * turnSpeed);

        if (Input.GetMouseButton(0))
        
            if (timer <= 0)
            
                //发射子弹
				//
                timer = fireTime;
            
            else
            
                timer -= Time.deltaTime;
            
        
    

添加一个简易的UI界面UIDemo,用于游戏开始和结束,也拖成预制体。

4.2 将玩家、子弹和UI预制体打到AB包中

首先将玩家和子弹对象 拖成预制体,并设置AB包的名称。

我这里给玩家和子弹加了两个材质,也一并打到AB包中。

包括UI预制体以及新添加了两张图片素材也一并打成AB包,如下图所示:

然后点击Window -> AssetBundles Browser 进行打包。
更多AB内容可以查看文章:Unity 热更新技术 | (二) AssetBundle - 完整系列教程

这里就直接进行Build了。

Build之后会出现几个AB包文件,后续就是加载这几个文件进行AB包加载并获取其中的对象。

4.3 加载AB包中的玩家和子弹 并进行实例化

创建加载AB包的脚本方法 ABLoadManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ABLoadManager : MonoBehaviour

    private AssetBundle ab;
    private static ABLoadManager instance;
    public static ABLoadManager GetInstance()
    
        if (instance != null)
        
            return instance;
        
        else
        
            return new ABLoadManager();
        
    

    private void Awake()
    
        instance = this;
    

    /// <summary>
    /// 根据AB包名字加载AB包
    /// </summary>
    public void LoadAB(string packageName)
    
        //加载指定AB包
        ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/"+ packageName);
        //加载主包
        AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");
        //加载主包中的固定文件
        AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        //从固定文件中得到依赖信息
        string[] strs = abManifest.GetAllDependencies(packageName);
        //得到依赖包的名字并加载
        foreach (var s in strs)
        
            AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + s);
        
     

    /// <summary>
    /// 根据预制体名称获取对象
    /// </summary>
    /// <param name="objName"></param>
    /// <returns></returns>
    public GameObject GetABGameObject(string objName)
    
        GameObject abGO = ab.LoadAsset(objName, typeof(GameObject)) as GameObject;
        return abGO;
    

然后在GameManager.cs 中先对UI预制体进行加载:

public class GameManager : MonoBehaviour

    private Transform canvas;

    private void Awake()
    
        canvas = GameObject.Find("Canvas").transform;
    
    void Start()
    
        ABLoadManager.GetInstance().LoadAB("ui");
        Instantiate(ABLoadManager.GetInstance().GetABGameObject("UIDemo"), canvas);
    

之后在GameController.cs中添加UI相关的方法,代码如下所示:

public class GameController : MonoBehaviour

    public Button btn_Start;
    public Button btn_Quit;
    public Image image_BG;

    private bool isPlay;
    //public LuaEnv luaenv;

    private void Awake()
    
        isPlay = false;
        btn_Start.onClick.AddListener(StartGame);
        btn_Quit.onClick.AddListener(QuitGame);
    

    private void StartGame()
    
        if (isPlay) return;
        isPlay = true;

        //加载AB包,获取图片素材
        ABLoadManager.GetInstance().LoadAB("texture");
        image_BG.sprite = ABLoadManager.GetInstance().GetABSprite("common_btn");
        btn_Start.image.sprite = ABLoadManager.GetInstance().GetABSprite("common_btn");
        btn_Quit.image.sprite = ABLoadManager.GetInstance().GetABSprite("common_btn");

        //加载AB包,实例化玩家
        ABLoadManager.GetInstance().LoadAB("module");
        GameObject player = Instantiate(ABLoadManager.GetInstance().GetABGameObject("Player"), new Vector3(0, 0, 1), Quaternion.identity);
    
    private void QuitGame()
    
#if UNITY_EDITOR //如果是在编辑器环境下
        UnityEditor.EditorApplication.isPlaying = false;
#else//在打包出来的环境下
    Application.Quit();
#endif
    

其中GameManager和ABLoadManager挂载到场景中,GameController挂载到UIDemo上面。

此时AB包的内容就算是暂时做完了,运行看一下效果:


打包成exe文件查看效果:

这样在开始运行之后,会从uiAB包中加载UIDemo并实例化,然后点击开始之后会从texture AB包中加载素材并赋值给Image组件,然后从module AB包中加载并实例化玩家。

当玩家按下鼠标左键时,从AB包中加载子弹并实例化发射子弹。

这样一个简单的Demo就制作好了,下面开始正式进行资源热更新和代码热更新的操作。


五、资源热更新

先来讲一下资源热更新,一般是用于项目中的资源替换,比如UI素材替换,预制体替换(材质/网格等)。
资源热更新使用Unity的AssetsBundle就可以做到,下面就用一个简单的案例来演示一下资源的热更新。

目的:利用AB包对项目中的UI素材和子弹的材质进行替换。

在上面我们已经构建了一个简单的场景并且可以运行了,下面在Unity中把子弹的颜色由原来的红色材质替换为绿色材质。

只需要在子弹预制体中将绿色的材质球拖到子弹上即可

然后再找一个新的素材拖到项目中,将名字改为common_btn,并将原来的那张图片删除或者改个别的名字,记得将新的素材也要设置AB包名称。

然后重新打开Window -> AssetBundle Browser 并进行Build。

此时打开我们刚才已经打包成exe的文件夹,将刚才Build的AB包进行替换。


替换前后对比如下
替换前:

替换后:

此时我们可以看到,在打包后的文件夹中只需要对AB包进行替换就可以完成一次资源的热更新,而无需重新从Unity重新打包即可生效。

在正常的游戏项目中如果遇到某些活动,游戏中的UI界面就会发生变化,此时就用到了热更新中的资源热更新。
只需要在玩家打开游戏时检查游戏的版本号,若需要进行更新,则从服务器中下载对应的AB包,然后将原来的包进行替换就可以实现该效果了。

资源热更新也可以通过代码热更新来实现,只需要将对应资源的调用代码进行更新替换同样也可以做到资源的热更新。
下面就来看看代码热更新怎样做到吧,这块属于本文的重点内容。


六、代码热更新(重点)

若是整个游戏项目都使用Lua脚本完成,那在打包后的游戏中直接对Lua脚本替换就可以实现代码的一次热更新。
因为Lua语言的特性如此,所以不向C#一样需要重新编译后才可以实现功能。

若项目原来使用的C#实现,现在需要使用到热更新,那就需要下面的方法来进行了,也就是利用了xLua的一个热补丁特性。

xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。
配置有两必须两建议:

  • 列表方式均必须是static的字段/属性
  • 列表方式均必须放到一个static类
  • 建议不用标签方式
  • 建议列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其它dll,必须放Editor目录)

更多内容可以来XLua文章查看:Unity 热更新技术 |(六)xLua框架学习最新系列完整教程

下面的测试用例使用打标签([Hotfix])的方式练习,就是在需要进行热更的类上面添加 [Hotfix] 特性。

6.1 在Resources创建lua文件夹保存lua脚本,unity中加载lua文件要以.lua.txt结尾,否则Unity不识别.lua文件

6.2 创建LuaEnv 脚本管理LuaManage并自定义加载器MyLoader。

using System.IO;
using UnityEngine;
using XLua;
public class LuaManager : MonoBehaviour

    public static LuaManager _instance;
    public static LuaManager Instance
    
        get
        
            return _instance;
        
    
    [CSharpCallLua]
    public delegate void LuaDelegate(string paras);
    /// <summary>
    /// 定义一个Delegate,Lua结果将传参回调给该Delegate
    /// </summary>
    public static LuaDelegate LuaFunc;
    /// <summary>
    /// 定义一个Lua虚拟机,建议全局唯一
    /// </summary>
    public static LuaEnv luaEnv;
    public LuaEnv GetLuaEnv()
    
        return luaEnv;
    
    void Awake()
    
        _instance = this;
        LuaEnvInit();
    
    public void LuaEnvInit()
    
        luaEnv = new LuaEnv();
        luaEnv.AddLoader(MyLoader);
        ///lua脚本的主入口
        luaEnv.DoString("require 'updateInfo'");
        //获取Lua中全局function,然后映射到delegate
        luaEnv.Global.Get("LuaFunc", out LuaFunc);
    
    private byte[] MyLoader(ref string filepath)
    
        string abspath = Application.dataPath + "/Resources/lua/" + filepath + ".lua.txt";
        return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(abspath));
    

6.3 先来个进行测试,在lua文件夹中创建名为hello.lua.txt

--将GameManager脚本中的Test方法替换为下列ChangeCode方法
xlua.hotfix(CS.GameManager,'Test',function(self)
CS.UnityEngine.Debug.Log("Hello Lua!")
end) 

然后在GameManager.cs中添加一个Test方法,如下所示:

此时点击运行Generate Code在执行Hotfix Inject In Editor,开始运行,效果如下:

这样lua脚本更新GameManager脚本中的Test方法输出"Hello Lua!"

GameManager中的Test()中的内容已经由hello.lua替换成功啦!
这也算是一个最最入门的热更新完成啦~

6.4 修改 PlayerController.cs 脚本,在代码中添加一个 Test() 和 Jump() 用于测试。

using UnityEngine;
using XLua;

[Hotfix]
public class PlayerController : MonoBehaviour

    public float moveSpeed = 5f;
    public float turnSpeed = 500f;
    public float fireTime = 0.5f;

    private float ver;
    private float hor;
    private float timer;

    private void Start()
    
        Test();
    
    void Update()
    
        InputControl();
    

    private void InputControl()
    
        ver = Input.GetAxis("Vertical");
        hor = Input.GetAxis("Horizontal");

        transform.position += transform.forward * ver * Time.deltaTime * moveSpeed;
        transform.Rotate(transform.up * hor * Time.deltaTime * turnSpeed);

        if (Input.GetMouseButton(0))
        
            if (timer <= 0)
            
                //从AB包中 加载 并实例化子弹
                GameObject go = Instantiate(ABLoadManager.GetInstance().GetABGameObject("Bullet"), transform.GetChild(0).position, Quaternion.identity);
                go.GetComponent<Rigidbody>().AddForce(transform.forward * 10,ForceMode.Impulse);
                Destroy(go, 10f);
                timer = fireTime;
            
            else
            
                timer -= Time.deltaTime;
            
        
        Jump();
    

    public void Test()
    
        Debug.Log("1测试热补丁。");
    
    public void Jump()
    
        Debug.Log("2测试热补丁。");
    

6.5 热更新变量添加

xlua.util提供了auto_id_map函数,执行一次后你就可以像以前那样直接用类,方法名去指明修补的函数。

可以在xLua源码中找到util.lua.txt 并copy到Assets\\Resources\\lua中。
创建updateInfo.lua.txt编写脚本引入util,require 'util’即可使用。

在lua文件夹中创建名为updateInfo.lua.txt,代码如下:

require "util"

--将PlayerController脚本中的Test()替换为下列ChangeCode方法
xlua.hotfix(CS.PlayerController,'Test',function(self)
ChangeCode(self)
print("Fixed PlayerController!")
end) 
function ChangeCode(self)
   -- 改变玩家的射速和旋转速度
   self.fireTime = 0.05
   self.turnSpeed = 1000
end

--将PlayerController脚本中的Jump()替换为下列ChangeCode方法
xlua.hotfix(CS.PlayerController,'Jump',function(self)
JumpCode(self)

Unity 热更新技术 | 热更新的基本概念原理及主流热更新方案介绍

  • 🎬 博客主页:https://xiaoy.blog.csdn.net

  • 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉

  • 🎄 学习专栏推荐:Unity系统学习专栏

  • 🌲 游戏制作专栏推荐:游戏制作

  • 🌲Unity实战100例专栏推荐:Unity 实战100例 教程

  • 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

  • 📆 未来很长,值得我们全力奔赴更美好的生活✨

  • ------------------❤️分割线❤️-------------------------

Unity 热更新技术最新系列全面教程


Unity 热更新技术 | (一) 热更新的基本概念原理及主流热更新方案介绍


前言

  • 开始学习热更新方面系列知识,就从这一篇开始吧!
  • 本系列教程 会从热更新基本概念出发,详细介绍热更新相关的全套知识点。
  • 如热更新技术基本原理、热更新主流方案、AssetBundle系列教程、Lua语言编程、Lua与Unity交互教程、Xlua框架使用、热更新实战教程等方面。
  • 热更新作为一个开发者必备技能,如果想学习的话,可以从现在开始了哦~
  • 接下来先从热更新的基本概念原理及主流热更新方案介绍 开始学习吧!

一、什么是热更新

  热更新 是一种App软件开发者常用的更新方式。简单来说,就是在用户通过下载安装APP之后,打开App时遇到的即时更新。

游戏热更新 是指在不需要重新编译打包游戏的情况下,在线更新游戏中的一些非核心代码和资源,比如活动运营和打补丁。

(1)游戏上线后,在运营过过程中,如果需要更换UI显示,或者修改游戏的逻辑行为。传统的更新模式下,需要重新打包游戏,让玩家重新下载包体,造成用户体验不佳的情况。
(2)热更新允许在不重新下载游戏客户端的情况下,更新游戏内容。

热更新分为 资源热更新 和 代码热更新 两种,代码热更新实际上也是把代码当成资源的一种热更新,但通常所说的热更新一般是指代码热更新。

  • 资源热更新 主要通过AssetBundle来实现,在Unity编辑器内为游戏中所用到的资源指定AB包的名称和后缀,然后进行打包并上传服务器,待游戏运行时动态加载服务器上的AB资源包。
  • 代码热更新 主要包括Lua热更新、ILRuntime热更新和C#直接反射热更新等。由于ILRuntime热更新还不成熟可能存在一些坑,而C#直接反射热更新又不支持IOS平台,因此目前大多采用更成熟的、没有平台限制的Lua热更新方案。

二、热更新必要性 作用

  一个游戏中有个很最重要的部分就是要想方设法的留住用户,如果每次游戏内容发生变化时(这在网游中经常会发生),都需要用户去重新下载一个安装包(客户端),这无疑是对游戏用户的留存产生了一个极大的威胁。

热更新作用:能够缩短用户取得新版客户端的流程,改善用户体验。

  • 没有热更新情况:
    pc用户:下载客户端->等待下载->安装客户端->等待安装->启动->等待加载->玩
    手机用户:商城下载APP->等待下载->等待安装->启动->等待加载->玩

  • 有了热更新情况:
    pc用户:启动->等待热更新->等待加载->玩
    有独立loader的pc用户:启动loader->等待热更新->启动游戏->等待加载->玩
    手机用户:启动->等待热更新->等待加载->玩

通过对比就可以看出,有没有热更新对于用户体验的影响还是挺大的,主要就是省去用户自行更新客户端的步骤。

尤其手游是快节奏的应用,功能和资源更新频繁,特别是重度手游安装包常常接近1个G,如果不热更新,哪怕改动一行代码也要重新打个包上传到网上让玩家下载。
对于IOS版本的手游包IPA,要上传到苹果商店进行审核,周期漫长,这对于BUG修复类操作是个灾难。

所以说就需要热更新技术的出现来解决这个问题。


三、热更新原理

  游戏中一些UI界面和某些模型等等的显示都是通过去加载相应的素材来实现的,当我们只把对应的素材资源进行替换就可以界面和模型发生变化,这个时候我们可以让客户端通过资源对比后从而进行相关资源的下载就可以实现热更新了。

  比如在一个游戏中的某些资源我们是放在服务器中的,当我们需要更换游戏中的某些资源时(如UI界面,某个英雄数值需要调整)。
我们只需要把这些新的资源与旧的资源进行替换,而不需要重新下载整个安装包就可以完成一个游戏版本的更迭,就相当于实现了一次热更新。

  • C#热更原理:将需要频繁更改的逻辑部分独立出来做成DLL,在主模块调用这些DLL,主模块代码是不修改的,只有作为业务(逻辑)模块的DLL部分需要修改。游戏运行时通过反射机制加载这些DLL就实现了热更新。
  • lua热更原理:逻辑代码转化为脚本,脚本转化为文本资源,以更新资源的形式更新程序。

3.1 为什么实现热更新一般都是用Lua,而不是C#?

  既然游戏需要热更新,那么我们既然使用了 Unity引擎,为什么不能直接使用 C# 脚本去进行游戏热更新,反而大多都是使用Lua语言去实现热更新呢?

  这就不得不提一下C#语言的特性了,热更新本身对于资源热更新是非常容易的,Unity自带的AB包就可以轻松解决,难的是代码热更新,因为Unity中的C#是编译型语言,Unity在打包后,会将C#编译成一种中间代码,再由Mono虚拟机编译成汇编代码供各个平台执行,它打包以后就变成了二进制了,会跟着程序同时启动,就无法进行任何修改了。

所以直接使用C#进行热更新显然是不可行的,但是也不是说一点办法也没有。在安卓上可以通过C#的语言特性-反射机制实现动态代码加载从而实现热更新。

C#的编译流程:写好的代码->编译成.dll扩展程序(UnityEditor完成)->运行于Unity
C#热更具体做法:将需要频繁更改的逻辑部分独立出来做成DLL,在主模块调用这些DLL,主模块代码是不修改的,只有作为业务(逻辑)模块的DLL部分需要修改。游戏运行时通过反射机制加载这些DLL就实现了热更新。

但苹果对反射机制有限制,不能实现这样的热更。为了安全起见,不能给程序太强的能力,因为反射机制实在太过强大,会给系统带来安全隐患。

其中 ILRuntime 就是使用C#进行的热更新(后边主流热更新方案中会讲到,这里先提一下)。

  而 LUA 则是解释型语言,并不需要事先编译成块,而是运行时动态解释执行的。这样LUA就和普通的游戏资源如图片,文本没有区别,因此可以在运行时直接从WEB服务器上下载到持久化目录并被其它LUA文件调用。

Lua热更新解决方案是通过一个Lua热更新插件(如ulua、slua、tolua、xlua等)来提供一个Lua的运行环境以及和C#进行交互。

lua热更原理:逻辑代码转化为脚本,脚本转化为文本资源,以更新资源的形式更新程序。


四、热更新流程

热更的基本流程可以分成2部分:

  • 第一步:导出热更新所需资源
  • 第二步:游戏运行后的热更新流程

第一步、导出热更新所需资源

  1. 打包热更资源的对应的md5信息(涉及到增量打包)
  2. 上传热更对应的ab包到热更服务器
  3. 上传版本信息到版本服务器

第二步、游戏运行后的热更新流程

  1. 启动游戏
  2. 根据当前版本号,和平台号去版本服务器上检查是否有热更
  3. 从热更服务器上下载md5文件,比对需要热更的具体文件列表
  4. 从热更服务器上下载需要热更的资源,解压到热更资源目录
  5. 游戏运行加载资源,优先到热更目录中加载,再到母包资源目录加载

更新注意:
要有下载失败重试几次机制;
要进行超时检测;
要记录更新日志,例如哪几个资源时整个更新流程失败。


五、目前主流热更新方案

  下面举例了目前市面上比较主流的几种热更新方案,后面会针对这几种热更新方案都做一个比较详细的介绍,看一看各自的优缺点。

  • LUA热更(xLua/toLua等)(LUA与C#绑定,方案成熟)
  • ILRuntime热更
  • puerts
  • HyBridCLR(原huatuo)

5.1 LUA热更(XLua/ToLua)(LUA与C#绑定,方案成熟)

  Lua热更原理:逻辑代码转化为脚本,脚本转化为文本资源,以更新资源的形式更新程序
Lua系解决方案: 内置一个Lua虚拟机,做好UnityEngine与C#框架的Lua导出。典型的框架有xLua, uLua,大体都差不多。

  Lua热更新解决方案是通过一个Lua热更新插件(如ulua、slua、tolua、xlua等)来提供一个Lua的运行环境以及和C#进行交互。xLua是腾讯开源的热更新插件,有大厂背书和专职人员维护,插件的稳定性和可持续性较强。

  由于Lua不需要编译,因此Lua代码可以直接在Lua虚拟机里运行,Python和JavaScript等脚本语言也是同理。而xLua热更新插件就是为Unity、.Net、Mono等C#环境提供一个Lua虚拟机,使这些环境里也可以运行Lua代码,从而为它们增加Lua脚本编程的能力。

借助xLua,这些Lua代码就可以方便的和C#相互调用。这样平时开发时使用C#,等需要热更新时再使用Lua,等下次版本更新时再把之前的Lua代码转换成C#代码,从而保证游戏正常运营。

5.2 ILRuntime热更

  ILRuntime 项目是掌趣科技开源的热更新项目,它为基于C#的平台(例如Unity)提供了一个纯C#、快速、方便和可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码热更新。
ILRuntime项目的原理实际上就是先用VS把需要热更新的C#代码封装成DLL(动态链接库)文件,然后通过Mono.Cecil库读取DLL信息并得到对应的IL中间代码(IL是.NET平台上的C#、F#等高级语言编译后产生的中间代码,IL的具体形式为.NET平台编译后得到的.dll动态链接库文件或.exe可执行文件),最后再用内置的IL解译执行虚拟机来执行DLL文件中的IL代码。

  由于ILRuntime项目是使用C#来完成热更新,因此很多时候会用到反射来实现某些功能。而反射是.NET平台在运行时获取类型(包括类、接口、结构体、委托和枚举等类型)信息的重要机制,即从对象外部获取内部的信息,包括字段、属性、方法、构造函数和特性等。我们可以使用反射动态获取类型的信息,并利用这些信息动态创建对应类型的对象。

ILRuntime中的反射有两种:

  • 一种是在热更新DLL中直接使用C#反射获取到System.Type类对象;
  • 另一种是在Unity主工程中通过appdomain.LoadedTypes来获取继承自System.Type类的IType类对象,因为在Unity主工程中无法直接通过System.Type类来获取热更新DLL中的类。

5.3 puerts(普洱TS)

git地址:https://github.com/Tencent/puerts

puerts 解决方案: 内置一个JavaScript/TypeScript解释器,解释执行TypeScript代码。

  • 强大的生态
    引入Node.js以及JavaScript生态众多的库和工具链,结合专业商业引擎的渲染能力,快速打造游戏。
  • 拥有静态检查的脚本
    相比游戏领域常用的lua脚本,TypeScript的静态类型检查有助于编写更健壮,可维护性更好的程序
  • 高效/高性能
    支持反射Binding,无需额外(生成代码)步骤即可开发。也支持静态Binding,兼顾了高性能的场景。

5.4 HyBridCLR(原huatuo)

官方地址:https://focus-creative-games.github.io/hybridclr/about/#%E6%96%87%E6%A1%A3

HybridCLR(代号wolong) 是一个特性完整、零成本、高性能、低内存的近乎完美的Unity全平台原生c#热更方案。

  HybridCLR扩充了il2cpp的代码,使它由纯AOT (opens new window) runtime变成‘AOT+Interpreter’ 混合runtime,进而原生支持动态加载assembly,使得基于il2cpp backend打包的游戏不仅能在Android平台,也能在IOS、Consoles等限制了JIT的平台上高效地以AOT+interpreter混合模式执行。从底层彻底支持了热更新。
  HybridCLR开创性地实现了 Differential Hybrid Execution(DHE) 差分混合执行技术。即可以对AOT dll任意增删改,会智能地让变化或者新增的类和函数以interpreter模式运行,但未改动的类和函数以AOT方式运行,让热更新的游戏逻辑的运行性能基本达到原生AOT的水平。

个人觉得HyBridCLR最大的优点就是对Unity开发者们非常友好,在使用前搭建好各种配置之后,热更新方面的操作就不需要我们下功夫了,按照之前的开发正常进行就好,只要更换对应的dll文件就可以自动实现热更新功能,恐怖如斯~

后续会详细介绍下HybridCLR,并按照文档做一些案例用于学习使用HybridCLR进行热更新。


总结

  • 总而言之,热更新技术是随着时代发展所诞生的必然趋势,也是作为一个开发者必备的一项技能。
  • 后面会对热更新的解决方案做一个完整的系列教程,感兴趣的小伙伴可以点击订阅专栏立即查看哦!
  • 本篇文章介绍了热更新方面的相关概念知识,后续完整的热更新方案及教程请看专栏其他文章

以上是关于最新完整热更新实战案例学习,包括资源热更新及代码热更新文末送书的主要内容,如果未能解决你的问题,请参考以下文章

Unity 热更新技术 | 热更新的基本概念原理及主流热更新方案介绍

实现iOS图片等资源文件的热更新化: 一个简单完整的资源热更新页面

原Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

Unity2020 几种常用热更新方案的优劣及XLua实战

JAVA实现代码热更新

Unity上面有啥好的热更新方案