|
Xlua简介
xLua是由腾讯维护的一个开源项目,xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能、易用性、扩展性而广受好评。现在,腾讯已经将xLua开源到GitHub。其git地址是:https://github.com/Tencent/xLua。
lua文件加载
- void Start()
- {
- //new 一个lua解释器
- LuaEnv luaenv = new LuaEnv();
- //1.调用dostring方法,执行字符串
- luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
- //2.加载lua脚本
- luaenv.DoString("require('Main')");
- //3.通过AddLoader添加回调,可以自定义加载lua文件路径,返回byte数组
- luaenv.AddLoader(CustomLoader);
- luaenv.Dispose();
- }
- /// <summary>
- /// 自定义加载lua脚本
- /// </summary>
- /// <param name="filepath"></param>
- /// <returns></returns>
- public byte[] CustomLoader(ref string filepath)
- {
- //自定义加载路径
- //加载lua文件
- //读取字节返回
- }
复制代码 C#访问Lua
1.获取全局数据结构类型。访问LuaEnv.Global即可。 Global定义了一个lua全局环境_G- luaenv.Global.Get<int>("a")
- luaenv.Global.Get<string>("b")
- luaenv.Global.Get<bool>("c")
复制代码 2.访问一个全局Table
方式一:映射到普通class或struct- string script = @"
- d = {
- f1 = 12, f2 = 34,
- 1, 2, 3,
- add = function(self, a, b)
- print('d.add called')
- return a + b
- end
- }
- ";
- public class DClass {
- public int f1;
- public int f2;
- }
- DClass d = luaenv.Global.Get<DClass>("d");//映射到有对应字段的class,by value
- Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}");
- //定义一个Class,有对应的table的字段public属性,而且有无参数构造函数即可。这种方式下xlua会帮你new一个实例,并把对应的字段赋值过去
- //table的属性可以多于或少于class属性。可以嵌套其他复杂类型。要注意的是,这个过程是值拷贝,如果class比较复代价会比较大,而且修改class字段的值不会同步到table,反过来也不会
复制代码 方式二:映射到interface
- [CSharpCallLua]//需要加[CSharpCallLua]特性
- public interface ItfD
- {
- int f1 { get; set; }
- int f2 { get; set; }
- int add(int a, int b);
- }
- ItfD d3 = luaenv.Global.Get<ItfD>("d"); //映射到interface实例,by ref,这个要求interface加到生成列表,否则会返回null,建议用法
- d3.f2 = 1000;
- Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}");
- Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2));
- //这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
复制代码 方式三:更轻量级的by value方式:映射到Dictionary<> , list<> 不想定义class或者interface的话,可以考虑这个,前提是table下key和value类型都是一致的。- string script = @"
- d = {
- f1 = 12, f2 = 34,
- 1, 2, 3,
- add = function(self, a, b)
- print('d.add called')
- return a + b
- end
- }
- ";
- Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");//映射到Dictionary<string, double>,by value
- Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count);
- List<double> d2 = luaenv.Global.Get<List<double>>("d"); //映射到List<double>,by value
- Debug.Log("_G.d.len = " + d2.Count);
复制代码 2.4 另外一种by ref方式:映射到LuaTable类。这种方式好处是不需要生成代码,但也有一下问题,比如慢,没有类型检查- LuaTable d4 = luaenv.Global.Get<LuaTable>("d");//映射到LuaTable,by ref
- Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}");
复制代码 3.访问一个全局function
任然使用Get方法,不同的是类型映射
方式一:映射到delegate。这种事建议的方法,性能好很多。而且类型安全。缺点是要生成代码。
delegate要怎样声明呢? 对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了。- string script = @"
- function e()
- print('i am e')
- end
- function f(a, b)
- print('a', a, 'b', b)
- return 1, {f1 = 1024}
- end
- function ret_e()
- print('ret_e called')
- return e
- end";
- public class DClass
- {
- public int f1;
- public int f2;
- }
- [CSharpCallLua]
- public delegate int FDelegate(int a, string b, out DClass c);
- [CSharpCallLua]
- public delegate Action GetE();
- Action e = luaenv.Global.Get<Action>("e");//映射到一个delgate,要求delegate加到生成列表,否则返回null,建议用法
- e();
- FDelegate f = luaenv.Global.Get<FDelegate>("f");
- DClass d_ret;
- int f_ret = f(100, "John", out d_ret);//lua的多返回值映射:从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数
- Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret);
- GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更复杂的类型,甚至是另外一个delegate
- e = ret_e();
- e();
复制代码 方式二:映射到LuaFunction。这种方式优缺点和方式一相反。使用简单,LuaFunction上有个变参Call函数,可以传入任意类型,任意个数的参数,返回值是object数组,对应于lua的多返回值- LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e");
- d_e.Call();
复制代码 官方使用建议:
访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
Lua调用C#
new C# 对象
- --lua脚本代码实例
- local newGameObj = CS.UnityEngine.GameObject()
- local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
- print(newGameObj, newGameObj2)
- ---------------
- 基本类似,除了:
- 1. lua里头没有new关键字;
- 2. 所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
- 如果有多个构造函数呢?放心,xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:
- local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
复制代码 访问C#静态属性,方法
- --读静态属性
- Cs.UnityEngine.Time.deltaTime
- --写静态属性
- Cs.UnityEngine.Time.timeScale = 0.5
- --调用静态方法
- CS.UnityEngine.GameObject.Find('helloworld')
- 小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
- local GameObject = CS.UnityEngine.GameObject
- GameObject.Find('helloworld')
- --建议_G内设置全局变量
复制代码 访问C#成员属性, 方法
- --读成员属性
- testobj.DMF
- --写成员属性
- testobj.DMF = 1024
- --调用成员方法
- --注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下
- testobj:DMFunc()
复制代码 父类属性,方法。
- xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法
复制代码 参数的输入输出属性(out,ref)
- Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用侧的实参列表;
- Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
复制代码 重载方法
- --直接通过不同的参数类型进行重载函数的访问,例如:
- testobj:TestFunc(100)
- testobj:TestFunc('hello')
- --注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)
复制代码 操作符
- 支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]
复制代码 参数带默认值的方法
- 和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
复制代码 可变参数方法: 实例: params string[] str
- //对于C#的如下方法:
- void VariableParamsFunc(int a, params string[] strs)
- --可以在lua里头这样调用:
- testobj:VariableParamsFunc(5, 'hello', 'john')
复制代码 使用Extension methods (扩展方法),在C#里定义了,lua里就能直接使用。
Extension Methods - C# Programming Guide | Microsoft Docs
枚举类型
- 枚举值就像枚举类型下的静态属性一样。
- //C#
- [LuaCallCSharp]
- public enum TestEnum
- {
- E1,
- E2
- }
- -- lua
- local test = CS.TestEnum.E1
- 枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
- CS.TestEnum.__CastFrom(1)
- CS.TestEnum.__CastFrom('E1')
复制代码 delegate使用(调用,+,-)
C#的delegate调用:和调用普通lua函数一样
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
Ps:delegate属性可以用一个luafunction来赋值。
event
比如testobj里头有个事件定义是这样:public event Action TestEvent;
增加事件回调- testobj:TestEvent('+', lua_event_callback)
复制代码 移除事件回调- testobj:TestEvent('-', lua_event_callback)
复制代码 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|