Unity C#与Xlua的交互 C#调用Lua CSharpCallLua

8 篇文章 0 订阅

最近学习XLua,看了下官方的文档。对于我这样的小菜鸡而言需要知道的知识点还是蛮多的,就做个笔记,其实差不多等于把文档里面的知识ctrl C ctrl V一遍。正在准备学习这块的小伙伴们可以多看看官方给的文档。下面是自己的一个GitHub XLua Demo 地址。里面也存放了文档(XLuaDocs文件夹中)

这篇主要记录记录C#与XLua的相互交互,即C#如何访问调用Lua的属性方法和Lua如何调用C#的。对应的文档是《xLua教程》

注:本文一些XLua API和XLua标签等知识会在后面的文章慢慢整理,这边就不细说了。对应的官方文档里面大家也可以找到。

CSharpCallLua

CSharpCallLua,顾名思义CS里面去调用访问Lua代码里面的属性方法。对应的官方Demo为“CSharpCallLua”。


访问全局基本数据类型

假如我在Lua中定义了下面三个变量:

  1. a = 1  
  2. b = 'hello world'  
  3. c = true  

那么我们只需要通过luaenv.Global.Get<T>(name)就可在C#中访问这几个变量,如下:

[csharp] view plain copy
  1. LuaEnv luaenv = new LuaEnv();  
  2.   
  3. Debug.Log("_G.a = " + luaenv.Global.Get<int>("a"));  
  4. Debug.Log("_G.b = " + luaenv.Global.Get<string>("b"));  
  5. Debug.Log("_G.c = " + luaenv.Global.Get<bool>("c"));  

访问全局table

比如我在Lua中定义了如下的一个table:

  1. d = {  
  2.    f1 = 12, f2 = 34,   
  3.    1, 2, 3,  
  4.    add = function(self, a, b)   
  5.       print('d.add called')  
  6.       return a + b   
  7.    end  
  8. }  

方法1:将table映射到一个c#的class或struct中

我们可以定义一个class,将lua中的table里面的字段(即键值对的键)对应到class中的属性中,该属性需要添加public字段,并且该class需要有无参的构造函数。如上诉table d中f1 = 12, f2 = 34可以对应到class中如下:

[csharp] view plain copy
  1. public class DClass {  
  2.     public int f1;  
  3.     public int f2;  
  4. }  

注意,这些方式,table的属性不需要和class属性一一对应,并且可以嵌套其他复杂类型,上诉table中的1,2,3和add的属性无法映射到class中。然后利用下面的方法即可实例化这个class:

[csharp] view plain copy
  1. DClass d = luaenv.Global.Get<DClass>("d");//映射到有对应字段的class,by value  
  2. Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}");  

这种方式,Xlua会帮我们new一个实例,并将lua中table对应的字段的值赋值到class中。整个过程是值拷贝,如果class比较复杂代价会比较大,并且由于是值拷贝,修改class中属性的值不会同步到table当中,反之亦然。我们可以通过在class前添加[GCOptimize]标签,降低开销。


方法2:将table映射到interface中

这种方式依赖生成的代码(如果没有生成代码,会抛出InvalidCastException异常)。这种方式不仅可以访问table里面的属性,还可以访问table中的函数。是官方推荐用法,如下:

[csharp] view plain copy
  1. [CSharpCallLua]  
  2. public interface ItfD {  
  3.     int f1 { getset; }  
  4.     int f2 { getset; }  
  5.     int add(int a, int b);  
  6. }  

需要注意的是,这种方式需要给interface添加[CSharpCallLua]标签。映射方式和class相同,如下:

[csharp] view plain copy
  1. //映射到interface实例,by ref,这个要求interface加到生成列表即添加[CSharpCallLua]标签,否则会返回null,建议用法  
  2. ItfD d3 = luaenv.Global.Get<ItfD>("d");   
  3. d3.f2 = 1000;  
  4. Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}");  
  5. Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2));  

这种方式是引用类型,即当你get一个属性时,会从table中去取对应字段的值,set一个属性时,会去设置table中对应字段的值。还可通过interface中定义的方法去访问对应的table中的函数。


方法3:将table映射到Dictionary<>或List<>

