XLua

安装

从**这里**下载一个zip压缩包,解压后会有Assets等几个文件夹,其他文件夹放进你所创建的unity工程的Assets同级的目录下,然后把加压出来的Assets里的文件和文件夹复制进unity项目的Assets文件夹里即可。

XLua教程

XLua加载文件

加载字符串

1.LuaEnv luaenv = new LuaEnv();  
2.luaenv.DoString("print('hello world')");  

第1行是用来实例LuaEnv虚拟机的,类似于用这个类来实现所有lua的操作,尽量定义为全局,这样优化
第2行是通过DoString函数来加载lua脚本,也可直接加载lua脚本内容,如上述代码,print(‘hello world’)就是lua的代码内容。加载就是把lua的脚本加载到luaenv虚拟机里。

加载lua文件

LuaEnv luaenv = new LuaEnv();
//方法一
public TextAsset lua_001;
//方法二
private TextAsset lua_002;
//方法三
//在Start函数里luaenv.DoString("require 'LuaText/lua_003'");
void Start()
{
    lua_002 = Resources.Load<TextAsset>("LuaText/lua_002.lua");
    luaenv.DoString(lua_001.text);
    luaenv.DoString(lua_002.text);
    luaenv.DoString("require 'LuaText/lua_003'");
}

void Update()
{

}
void OnDestroy()
{
    luaenv.Dispose();
}

lua脚本在放在Resources文件夹的时候,后缀必须加上.txt,例如lua_001.lua.txt,因为Resources对支持的文件类型有限,也就是无法用Resouces读取.lua文件。

方法一是在脚本所挂物体上,通过拖拽赋值变量的方法,直接把lua的脚本拖拽到脚本的变量上,然后通过DoString执行lua文件
方法二是先定义TextAsset变量,然后在初始化的时候读取Resources里的文件,然后赋值到变量上,然后通过DoString执行lua文件。
方法三此处主要是对require和loader的理解,可以吧loader看成是指向某个路径里的一个个文件,require实际上就是调用一个个loader去加载,和自己所输入文件名字匹配,有一个成功,就不再往下进行查找,全部失败则报找不到文件的错误,目前xLua除了包含自己原本的loader外还包含了Resource所加载的loader,也就是require也可直接读取Resources目录下的文件,类似Resources的读取,但是因为Resource支持的文件类型有限,所以如果lua脚本放进了Resources文件里,就必须在后面加一个.txt的后缀,就像lua_001.lua.txt
建议在整个程序中加载脚本只用一个DoString(“require’main’”),然后在main.lua脚本里可以加载自己所需要的其他脚本,其实就只把main.lua脚本看成一个接口脚本,然后在这个脚本里加载其他的lua脚本。

自定义Loader

LuaEnv luaenv = null;
void Start () {
    luaenv = new LuaEnv();
    LuaEnv.CustomLoader loader = CustomLoaderMethod;
    luaenv.AddLoader(loader);
    luaenv.DoString("require('main')");
}



// Update is called once per frame
void Update () {
    
}
private byte[] CustomLoaderMethod(ref string filepath)
{
    if(filepath== "main")
    {
        TextAsset lua_003 = Resources.Load<TextAsset>("LuaText/lua_003.lua");
        return System.Text.Encoding.UTF8.GetBytes(lua_003.text);
    }
    return null;
}

