XLua-操作与使用

C#源代码->dll->安装包
Lua源代码->安装包

Lua源代码可以在客户端直接下载没有编译的过程

1.直接用Lua调用C#的方法
使用XLua输出HelloWorld
引入命名空间XLua,声明一个LuaEnv的类的对象,这个类提供了一些方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
public class HellowWorld01 : MonoBehaviour {
    private LuaEnv luaenv;
    void Start () {
        luaenv = new LuaEnv();
        luaenv.DoString("print('hello world')");
        //luaenv.DoString("CS.UnityEngine.Debug.Log('hello kk')");
    }
    private void OnDestroy()
    {
        luaenv.Dispose();
    }
}

输出结果:

LUA: hello world

2.通过读取Lua文件执行Lua语句

//加载helloWorld.lua.txt,TextAsset会自动加txt的后缀
        TextAsset ta = Resources.Load("helloWorld.lua");
        //print(ta);
        LuaEnv luaEnv = new LuaEnv();
        luaEnv.DoString(ta.text);
        luaEnv.Dispose();

3.自定义Loader
在xLua加自定义loader是很简单的,只涉及到一个接口:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)

通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。

public class CreateLoader : MonoBehaviour {
    private LuaEnv luaEnv;
    // Use this for initialization
    void Awake () {
        luaEnv = new LuaEnv();
        luaEnv.AddLoader(MyLoader);
        luaEnv.DoString("require 'helloWorld.lua'");
        luaEnv.Dispose();
    }

    private byte[] MyLoader(ref string filePath)
    {
        string s = "print('best kk')";
        return System.Text.Encoding.UTF8.GetBytes(s);
    }
}

4.读取非Resources文件夹下的lua文件通过自定义Loader

public class CreateLoader : MonoBehaviour {
    private LuaEnv luaEnv;
    void Awake () {
        luaEnv = new LuaEnv();
        luaEnv.AddLoader(MyLoader);
        luaEnv.DoString("require 'test007'");
        luaEnv.Dispose();
    }
    private byte[] MyLoader(ref string filePath)
    {
        print(filePath);
        print(Application.streamingAssetsPath);
        string path = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";
        return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
    }
}

C#访问Lua

一.获取Lua中的全局变量

int a = luaEnv.Global.Get("a");
        print(a);

二.访问一个全局的Table
在C#中定义一个类与Lua中的Table相对应

 class Person
    {
        public string name;
        public int age;
    }

在用这个类创建对象来进行接收

Person kk = luaEnv.Global.Get("person");
        print(kk.name + " " + kk.age);

2.使用接口访问Table
声明一个接口,添加CSharpCallLua的特性

    [CSharpCallLua]
    interface IPerson
    {
        string name { get; set; }
        int age { get; set; }
    }

使用接口将获取Lua文件中的引用,这与通过声明类不同,修改实例化的接口将直接修改Lua中的数据。
在接口中声明方法将直接对应Lua中Table中的方法。

person = {
    name = "kk";
    age = 23;
    eat = function()
        print("111");
    end
}
    [CSharpCallLua]
    interface IPerson
    {
        string name { get; set; }
        int age { get; set; }
        void eat();
    }

3.使用轻量级的映射方式字典或者集合
将Lua中Table的键值对存在Dictionary中,存在限制,没有明确的键的无法存在Dictionary中。

        Dictionary dic = luaEnv.Global.Get>("person");
        foreach (string item in dic.Keys)
        {
            print(item + " " + dic[item]);
        }

将Lua中Table的键值对存在List中,List中只能存没有明确键的。只能映射值,没有key的。

        List list = luaEnv.Global.Get>("person");
        foreach (var item in list)
        {
            print(item);
        }
 
   

4.另一种by ref方式:映射到LuaTable类

        LuaTable luaTable = luaEnv.Global.Get("person");
        print(luaTable.Get("name"));
        print(luaTable.Get("age"));

在LuaTable中可以通过.GetInPath获得表中表

person = {
    man = {height = 100;};
    name = "kk";
    age = 23,
    2,
    3,
    4,
    56,
    eat = function()
        print("111");
    end
}
print(luaTable.GetInPath("man.height"));

三.访问一个全局的function
1.映射到delegate
在C#中delegate的参数与Lua中的funciton的参数对应即可

Action act1 = luaEnv.Global.Get("add");
act1();
act1 = null;//释放对Lua的引用

在Lua中的方法需要传参的时候:

function add(a,b)
    print(a+b);
end

在定义委托的时候就用delegate并添加CSharpCallLua的特性

[CSharpCallLua]
    delegate void Add(int a, int b);

接受返回值

function add(a,b)
    print(a+b);
    return 666;
end
[CSharpCallLua]
    delegate int Add(int a, int b);

多返回值
使用ref或out参数接受多的返回值

function add(a,b)
    print(a+b);
    return 666,a,b;
end
 [CSharpCallLua]
    delegate int Add(int a, int b,out int resa,out int resb);
Add add = luaEnv.Global.Get("add");
        //print (add(3, 2));
        int resa;
        int resb;
        int ans = add(6,9,out resa,out resb);
        print(resa + "__" + resb);

2.映射到LuaFunction
调用起来比较慢,LuaFunction上有个变参Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。

四.使用建议
C#调用Lua全局的变量代价较大尽量少做,可以的话可以讲Lua中的变量存起来避免重复的映射。适用方于xLua解耦。

Lua访问C#

newC#对象
在Lua中创建一个游戏物体:

CS.UnityEngine.GameObject()

lua中没有new关键字。
所有C#相关都放到CS下,包括构造函数、静态成员属性、方法
xlua支持重载

访问C#静态属性、方法
Lua中调用某一个对象中的方法要通过:的方式进行调用。

new C#对象
你在C#这样new一个对象:
var newGameObj = new UnityEngine.GameObject();
对应到Lua是这样:
local newGameObj = CS.UnityEngine.GameObject()
基本类似,除了:
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')

访问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')
将分别访问整数参数的TestFunc和字符串参数的TestFunc。
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)

操作符
支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]

参数带默认值的方法
和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
可变参数方法
对于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
可以在lua里头这样调用:
testobj:VariableParamsFunc(5, 'hello', 'john')
使用Extension methods
在C#里定义了,lua里就能直接使用。
泛化(模版)方法
不直接支持,可以通过Extension methods功能进行封装后调用。

枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的
另外,如果枚举类加入到生成代码的话,枚举类将支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.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)

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

C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等,例如:
C#下B结构体(class也支持)定义如下:
public struct A
{
public int a;
}

public struct B
{
public A b;
public double c;
}
某个类有成员函数如下:
void Foo(B b)
在lua可以这么调用
obj:Foo({b = {a = 100}, c = 200})

获取类型(相当于C#的typeof)
比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样
typeof(CS.UnityEngine.ParticleSystem)

“强”转
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。

EG:在VSCode中找到files.associations文件,在settings.json中添加

{
    "files.associations": {"*.lua.txt":"lua"}
}

就可以在txt文件中编写lua代码并显示高亮了

你可能感兴趣的:(XLua-操作与使用)