Unity中Xlua热更入门

在学习Xlua之前要先去了解Lua的语法规则,该文章主要是讲如何理解Xlua热更和一些注意事项,不能作为教程来阅读,热更想学习的话可以去参考其它人的好文章

首先要知道xlua 的热更原理,热更新打补丁其实就是用lua写方法把原在c#中的方法覆盖,执行lua中的覆盖方法即可;假设在一个游戏已经打好包了,其中有个类的方法Funtion内容为a=b+c;这个时候这个方法有问题又或者出现了bug,需要修复,在不修改代码重新打包的情况下就可以直接用xlua热更新Function函数内容为a=b-c;这时就执行该lua的Function方法将其原方法覆盖。热更新除了修复代码以外还可以用来更新资源

Xlua热更新的执行顺序是在c#执行原本所有函数之前要执行一个函数来调用lua(其中lua的内容就是热更新的函数,其中lua中的热更新函数就需要在lua中调用c#中的函数),因此这里就涉及两个概念,c#调用lua和lua调用c#

C#中调用lua:需要用到xlua中的虚拟机,xlua提供了一个LuaEnv虚拟机,用来在c#的环境中执行lua脚本,使用方法就是在声明了一个lua虚拟机后,直接用luaenv.DoString();就可以调用,调用有两种形式,

1.直接用 luaenv.DoString("print('hello world')");执行lua语句

2.luaenv.DoString("require 'fish'");用require调用lua的fish脚本(其实这里也是lua语句,用lua语句执行reqiure而已)

在使用第二种lua的reqiure方法时就会涉及到loader的定义问题,require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。原生loader默认路径是Resources文件夹,如果想要从其它路径加载lua脚本,比如热更新从服务器上加载脚本就需要用到自定义loader,详情见xlua文档。还有c#调用lua中的全局变量,访问lua中的table等,因为不是热更新的重点,因此去看文档了解

Lua调用c#:在lua中写c#的代码,但是又要符合lua的语法规则;

  1. new C#对象:local newGameObj= CS.UnityEngine.GameObject();
  2. 调用C#静态方法:CS.UnityEngine.Debug.Log("这是我在lua调用的C#中的静态方法");
  3. 调用C#成员的属性和方法:属性可以直接调用,方法需要注意,在调用成员方法,第一个参数需要传该对象(也就是self),或者用冒号语法糖testobj:DMFunc()

  在了解完c#和lua之间的相互调用之后,就可以来了解热更新了,还有一个重要的知识点,那就是xlua 的配置(就是会生成一些配置代码来保证热更新),xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。常见的配置有LuaCallSharp以及CSharpCallLua和[Hotfix]其中第一个和第三个用得最多。

先以打标签的形式减少各个标签的作用

[Hotfix]的作用顾名思义就是指定某个类可以成为热更新的类,只有指明了某个类可以热更新后才能对其中的函数等进行修改。

[LuaCallSharp]的作用是当该类被打上[Hotfix]标签后,就可以在该类的需要被更新的函数打上

该标签,随后就可以在lua中对调用该函数进行热更新

[CSharpCallLua]的作用就是将lua中的函数映射到c#中时,就需要c#中有一个委托(还可以是接口等)来接收它,因此就需要在该委托上打上该标签

(区分LuaCallSharp以及CSharpCallLua两种生成各在什么场景下用?,因为该标签都是写在CSharp中,因此就是调用者和被调用者的区别;当LuaCallSharp为类打标签时,就是在Lua中调用C#,因此此时C#就是被调用者;同理CSharpCallLua就是要在C#中调用Lua,需要将Lua中的函数映射到C#中,因此C#就是调用者)

除了打标签的形式以外,还可以用静态列表和动态列表,用这两者就不用在每个需要更新的类或者方法打上标签,直接用列表的方式统一指定,静态列表和动态列表的区别就在于动态列表可以像热更新代码一样更新列表中的内容,因此如果没有采用动态列表的方式的话这里就需要主要在写c#时需要提前注意一下c#中哪些类需要更新,要打上热更新标签,除了需要提前打标签还需要提前在c#中调用lua的方法(因为需要调用lua才能将c#的方法进行覆盖)

以上是都是用xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,同样也有黑名单的形式

在了解完xlua的配置之后,就需要添加HOTFIX_ENABLE宏(在Unity3D的File->Build Setting->Scripting Define Symbols下添加),其中的XLua/Generate Code就是生成代码,生成代码的内容就是上方打标签的内容;生成代码后还需要注入代码XLua/Hotfix Inject In Editor,这样才能热更新执行成功(注意在每次修改完c#中的代码后都需要general生成和注入,这样lua才能生成代码执行来连接c#和lua(给的官方例子生成代码后会有相当多的bug!!!无论是版本不兼容,还是白名单有问题。。。最后在自己写热更新之前把example文件删除最好))

接下来就是lua中热更新代码的书写,在打好标签的前提下([Hotfix]和[LuaCallSharp]),就可以书写lua脚本(注意这里调用lua脚本最好创建一个专门用来挂载调用lua脚本的对象,并用自定义的loader来require加载lua脚本,这里还需要注意出来更新的lua脚本以外,还应该有个关于dispose的lua脚本(后面再详细讲),如果要给unity中某个脚本的start打补丁的话,要注意调用顺序,正确的顺序应该是要先打补丁,再执行游戏脚本的逻辑,如果调用打补丁的执行也在start中就不会成功打上补丁。因此在执行lua脚本时,应该把它放在最前执行(也就是在用c#调用lua时应该尽早)

Lua脚本的书写直接看例子

在热更新时,如果想要保留c#中的原有内容,添加附加内容可以用可以先执行c#已有的逻辑,然后再附加内容

到这里大致lua热更新的主要内容就是这些了,后续还有一些注意事项也比较重要

1.lua的dispose,因为在关闭程序时会自动释放lua 的虚拟机,如果主要释放C#还存在指向lua虚拟机里头某个函数的delegate,就会造成业务在虚拟机释放后无效调用(因为其引用的lua函数所在虚拟机都释放了)delegate导致的异常甚至崩溃,因此就需要手动释放lua虚拟机中的委托;如果你是通过xlua.hotfix(class, method, func)注入到C#,则通过xlua.hotfix(class, method, nil)删除;

2.在lua中如果使用随机数self.num=UnityEngine.Random.Range(0,4);其中num为c#中的int,这样num会直接为0;因为在lua中UnityEngine.Random.Range(0,4)默认产生的数为float的数,如果直接将float给int赋值就会变为0,如果转的话需进行强转

3.Lua中调用函数时,注意self的使用,1.在lua调用成员变量和方法时就需要用self(当this指针用)2.在调用类的成员方法时,如果用.调用时就需要传入self参数,用:调用就不用传入self

4.之前说了lua除了可以用来更新代码以外,还可以用来更新资源,在加载ab包时,因为需要用到泛型,因此在lua就不能实现,这就需要在项目发布之前提前预测然后使用c#(建议创建一个新类单独管理)加载ab包,随后用lua调用

5.如果热更新时,想要创建一个新的脚本时(不是在已有的函数上打补丁),这样如果直接在lua中操作就比较麻烦,这里推荐提前预测哪些游戏对象可能需要新增函数,随后在该游戏对象上挂载一个空的c#脚本专门用来用热更新添加新的函数

猜你喜欢

转载自blog.csdn.net/qq_62947569/article/details/134914384
今日推荐