有些脚本可能是程序运行下载下来的,或者是自定义的文件格式里头解压出来的,或者是需要解密的,所以存在文件不在指定的目录里,或者不是规定的lua脚本或者lua.txt格式,这样可有使用自定的loader,就类似于上面脚本,假如loader只包含原生的loader,这样就无法读取Resources里的文件夹,这样可以自定义loader,然后根据所要查找的lua脚本名字,指定到对应所在的地址,然后读取到需要的脚本,把文件里的内容一byte[]的形式回调回来即可,这样require(“脚本名字”)就可以直接执行脚本,不论脚本在什么位置,只要脚本所在的位置已经增加到loader上即可,即用LuaEnv.AddLoader(自己定义的loader),定义loader可以用LuaEnv.CustomLoader loader名字=CustomLoaderMethod来定义loader,用private byte[] CustomLoaderMethod(ref string filepath)函数来实现自己定义的loader.
通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。 有了这个就简单了,用IIPS的IFS?没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密?没问题,自己写loader读取文件解密后返回即可。。。(此处的IIPS是什么可以不做了解

C#访问Lua

访问全局基本类型

lua脚本

a = 10  
b = 100  
c = "LianBai"

c#脚本

LuaEnv luaenv = null;
private TextAsset luatext;
void Start () 
{
    luaenv = new LuaEnv();
    luatext = Resources.Load<TextAsset>("LuaText/lua_004.lua");
    luaenv.DoString(luatext.text);
    int a = luaenv.Global.Get<int>("a");
    string b = luaenv.Global.Get<string>("b");
    string c = luaenv.Global.Get<string>("c");
    print("a = "+a);
    print("b = " + b);
    print("c = " + c);
}

C#访问Lua脚本里的变量其实很简单,只需要先用DoString加载脚本,然后在通过luaenv.Global.Get<变量类型>(“变量名”);即可读取自己所需要的基础变量。

访问一个全局的table

lua脚本

d ={
    x1=2,
    x2=3,
    x3="LianBai",
    4,
    5,
    6,
    add = function(self,a,b)
        return a+b
    end
}

映射到一个普通的class

C#脚本

public class Table1
{
    public int x2;
    public int x1;
    public string x3;
}
Table1 table1 = luaenv.Global.Get<Table1>("d");

可以自己测试输出table1的结果,根据结果可以看出lua的table映射到C#的class的时候,并不是按照顺序一一对应进行映射的,而是根据名字映射,lua的table中的x1变量只会映射到C#的class中的x1变量中,如果lua的table里没有class中所包含的变量,则会在class中赋值为空,如果lua中table变量多余class,多余的也不会赋值过来,只会把class所包含的赋值过来。

映射到一个Dictionary中

C#脚本

Dictionary<string,double> table2= luaenv.Global.Get<Dictionary<string,double>>("d");
print("table1.x1:" + table2["x1"]);
print("table1.x2:" + table2["x2"]);

在映射到Dictionary的时候,只会映射符合Dictionary自己定义的规则的变量,把变量名存为keys,把变量值存为对应的数据,所以不会把x3等后面的数据存到Dictionary里。

映射到一个List里

C#脚本

List<double> table3= luaenv.Global.Get<List<double>>("d");
print("List的Count" + table3.Count);
foreach(double i in table3)
{
    print("i:" + i);
}

在映射到List中,也只会映射到符合List规则的变量,但是有一点要注意的是,table中带keys的不会映射进List,不管储存的变量是不是符合规则,都不会映射进去,所以在上述脚本打印的时候,table3存的值是lua脚本中table中的4,5,6,三个变量。

映射到一个interface

C#脚本

[CSharpCallLua]
public interface ITable4
{
    int x1 { get; set; }
    int x2 { get; set; }
    string x3 { get; set; }
    int add(int a, int b);
}

ITable4 table4 = luaenv.Global.Get<ITable4>("d");
print("table4 x1 = " + table4.x1);
print("table4 x2 = " + table4.x2);
print("table4 x3 = " + table4.x3);
print("table4 add = " + table4.add(5, 6));

什么是interface?**点击这里**。理解清楚interface后,从代码里我们可以看出接口类多了一个[CSharpCallLua],这是在为这个类打上了一个标签,因为与LUA交互的代理和类型需要添加 CSharpCallLua 标签,xlua的特性之一就是标记,这一次与lua交互的特性标记则是[CSharpCallLua],只有这样写,才可支持热更新。然后接口类的继承和普通类类似,也是根据变量名字一一对应的关系。

映射到一个LuaTable变量

C#脚本

LuaTable table5= luaenv.Global.Get<LuaTable>("d");
print("table5长度:" + table5.Length);
print("table5.x1:" + table5.Get<int>("x1"));
print("table5.x2:" + table5.Get<int>("x2"));
print("table5.x3:" + table5.Get<string>("x3"));

这是一种by ref方式,所以不会生成代码,但是这种方式比较慢,并且没有变量检测。

访问一个funaction

Lua脚本

function e()
{
    print("lua脚本的函数")
}
function f(a, b)
    print('a', a, 'b', b)
    return 1, 2, {x1 = 1024,x2 = 2048}
end
function ret_e()
    print("LuaScripts return e function")
    return e
end

映射到一个委托(Action或者delgate)

C#脚本

public static class AddGenerateList
{
     [CSharpCallLua]
       public static List<Type> CSharpCallLua = new List<Type>()
    {
        typeof(Action),
        typeof(Action<bool>),
    };
}

Action function1 = luaenv.Global.Get<Action>("e");
function1();

在把函数映射到Action的时候,一定要把Action添加到生成列表,脚本中第一个静态的AddGenerateList类就是把Action增加到生成列表里。然后才可把函数映射到Action中。否则将会报错。

lua脚本多返回值映射

C#脚本

[CSharpCallLua]
public delegate int Function2(int a, string b, out int c, out Table1 d);

Function2 function2 = luaenv.Global.Get<Function2>("f");
Table1 table1;
int fun2_02;
int fun2_01 = function2(66, "LianBai", out fun2_02, out table1);
print("fun2_01:" + fun2_01);
print("fun2_02:" + fun2_02);
print("table1.x1:" + table1.x1);
print("table1.x2:" + table1.x2);

lua脚本里的函数支持多个返回值,而且返回值的类型也不同,在C#访问lua函数的时候,可以通过上述方法接收lua函数的多个返回值,第一个返回值直接接收映射过来函数的返回值,正如上述脚本里的fun2_01就是第一个返回值,然后后面的返回值均可用out接收,不过映射前声明的时候要用上述的声明方法:
[CSharpCallLua]
public delegate 第一个返回值类型 Function(参数1,参数2,…, out 第二个返回值,out 第三个返回值,…)

映射lua返回复杂类型的函数

C#脚本

[CSharpCallLua]
public delegate Action Function3();

Function3 function3 = luaenv.Global.Get<Function3>("ret_e");
print("第一步:");
function3();
print("第二步:");
Action function3_01 = function3();
function3_01();

在C#访问lua的函数返回类型是复杂类型的时候,可以用委托接收,例如上述脚本,访问的函数的返回值是一个函数,在访问的时候,同样用委托接收,不过是两层委托,例如Function3的定义,function3所访问的就是访问对应的函数,然后用一个委托接收function3的返回值,然后就可以访问所返回的函数。

使用建议

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

2.如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。

Lua访问C#脚本

lua创建一个C#的对象

Lua脚本

local CShapObject = CS.UnityEngine.GameObject("LianBai")

通过上述方法可以用lua脚本创建一个新的gameobject,后面跟的字符串就是物体的名字,所以创建新物体的lua脚本就是**local lua函数名 = CS.对象(class)命名空间.C#对象(class)名(参数)**。

lua访问C#的静态属性、静态方法

C#脚本

LuaEnv luaenv = null;
private TextAsset luatext;
namespace MySpace
{
    [LuaCallCSharp]
    public static class CShapStaticClass
    {
        public static int a = 10;
        public static string b = "LianBai";
    }
}

luaenv = new LuaEnv();
luatext = Resources.Load<TextAsset>("LuaText/lua_005.lua"); 
luaenv.DoString(luatext.text);
print(CShapStaticClass.a);

Lua脚本

local CShapObject = CS.UnityEngine.GameObject("LianBai")
local a=CS.MySpace.CShapStaticClass.a
local b=CS.MySpace.CShapStaticClass.b
CS.MySpace.CShapStaticClass.a=66
print(a);
print(b);

lua在访问c#的静态变量时与创建对象类似,CS.命名空间.静态类.静态变量
如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能。

lua访问C#成员属性、方法

lua访问普通的类

C#脚本

[LuaCallCSharp]
public class CShapClass02
{
    public int c = 99;
    public void Class02Fun01()
    {
        Debug.Log("This is CShapClass01's function d");
    }
}

lua脚本

local LuaClass02 = CS.MySpace.CShapClass02
local luaclass02 = LuaClass02()
print(luaclass02.c)
luaclass02.c = 88
luaclass02:Class02Fun01()

lua在访问C#的成员属性和方法的时候,先用变量映射出C#的类,然后再用一个变量接收。local 变量1 = CS.类所在命名空间.类名,变量1是映射过来的对象的声明,需要自己定义一个对象,local 变量2 = 变量1(),即变量2就是所映射过来的类的实例,通过变量2.变量名就可访问变量,通过**变量2: 函数名()**就可启用C#的函数。

lua访问继承的类

C#脚本

[LuaCallCSharp]
public class CShapClass01
{
    public static int a = 1;
    public static string b = "LianBai";
    public static void Class01Fun01()
    {
        Debug.Log("This is Class01Fun01");
    }
    public void Class01Fun02()
    {
        Debug.Log("This is Class01Fun02");
    }
}
[LuaCallCSharp]
public class CShapClass02: CShapClass01
{
    public int c = 99;
    public void Class02Fun01()
    {
        Debug.Log("This is CShapClass01's function d");
    }
}

lua脚本

local LuaClass02 = CS.MySpace.CShapClass02
local luaclass02 = LuaClass02()
print(luaclass02.c)
print(LuaClass02.a)
print(LuaClass02.b)
luaclass02.c = 88
luaclass02:Class02Fun01()
LuaClass02.Class01Fun01()
luaclass02:Class01Fun02()

lua在访问C#的含有继承类的类的时候,访问基类普通变量和访问子类一样,用变量2.变量名,和变量2:函数名来访问,但是在访问基类的静态变量的时候,通过变量1.变量名来访问基类静态变量,通过变量1:函数名来访问基类静态函数。xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法。

访问复杂函数

C#脚本

[LuaCallCSharp]
public class CShapClass02
{
    public int c = 99;
    public void Class02Fun01()
    {
        Debug.Log("This is CShapClass01's function d");
    }
    public double Class02Fun02(CShapClass01 cshapclass01,ref int x1,out string x2,Action luafun,out Action csfun)
    {
        Debug.Log(cshapclass01.c + " : " + cshapclass01.d);
        luafun();
        x1 = x1 * cshapclass01.c;
        x2 = "CShapClass01.d = " + cshapclass01.d;
        csfun = () =>
        {
            Debug.Log("This is CsFun");
        };
        Debug.Log(x1 + " : " + x2);
        return 6.66;
    }
}

Lua脚本

local LuaClass02 = CS.MySpace.CShapClass02
local luaclass02 = LuaClass02()
function LuaFun()
    print("This is LuaFun")
end
local cshapclass01,x1,x2,csfun = luaclass02:Class02Fun02({c = 100,d = "LianHei"},88,LuaFun)
print("cshapclass01:",cshapclass01)
print("x1:", x1)
print("x2:", x2)
print("csfun:", csfun)
csfun();

参数的输入输出属性(out,ref)
Lua调用测的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用测的实参列表;
Lua调用测的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。

上述话的意思是lua在映射复杂的函数的时候,C#函数中的参数中有普通参数,ref定义的参数,out定义的参数,例如上述的Class02Fun02函数中CShapClass01 cshapclass01是普通的参数,就是我们正常定义的参数,ref int x1out string x2分别是ref和out定义的参数,在lua调用的时候,luaclass02:Class02Fun02({c = 100,d = “LianHei”},88,LuaFun),第一个表{c = 100,d = “LianHei”}是给cshapclass01传参数,88传给的是x1,LuaFun是传给的,Action luafun,因为x2和csfun是out形容的参数,所以不用传递。

ref定义的参数,在调用的时候要传参数,在返回的时候也要接收,out定义的参数在调用的时候不用传递参数,但是在接收的时候要接收参数
在调用这个函数的时候会执行C#脚本里面的函数,然后函数会有返回值,如果函数本身就有返回值,则lua脚本在接收的第一个返回值就是函数的返回值,然后在C#函数中有些参数是ref或者out参数,这些参数从左往右依次都是函数的返回值,所以在C#函数里必须为这些参数赋值。

访问重载方法

C#脚本

[LuaCallCSharp]
public class CShapClass03
{
    public void CShapFun()
    {
        Debug.Log("This is NULL");
    }
    public void CShapFun(int a)
    {
        Debug.Log("This is int");
    }
    public void CShapFun(string a)
    {
        Debug.Log("This is string");
    }
}

Lua脚本

local LuaClass03 = CS.MySpace.CShapClass03
local luaclass03 = LuaClass03()
luaclass03:CShapFun()
luaclass03:CShapFun(2)
luaclass03:CShapFun("LianBai")

在C#里运行脚本可以看出,在访问重载函数的时候,只要改变穿进去的参数,就会自动调用重载对应的函数。
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)

