xLua热更新(二)实现热更新

曾经终败给现在 2023-09-26 09:42 290阅读 0赞

一、环境配置

要实现热更新功能,我们首先需要开启热更新的宏。操作方法是在「File->Build Settings->Player Settings->Player->Other Settings->Scripting Define Symbols」选项中添加HOTFIX_ENABLE
77bb890665ec135493836004a959b811.png

开启后,在xLua的菜单中就出现了「Hotfix Inject In Editor」选项。
1d8506a9f2d6ecff8c148b666c70dc3b.png

当我们在开发补丁版本需要进行热更新测试时,都需要点击一次上图中的「Generate Code」选项重新生成一次代码,然后再点击「Hotfix Inject In Editor」进行注入。
如果注入时出现了如下错误信息,我们需要将xLua源码中的「Tools」文件夹复制到我们工程的根目录下。
194d2f433b4afee12f5715478261ec9c.png

以上两步操作如果成功的话,在控制台都会有相应提示。环境配置完成后,可以运行xLua自带的热更新示例,测试一下是否能热更成功。
0bf3a6a0881223fc5b78f0c8754ad2fb.png

二、热更新原理

首先引用一段xLua作者的话,介绍一下xLua实现热更新的原理(原文出处)


热补丁的基本原理其实非常简单,了解后任何程序员都很容易分析出开销,比如对于这个类

  1. public class Calc
  2. {
  3. int Add(int a, int b)
  4. {
  5. return a + b
  6. }
  7. }

打了hotfix标签后,xLua会在il层面注入代码,注入之后这个类会类似这样:

  1. public class Calc
  2. {
  3. static Func<object, int, int, int> hotfix_Add = null;
  4. int Add(int a, int b)
  5. {
  6. if (hotfix_Add != null) return hotfix_Add(this, a, b);
  7. return a + b
  8. }
  9. }

如果lua中执行了hotfix调用,hotfix_Add会指向一个lua的适配函数。


也就是说,在给类加上[Hotfix]特性后,我们就可以在Lua中指定需要“替换”的方法。然后xLua就会将委托指向Lua中对应的函数。

明白了原理后,我们来尝试实现一个简单的案例

比如原本有一个C#脚本如下所示

  1. [Hotfix]
  2. public class HotfixExample : MonoBehaviour
  3. {
  4. private float _timer = 0f;
  5. [LuaCallCSharp]
  6. private void Update()
  7. {
  8. _timer += Time.deltaTime;
  9. if (_timer > 2f)
  10. {
  11. _timer = 0;
  12. Debug.Log("这是C#代码");
  13. }
  14. }
  15. }

运行结果如下
61a74f7e4576e6de717f59e7d5fcf76d.png

现在我们希望通过热更新的方式修改Update方法,那么只需要在Lua脚本中调用xlua.hotfix()方法即可。该方法的第一个参数传入需要热更的C#类,第二个参数传入需要覆盖的方法名,第三个参数传递一个function,作为覆盖后的新方法。

  1. -- HotfixExample.lua.txt
  2. local class = CS.XLuaExample.HotfixExample
  3. local engine = CS.UnityEngine
  4. -- 允许访问私有成员
  5. xlua.private_accessible(class)
  6. xlua.hotfix(class,"Update",function(self)
  7. self._timer=self._timer + engine.Time.deltaTime
  8. if self._timer>2 then
  9. self._timer = 0
  10. engine.Debug.Log("这是Lua代码")
  11. end
  12. end)

在运行结束前还需要将注入到C#方法的引用置空,所以再写一个置空的Lua脚本

  1. -- HotfixExampleDispose.lua.txt
  2. xlua.hotfix(CS.XLuaExample.HotfixExample,"Update",nil)

然后再写个C#脚本模拟调用Lua脚本的入口

  1. public class StartLua:MonoBehaviour
  2. {
  3. private LuaEnv _luaEnv;
  4. private void Awake()
  5. {
  6. _luaEnv = new LuaEnv();
  7. _luaEnv.DoString("require 'HotfixExample'");
  8. }
  9. private void OnDisable()
  10. {
  11. _luaEnv.DoString("require 'HotfixExampleDispose'");
  12. }
  13. private void OnDestroy()
  14. {
  15. _luaEnv.Dispose();
  16. }
  17. }

重新生成并注入代码后,运行项目,就会发现热更的Lua代码已经生效了
81853d676dcbc34310fbcbf43f59ee38.png

当然,在某些情况下我们只需要在原方法的基础上增加一点逻辑,而不是将方法完全覆盖。这时我们可以引入xLua中的util.lua脚本,并使用其提供的hotfix_ex()方法,实现增量热更

  1. local class = CS.XLuaExample.HotfixExample
  2. local engine = CS.UnityEngine
  3. xlua.private_accessible(class)
  4. local util = require 'util'
  5. util.hotfix_ex(class,"Update",function(self)
  6. -- 调用原本的方法
  7. self.Update(self)
  8. if self._timer>1 then
  9. engine.Debug.Log("这是Lua代码")
  10. end
  11. end)

效果如下
cf2d9cf62195c2c91045c75155efd014.png

下面来总结一下热更新的流程

  • 首先我们在开发时应该在所有可能需要热更的类前加上[Hotfix]特性,在所有可能需要调用Lua脚本的地方加上[CSharpCallLua],在所有可能被Lua调用的地方加上[LuaCallCSharp],也可以通过反射实现。
  • 如果真的需要热更新,那就通过编写Lua脚本覆盖所需的方法。Lua脚本应该有一个统一的入口集中加载。
  • 最后将热更新的脚本、资源上传到服务器。玩家的客户端检查到更新后将热更新内容下载到本地,完成热更新。

发表评论

表情:
评论列表 (有 0 条评论,290人围观)

还没有评论,来说两句吧...

相关阅读

    相关 更新

    单来说我的理解就是:当你的文件发现改变的时候页面自己就更新了。不用你再手动刷新页面。无刷新更新 常见的需求如赛事网页推送比赛结果、网页实时展示投票或点赞数据、在线评论或...

    相关 更新

    介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面   1.当修改了一个或多个文件; 2.文件系统接收更改并通知webpack; 3.we

    相关 webpack实现更新

    昨天看到公司有前端同事在研究`webpack`,今天有空正好找下资料也`look look` 我最早接触`webpack`是2019年6月-7月之间,当时对前端还是很模糊的,

    相关 更新

    虽然有插件开发,但热更新少不了。 ![Center][] 当我们需要更新插件的时候使用的是插件开发,当我们需要更新宿主程序就需要使用热更新 热更新使用框架bsdif