这种方法不需要像上面自己定义class,struct或interface。这种方法是一种值拷贝,并且只会映射出同为一种类型的值。如下:

[csharp] view plain copy
  1. //映射到Dictionary<string, double>,by value  
  2. Dictionary<stringdouble> d1 = luaenv.Global.Get<Dictionary<stringdouble>>("d");  
  3. Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count);  
  4. //只能映射出table中键值对<string, double>的属性,即上述的f1=12,f2=34  
  5.   
  6. //映射到List<double>,by value  
  7. List<double> d2 = luaenv.Global.Get<List<double>>("d");   
  8. Debug.Log("_G.d.len = " + d2.Count);  
  9. //只能映射出table中类型为number的属性,即上述的1,2,3  

方法4:将table映射到LuaTable类

这种方法也是引用类型,LuaTable类是XLua里面的一个Class,同样不需要生成代码。但是效率较低,且没有类型检测,如下:

[csharp] view plain copy
  1. LuaTable d4 = luaenv.Global.Get<LuaTable>("d");//映射到LuaTable,by ref  
  2. Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}");  

访问全局function

例如我们在XLua里定义了如下三个函数:

  1. function e()  
  2.     print('i am e')  
  3. end  
  4.   
  5. function f(a, b)  
  6.     print('a', a, 'b', b)  
  7.     return 1, {f1 = 1024}  
  8. end  
  9.   
  10. function ret_e()  
  11.     print('ret_e called')  
  12.     return e  
  13. end  

e为无参无返回值函数,f为有参有返回值函数,ret_e为无参有返回值(返回一个函数)函数


方法1:映射到delegate

该方法是官方推荐方式,不仅性能好,而且类型安全,需要生成代码(否则抛出InvalidCastException异常)。像上面的函数e,我们可以直接映射到系统的delegate Action中,如:

[csharp] view plain copy
  1. Action e = luaenv.Global.Get<Action>("e");  
  2. e();  
  3.   
  4. //带参数的f函数映射到Action中,可以映射成功,但是函数体内参数值传递进来为nil  
  5. Action f = luaenv.Global.Get<Action>("f");  
  6. f();  

对于有参数有返回值(以及多返回值)的函数,我们需要自定义delegate并添加[CSharpCallLua]标签。lua函数里面的每个形参就要在delegate中声明一个对应的输入类型参数。我们可以看到f函数是具有两个返回值的(number和table),那么多返回值的情况,第一个返回值还是对应delegate的返回值,剩下的返回值从左到右对应到delegate的输出类型参数(out参数,ref参数)。所以f函数的映射如下:

[csharp] view plain copy
  1. //f函数返回的{f1=1024},可以对应之前映射的class DClass  
  2. [CSharpCallLua]  
  3. public delegate int FDelegate(int a, string b, out DClass c);  
使用:
[csharp] view plain copy
  1. FDelegate f = luaenv.Global.Get<FDelegate>("f");  
  2. DClass d_ret;  
  3. int f_ret = f(100, "John"out d_ret);//lua的多返回值映射:从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数  
  4. Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret);  

同时function还可以返回一个function,即对应C#的返回值为delegate,ret_e对应的映射可以为:

[csharp] view plain copy
  1. [CSharpCallLua]  
  2. public delegate Action GetE();  

使用如下:

[csharp] view plain copy
  1. GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更复杂的类型,甚至是另外一个delegate  
  2. Action e2 = ret_e();  
  3. e2();  
方法2:映射到LuaFunction

这种方式不需要生成代码,但是性能和安全性就不如前者。LuaFunction是XLua的一个class,有一个变参的Call函数,可以传任意类型,任意个数的参数。返回值是一个object数组,对应lua的多返回值,如下:

[csharp] view plain copy
  1. LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e");  
  2. d_e.Call();  


使用建议

1.访问Lua全局数据,特别是table和function,代价较大,建议尽量少做,比如在初始化时把要调用的lua table function获取一次(映射到delegate)后保存下来,后续直接调用对应delegate即可。

2.如果Lua部分的映射都以delegate和interface的方式处理,使用方法可以完全和XLua解耦:由一个专门的模块负责XLua的初始化以及delegate,interface的映射,然后把这些delegate,interface设置到要用到他们的地方。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值