调用操作符

C#脚本

[LuaCallCSharp]
public class CShapClass03
{
    public int a = 66;
    public string b = "LianBai";
    public static CShapClass03 operator + (CShapClass03 class1, CShapClass03 class2)
    {
        CShapClass03 ret = new CShapClass03();
        ret.a = class1.a + class2.a;
        ret.b = class1.b + class2.b;
        return ret;
    }
}

Lua脚本

local LuaClass03 = CS.MySpace.CShapClass03
local luaclass03 = LuaClass03()
local luaclass04 = LuaClass03()
print(luaclass03.a)
print(luaclass04.b)
luaclass04.a = 88
print(luaclass04.a)
print("This ",(luaclass03 + luaclass04).a)
luaclass03.b="I am "
print((luaclass03+luaclass04).b)

在lua使用所访问的对象的操作符的时候,在对象内部必须重载此运算符,这样才可以使用此运算符,C#的运算符可以自己百度查询。然后在类里面即可像C#里面一样调用,允许操作的运算符有:
+,-,*,/,==,一元-,<,<=, %,[]

访问含默认值方法

C#脚本

public void CShapClass03DefaultFun(int a = 66,string b = "LianBai",string c = null)
{
    Debug.Log("a = " + a + " b = " + b + " c = " + c);
}

