XLua初识

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();

官方使用建议:

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

  2. 如果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)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值