找回密码
 立即注册
查看: 293|回复: 0

XLua学习记录

[复制链接]
发表于 2021-8-14 21:26 | 显示全部楼层 |阅读模式
/****************************************************    文件:Test001.cs        作者:Edision    邮箱: 424054763@qq.com    日期:#CreateTime#        功能:执行字符串*****************************************************/using UnityEngine;using XLua;public class Test001 : MonoBehaviour{    LuaEnv luaEnv;    private void Start()    {        luaEnv = new LuaEnv();        luaEnv.DoString("CS.UnityEngine. Debug.Log('Hello')");           }    private void OnDestroy()    {        luaEnv.Dispose();    }}/****************************************************    文件:Test002.cs        作者:Edision    邮箱: 424054763@qq.com    日期:#CreateTime#        功能:加载Lua文件*****************************************************/using UnityEngine;using XLua;public class Test002 : MonoBehaviour {    LuaEnv luaEnv;    private void Start()    {        luaEnv = new LuaEnv();        //Resources文件夹下的lua文件格式为xxx.lua.txt 方便读取        //string luaContent = Resources.Load<TextAsset>("Hello.lua").text;        //luaEnv.DoString(luaContent);        luaEnv.DoString("require'Hello'");    }    private void OnDestroy()    {        luaEnv.Dispose();    }}/****************************************************    文件:Test003.cs        作者:Edision    邮箱: 424054763@qq.com    日期:#CreateTime#        功能:自定义Loader*****************************************************/using System.IO;using UnityEngine;using XLua;public class Test003 : MonoBehaviour {    LuaEnv luaEnv;    private void Start()    {        luaEnv = new LuaEnv();        luaEnv.AddLoader(MyLoader);        luaEnv.DoString("require'Test003'");    }    byte[] MyLoader(ref string filePath)    {        //streamingAssets文件加下放文件Test003.lua.txt        string str = File.ReadAllText(Application.streamingAssetsPath + "/" + filePath + ".lua.txt");        return System.Text.Encoding.UTF8.GetBytes(str);    }    private void OnDestroy()    {        luaEnv.Dispose();    }}/****************************************************    文件:Test004.cs        作者:Edision    邮箱: 424054763@qq.com    日期:#CreateTime#        功能:CSharpCallLua*****************************************************/using System;using System.Collections.Generic;using UnityEngine;using XLua;public class Test004 : MonoBehaviour{    LuaEnv luaEnv;    private void Start()    {        luaEnv = new LuaEnv();        luaEnv.DoString("require'Test004'");        //int a = luaEnv.Global.Get<int>("a");        //string b = luaEnv.Global.Get<string>("str");        //print(a);        //print(b);        /* 1. 映射到普通class或struct(修改属性不影响原lua属性,不用加[CSharpCallLua])        定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。        这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。        table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。        要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。        这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。*/        // Person p = luaEnv.Global.Get<Person>("person");        // print(p.name + "*******" + p.age);        /*2. 映射到一个interface  (修改属性可影响原lua属性,要给接口加[CSharpCallLua])        这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),        代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,        如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。*/        //IPerson p = luaEnv.Global.Get<IPerson>("person");        //p.name = "***";        //luaEnv.DoString("print(person.name)");        //print(p.name + "*******" + p.age);        //p.eat(12,12);        /*3. 更轻量级的by value方式:映射到Dictionary<>,List<>        不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。*/        //Dictionary<string, object> dict = luaEnv.Global.Get<Dictionary<string, object>>("person");        //foreach (string key in dict.Keys)        //{        //    print(key + "----"+dict[key]);        //}        //List<object> list = luaEnv.Global.Get<List<object>>("person");        //foreach (object key in list)        //{        //    print(key);        //}        /*2. 映射到LuaFunction               这种方式的优缺点刚好和第一种相反。        使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。*/        //LuaTable tab = luaEnv.Global.Get<LuaTable>("person");        //print(tab.Get<string>("name"));        //print(tab.Get<string>("age"));        //foreach (var item in tab.GetKeys<string>())        //{        //    print(item);        //}        /*3. 访问一个全局的function        仍然是用Get方法,不同的是类型映射。        1. 映射到delegate        这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。        delegate要怎样声明呢?        对于function的每个参数就声明一个输入类型的参数。        多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。        参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。        delegate的使用就更简单了,直接像个函数那样用就可以了。*/        //Add act1 = luaEnv.Global.Get<Add>("add");        //int num1, num2, num3;        //act1(70, 80, out num1, out num2, out num3);        //print(num1);        //print(num2);        //print(num3);        /*2. 映射到LuaFunction                这种方式的优缺点刚好和第一种相反。        使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。*/        LuaFunction func = luaEnv.Global.Get<LuaFunction>("add");        object[] os = func.Call(1,2);        foreach (var item in os)        {            print(item);        }    }    private void OnDestroy()    {        luaEnv.Dispose();    }}[CSharpCallLua]public delegate void Add(int a, int b, out int value1, out int value2, out int value3);public class Person{    public string name;    public int age;}[CSharpCallLua]interface IPerson{    string name { get; set; }    int age { get; set; }    void eat(int a, int b);}a=10str="woshiLua"person={name="edision",age=28,12,13,14,15,true,3.3}function person:eat(a,b)        print(a+b);        endfunction person:run(a,b)        print(a-b);        endfunction add(a,b)        print(a+b)        return a,b,a+bend/****************************************************    文件:Test005.cs        作者:Edision    邮箱: 424054763@qq.com    日期:#CreateTime#        功能:LuaCallCSharp*****************************************************/using UnityEngine;using XLua;public class Test005 : MonoBehaviour{    LuaEnv luaEnv;    private void Start()    {        luaEnv = new LuaEnv();        luaEnv.DoString("require'LuaCallCSharp'");    }    private void OnDestroy()    {        luaEnv.Dispose();    }}local newGameObj = CS.UnityEngine.GameObject()local newGameObj2 = CS.UnityEngine.GameObject('helloworld')print(CS.UnityEngine. Time.deltaTime)CS.UnityEngine.Time.timeScale = 0.5local go=CS.UnityEngine.GameObject.Find('helloworld')go.name="update by lua"local test=CS.UnityEngine.GameObject.Find('Test')--调用成员方法的时候,使用冒号local rb=test:GetComponent("Rigidbody")rb.mass=2附:
## xLua教程
### Lua文件加载
1. 执行字符串
    最基本是直接用LuaEnv.DoString执行一个字符串,当然,字符串得符合Lua语法
     比如:
        luaenv.DoString("print('hello world')")
    完整代码见XLua\Tutorial\LoadLuaScript\ByString目录
     > 但这种方式并不建议,更建议下面介绍这种方法。
2. 加载Lua文件
    用lua的require函数即可
     比如:
        DoString("require 'byfile'")
    完整代码见XLua\Tutorial\LoadLuaScript\ByFile目录
    require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。
     目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀(见附带的例子)。
    建议的加载Lua脚本方式是:整个程序就一个DoString("require 'main'"),然后在main.lua加载其它脚本(类似lua脚本的命令行执行:lua main.lua)。
    有童鞋会问:要是我的Lua文件是下载回来的,或者某个自定义的文件格式里头解压出来,或者需要解密等等,怎么办?问得好,xLua的自定义Loader可以满足这些需求。
3. 自定义Loader
    在xLua加自定义loader是很简单的,只涉及到一个接口:
        public delegate byte[] CustomLoader(ref string filepath);
         public void LuaEnv.AddLoader(CustomLoader loader)
    通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。
     有了这个就简单了,用IIPS的IFS?没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密?没问题,自己写loader读取文件解密后返回即可。。。
     完整示例见XLua\Tutorial\LoadLuaScript\Loader
### C#访问Lua
这里指的是C#主动发起对Lua数据结构的访问。
本章涉及到的例子都可以在XLua\Tutorial\CSharpCallLua下找到。
1. 获取一个全局基本数据类型
     访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。

         luaenv.Global.Get<int>("a")
         luaenv.Global.Get<string>("b")
         luaenv.Global.Get<bool>("c")
2. 访问一个全局的table
    也是用上面的Get方法,那类型要指定成啥呢?
     1. 映射到普通class或struct
        定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。
         这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
        table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。
         要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
        这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。
         那有没有引用方式的映射呢?有,下面这个就是:
    2. 映射到一个interface

         这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
    3. 更轻量级的by value方式:映射到Dictionary<>,List<>
        不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
    4. 另外一种by ref方式:映射到LuaTable类

         这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。
3. 访问一个全局的function
    仍然是用Get方法,不同的是类型映射。
     1. 映射到delegate
        这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
        delegate要怎样声明呢?
         对于function的每个参数就声明一个输入类型的参数。
         多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
        参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
        delegate的使用就更简单了,直接像个函数那样用就可以了。
    2. 映射到LuaFunction

         这种方式的优缺点刚好和第一种相反。
         使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
4. 使用建议
    1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
    2. 如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
### Lua调用C#
> 本章节涉及到的实例均在XLua\Tutorial\LuaCallCSharp下
#### 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未整数,而luajit版本,相当于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#复杂类型,在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对象。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 11:31 , Processed in 0.090247 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表