Lua脚本

local LuaClass03 = CS.MySpace.CShapClass03
local luaclass03 = LuaClass03()
luaclass03:CShapClass03DefaultFun()
luaclass03:CShapClass03DefaultFun(88)
luaclass03:CShapClass03DefaultFun(88,"LianHei")
luaclass03:CShapClass03DefaultFun(88,"LianHei","COOL")

在访问含有默认值的方法时,在lua里调用函数和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。

访问可变参数方法

C#脚本

[LuaCallCSharp]
public class CShapClass03
{
    public int a = 66;
    public string b = "LianBai";
    public void CShapClassVariableFun(int a,params string[] str)
    {
        string s = null;
        foreach(string c in str)
        {
            s = s + c +" ";
        }
        Debug.Log("a = " + a + "  str = " + s);
    }
}

Lua脚本

local LuaClass03 = CS.MySpace.CShapClass03
local luaclass03 = LuaClass03()
luaclass03:CShapClassVariableFun(10,"I")
luaclass03:CShapClassVariableFun(20,"I","am")
luaclass03:CShapClassVariableFun(30,"I","am","LianBai")

lua在访问的方法中含有可变参数(params)的时候,也可以类似于在C#脚本的使用方法传进去参数。

映射枚举并访问枚举

C#脚本

