现在工作中经常用到XLua修复Bug或者使用Lua作为逻辑脚本,是时候学习一下源码看看如何实现这些方法的了。
那么XLua中是如何在Lua代码中调用Unity中的C#方法的呢?
Lua调用C#方法
CS是一个全局的Table,所以CS.UnityEngine可以当做是在一个名为CS的Table表中查询名为UnityEngine的值。
获取其值是通过CS的元方法__index来实现的。其逻辑代码在创建LuaEnv时候调用下面的代码,进行CS表的初始化。
下面代码是截取了部分的init_xlua代码。
这部分描述的是_index元方法的实现。元方法__index就是CS表中访问不存在的元素时候进行的操作。
比如CS={‘A=’a’,’B’=’b’},那么在Lua中直接访问CS.A就会返回a。但是如果访问C就会因为原来表中不存在这个记录,那么而调用__index这个方法。
代码实现注释如下
Lua中获取C#类对应的Table表
XLua中有两种方式来实现Lua调用CS中的方法,一种是反射来调用,一种是生成适配的代码。
在获取对应类的Lua表时候,使用的是import_type方法,也是在创建LuaEnv实例时候进行注册的代码如下。
上面代码中的importTypeFunction是一个C#委托当Lua中是调用import_type时候Lua会调用对应的C方法(Lua调用CFunction的原理,请查找Lua手册),最后会调用到对应的C#委托上来。
其中xlua全局table是在C中设置的代码如下:
代码很简单,luaopen_xlua是一个c函数,属于xlua.dll在创建LuaEnv时候会调用。
调用后会设置一个全局变量xlua,也就是ObjectTranslator类中获取的xlua变量。
然后将键值对”import_type”=C#委托,压入xlua表中。这样就能在inti_xlua.lua中调用import_type方法了。
C#中查找指定Type对应的Lua表的实现
在C#的ImportType方法中会尝试在缓存中获取对应的Type。
如果Type为空,那么说明是第一次尝试引用对应的Type,代码就会判断是时使用生成适配代码还是反射模式,来生成对应的表。
代码如下
在生成完Type对应的Lua表后还需要设置到Lua上去
下面的代码简单来说就是用前面代码生成的table表设置到CS.UnityEngine[Debug]中
现在可以看到在调用到CS.UnityEngine.Debug时候,我们在Lua中已经获取到了这个类对应Lua Table了。
那么接下来调用CS.UnityEngine.Debug.Log(“hello world”),大提上与之前的获取Type类是一致的。
不过要注意的是调用static的方法字段和对象的方法字段使用的是不同的table。这次文章都讨论的是静态方法的调用.
调用指定的C#方法
上面的已经提到XLua中有两种方式来实现Lua调用CS中的方法,一种是反射来调用,一种是生成适配的代码.
使用生成适配代码调用
在XLua中生成适配代码后会在Gen目录生成代码如下
使用反射式调用
那么到现在为止所有代码设置都已经完成,就差调用了。
当DoString到CS.UnityEngine.Debug.Log(“hello world”)时候,先从CS.UnityEngine.Debug[Log]获取到对应的value,
在lua中这个值是一个function,那么就执行call,压入参数然后就开始调用了。
如果是生成是适配代码的方式的话其对应的C#委托就是 m_Log_xlua_st(RealStatePtr L)了。
但是如果是反射式调用的话,其对应的C#委托永远都是StaticLuaCallbacks.FixCSFunctionWraper这个委托,就是上面代码的FixCSFunction。
当调用FixCSFunction后会从中取出upvalue,这个值是一个数字,是个索引。索引的是之前生成wrap时候缓存的方法。然后直接进行调用。
到此整一个调用就结束了.