热更新是unity一个长久的话题,热更新主要有两部分,一部分就是资源,这个靠u3d的assetbundle就可以做,另一部分就是逻辑了。游戏更新难免会遇到一些流程或者UI的逻辑变动。在PC和Android上可以使用C#的反射来动态的生成代码,而IOS的Full AOT(完全 预先编译/静态编译)的性质,因此这个办法在IOS上使用不了。为了解决这个问题,我们引入了Lua。使用Lua的主要原因是因为Lua在Unity是当作文本资源使用的,Lua在Unity中的执行是依赖于解释器(虚拟机)读取Lua代码进行解释执行。所以工程中比较常变化的逻辑代码都是写在Lua中,通过更新Lua(文本文件)来实现逻辑的热更,而常用的热更新方案有sLua,xLua,uLua等等。公司这里使用的是ulua,因此今天就来初步解析并使用下uLua。为了方便学习,本文章使用的是SimpleFramework_NGUI_v0.4.1,这个集成了NGUI,uLua的框架。

1.简单认识uLua

uLua具体怎么去调用Lua的,怎么让Lua调用C#这里先不做具体研究,先简单地使用起来,不去探究原理了。我们先认识下uLua中的几个类,让我们不至于使用得不明不白。当然如果想要直接使用,则可以跳过这个部分。

(1)WrapFile.cs

WrapFile类的内容很简单,就是保存了一个需要生成Wrap的数组(binds)。通过_GT()方法来获取类的信息并标记类的类型。如果我们用C#写了个类想要在Lua中使用,可以在binds中添加

(2)BindLua.cs

BingLua.cs类里主要是uLua对Unity菜单栏的一些扩展。里面有一些批处理方法,比如Lua/Gen Lua Wrap Files实际上是调用了Binding()方法。这个方法用来遍历WrapFil里的binds数组,来生成相应的Wrap文件。而里面调用的GenLuaBinder()方法用来生成Wrap.lua和LuaBinder.cs。GenLuaDelegates()方法用来生成DelegateFactory.cs。 Binding()

(3)Wrap.lua

Wrap.lua使用了”import 类名”把类注册到lua中,import字段是在Lua.cs的LuaState()方法来绑定到lua的全局表中,并且用来调用LuaStatic.importWrap()方法。因为我们要调用Lua时一般会用用到LuaScriptMgr.cs。在New LuaScriptMgr()时,则会自动调用LuaState()方法来绑定一些全局的字段。

(4)LuaBinder.cs

LuaBinder.Bind()方法则是用来把Wrap.lua里的类和相应的Wrap类,通过Wrap类的Register()方法来进行绑定。

(5)DelegateFactory.cs

大致和LuaBinder类似,用来注册委托。


这样我们就知道通过Unity的菜单栏,uLua为我们做了什么事。我们该如何简单的使用uLua,下面我来演示一下。

2.简单使用uLua

首先,把AppConst.cs中的DebugMode设置为true,这是框架的设置。意思是我们会在Unity编辑器下来运行程序,而不是打包到具体平台上使用。当DebugMode为true时会读取工程的Lua文件夹来获取Lua文件。 以后如果需要导出具体平台上的包则需要通过Game/Build xxx Resource 他会根据不同平台把lua和资源生成到相应的文件夹下。

我们先写一个cs用来注册到Lua中:

1
2
3
4
5
6
7
8
public class CsToLuaTest
{
    public CsToLuaTest() { }
	public void  testprint()
    {
        Debug.Log("success");
    }
}

这里有一个坑,我们写的这个类不能继承MonoBehaviour。因为继承了MonoBehaviour的类不能使用构造函数来创建对象,只能用AddComponent()来创建对象。而uLua是通过把注册到Lua中的类所有的构造方法绑定为New来创建对象的。如果从Unity中新建的脚本记得把MonoBehaviour删去。

再写一个Lua文件LuaTest.lua,用来在Lua中调用我们写的testprint()方法:

1
2
3
4
local this = LuaTest;
function LuaTest.Test(data)
      data:testprint();
end

写一个类用来运行调用这个lua文件 StartTest.cs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class StartTest : MonoBehaviour {
<span class="k">public</span> <span class="k">void</span> <span class="n">Awake</span><span class="p">()</span>
<span class="p">{</span>

<span class="p">}</span>
<span class="c1">// Use this for initialization

void Start () {
CsToLuaTest test = new CsToLuaTest();
LuaScriptMgr mgr = new LuaScriptMgr();
mgr.Start();
mgr.DoFile("LuaTest.lua");//这里放在工程中Lua文件夹下
mgr.CallLuaFunction("LuaTest.Test", test);//传入一个CsToLuaTest对象

}
// Update is called once per frame
void Update () {

<span class="p">}</span>

}

接下来打开WrapFile.cs,仿照着把CsToLuaTest写进binds中。在菜单栏中点击Lua/Clear LuaBinder + Wrap Files。这是清除之前已经生成的文件。然后再点击Lua/Gen Lua Wrap Files。这样就能生成Wrap文件并在Lua中进行注册了。 把StartTest绑到场景中的物体上,运行游戏。就能看到我们在LuaTest.lua成功调用了CsToLuaTest.cs中的testprint()方法并打出了success的日志。 ulua菜单栏