[LuaCallCSharp]
public enum MyEnum
{
    x1 = 66,
    x2 = 88,
    x3 = 99
}
[LuaCallCSharp]
public class CShapClass04
{
    public MyEnum CShapEnumFun(MyEnum e)
    {
        Debug.Log("MyEnum:" + e);
        return e;
    }
}

Lua脚本

local LuaEnum = CS.MySpace.MyEnum
print(LuaEnum.__CastFrom("x1"),LuaEnum.__CastFrom("x2"),LuaEnum.__CastFrom("x3"))
local LuaClass04 = CS.MySpace.CShapClass04
local luaclass04 = LuaClass04()
local luaenum = luaclass04:CShapEnumFun(CS.MySpace.MyEnum.x1)
print(luaenum,luaenum == LuaEnum.x1)
print(LuaEnum.__CastFrom(0),LuaEnum.__CastFrom(1),LuaEnum.__CastFrom(2))

在映射枚举的时候,直接像类一样直接声明枚举对象就可调用,方法是:local 变量1 = CS.枚举所在命名空间.枚举名,然后就可以通过变量1.key值就可访问枚举中的对象。
在映射类里面含有枚举参数的时候,可以直接通过**CS.枚举所在命名空间.枚举名.key值”直接传进key对应的值,也可直接通过映射过来的变量1,通过变量1.key值”访问对应的key值。
如果枚举类加入到生成代码的话,枚举类将支持
__CastFrom()**方法,可以实现从一个整数或者字符串到枚举值的转换。(注:CastFrom前面是两个英文的下划线)

Lua访问C#委托

C#脚本

[LuaCallCSharp]
public class CShapClass04
{
    public MyEnum CShapEnumFun(MyEnum e)
    {
        Debug.Log("MyEnum:" + e);
        return e;
    }
    public static Action<string> CShapStaticAction;
    public Action<string> CShapAction = (param) =>
    {
        Debug.Log("This is My CShapAction:" + param);
    };
public delegate void CShapDelegate(string obj);
}
[LuaCallCSharp]
public class CShapClass05
{
    public CShapClass04.CShapDelegate MyClassFun = null;
    public CShapClass05()
    {
        MyClassFun = new CShapClass04.CShapDelegate(MyAction) ;
    }
    private void MyAction(string obj)
    {
        Debug.Log("This is My CShapDelegates:" + obj);
    }
}

MySpace.CShapClass04.CShapStaticAction += MyStaticAction;

Lua脚本

local LuaClass04 = CS.MySpace.CShapClass04
local luaclass04 = LuaClass04()
local LuaClass05 = CS.MySpace.CShapClass05
local luaclass05 = LuaClass05()
LuaClass04.CShapStaticAction("LianBai")
luaclass04.CShapAction("LianBai")
local MyLuaClass = luaclass05.MyClassFun
上MyLuaClass("LianBai")
MyLuaClass("LianBai")
local function LuaAction(str)
    print("This is LuaAction :",str)
end
local luaaction = LuaAction + LuaClass04.CShapStaticAction
luaaction("+")
local luaaction = LuaClass04.CShapStaticAction - LuaAction
luaaction("-")
local luaaction = LuaClass04.CShapStaticAction + LuaAction
luaaction("+2")
local luaaction = LuaClass04.CShapStaticAction - LuaAction
luaaction("-2")

C#的委托关键字是delegate,Action和Func都是别人封装好的委托,通过上述方法可以观测到,delegate在定义委托的时候,默认就是静态的方法,但是Action定义委托的时候需要手动添加关键字,如果没有添加关键字,只能通过对象调用,但是无法传递参数,因为delegate本身是静态的,所以导致无法使用,关于delegate和Action、Func的区别请**点击这里** 、
通过上述脚本,测试可发现,lua在访问委托的时候,和调用普通的函数方法一样。
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
+操作符左右任意一个是delegate就可以,但是-操作符delegate必须放在左边
Ps:delegate属性可以用 一个luafunction来赋值。

Lua访问类的事件(Event)

C#脚本

[LuaCallCSharp]
public class DelegateClass
{
    public delegate void MyDelegate();  //定义一个委托
    public event MyDelegate myevent;    //定义了一个事件
    public void ClassFun01()
    {
        myevent();
    }
}

Lua脚本

local LuaClass = CS.MySpace01.DelegateClass
local luaclass = LuaClass()
local function luafun01()
    print("This is luafun01")
end
local function luafun02()
    print("This is luafun02")
end
luaclass:myevent("+",luafun01)
luaclass:ClassFun01()
luaclass:myevent("+",luafun02)
luaclass:ClassFun01()
luaclass:myevent("-",luafun01)
luaclass:ClassFun01()

Lua在访问Event的时候,添加事件回调的方法是**类对象:委托名(“+”,函数名),同样的,在移除事件回调的时候是类对象:委托名(“-“,函数名)**。

Lua访问64位整数

C#脚本

public ulong LongFun(long l)
{
    return (ulong)l + 1;
}

Lua脚本

local LuaClass = CS.MySpace01.DelegateClass
local luaclass = LuaClass()
local a = luaclass:LongFun(11)
print(type(a),a+10,a+100,a+1000,a+10000)

Lua53版本64位整数(long,ulong)映射到原生的64未整数,而luaji版本t,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata:
支持在lua里头进行64位的运算,比较,打印
支持和lua number的运算,比较
要注意的是,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算,比较,我们采取和java一样的支持方式,提供一组API,详情请看API文档。

C#复杂类型和table的转换

C#脚本

[LuaCallCSharp]
public class MyClass01
{
    public int a;
    public void Show()
    {
        Debug.Log("This is MyClass01's a:" + a);
    }
}
[LuaCallCSharp]
public class MyClass02
{
    public MyClass01 a;
    public string b;
    public void Show()
    {
        a.Show();
        Debug.Log("This is MyClass02's b:" + a);
    }

}
[LuaCallCSharp]
public class MyClass03
{
    public void Set(MyClass02 my)
    {
        my.Show();
    }
}

Lua脚本

local LuaClass = CS.MySpace01.MyClass03
local luaclass = LuaClass()
luaclass:Set({a={a=66},b="LianBai"})

Lua在访问复杂的Class参数时,一个{}就是一个类,如果想用上述方法,变量必须public,这样lua才能根据变量名进行赋值,就像一开头讲的,根据变量名字进行赋值。

Lua获取C#的类型(相当于C#的typeof)

Lua脚本
print(typeof(CS.MySpace01))

要获取CS.MySpace01类的Type信息,可以用上述脚本

Lua强转数据

C#脚本

[LuaCallCSharp]
public interface MyClass04
{
    void  Show(int a, string b);
}
[LuaCallCSharp]
public class MyClass05 : MyClass04
{
    public int ID = 88;
    public void Show(int a, string b)
    {
        Debug.Log(a);
        Debug.Log(b);
    }
    public MyClass04 GetClass04()
    {
        return new MyClass05();
    }
}

Lua脚本

local LuaClass01 = CS.MySpace01.MyClass05 
local luaclass01 = LuaClass01()
local luainterface01 = luaclass01:GetClass04()
luainterface01:Show(66,"LianBai")
assert(luainterface01.ID == 88)
--assert(luainterface01.ID == nil)

local LuaClass02 = CS.MySpace01.MyClass05 
local luaclass02 = LuaClass02()
local luainterface02 = luaclass02:GetClass04()
cast(luainterface02,typeof(CS.MySpace01.MyClass04))
luainterface02:Show(88,"LianHei")
--assert(luainterface02.ID == 88)
assert(luainterface02.ID == nil)

什么是assert
通过上代码演示和输出,我们不难看出第一种访问和第二种访问的区别,如果我们把lua里面注释掉的两个代码解放开,就会抛出异常,我们在访问interface或者抽象的时候,我们的实现类都是隐藏的,所以,在访问的时候是进行映射到C#脚本里,其实还是在C#脚本里运行,这就是映射过去,所以我们第一种访问,依旧可以访问到ID,因为C#里有ID这个变量,其实luainterface只是C#的类的映射,本质还是C#的类,然后我们在第二种方法中,只是添加了一个**cast(luainterface02,typeof(CS.MySpace01.MyClass04))**这样我们就告诉lua,把这个接口加到生成代码里,所以我们就是把接口类放进了lua文件里,只是我们看不见而已,这样我们的接口就不存在ID这个变量了。但是方法依然存在。
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用生成代码来访问。

XLua配置


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 841774407@qq.com

×

喜欢就点赞,疼爱就打赏