• toLua踩坑


    新博客:https://yinl.fun
    欢迎关注,同步更新

    toLua踩坑篇

    最近工作得用Lua实现逻辑,桥梁用的toLua,踩了很多坑,在这里记录一下。

    坑~toLua解析Lua属性

    首先我们给出Lua文件的内容,基于toLua Examples 04修改:

    print('Objs2Spawn is: '..Objs2Spawn)
    var2read = 42
    varTable = {1,2,3,4,5}
    varTable.default = 1
    varTable.map = {}
    varTable.map.name = "map"
    
    meta = {name = "meta"}
    setmetatable(varTable, meta)
    
    function TestFunc()
        print('get func by variable')
    end
    
    function varTable.func()
        print("获取table中的函数")
    end
    

    在展示坑之前先看看使用toLua,下面是解析全局变量的代码:

    LuaState lua = new LuaState();
    Debug.Log(lua["全局变量名"]);
    

    缓存成函数类LuaFunction:

    LuaFunction func = lua.GetFunction("函数名");
    

    缓存成表类LuaTable:

    LuaTable table = lua.GetTable("表名");
    

    那么下面开始踩坑,上代码:

    // 声明lua解析器对象
    LuaState lua = new LuaState();
    lua.Start();
    // 添加lua执行路径
    lua.AddSearchPath(Application.dataPath + "/Lua");
    // 将Objs2Spawn存到lua的全局变量中并赋值
    lua["Objs2Spawn"] = 5;
    // 寻找文件并执行,如找到则返回一个对象。
    lua.DoFile("CallLuaFunction.lua");
    // 寻找文件并执行,第一次生成对象返回,之后返回之前生成过的对象。
    lua.Require("AccessingLuaVariables");
    
    // 通过LuaState访问
    Debug.Log(lua["var2read"]); // 输出-> 42
    Debug.Log(lua["varTable.default"]); // 输出-> 1
    // 直接利用LuaState获取name
    Debug.Log(lua["varTable.map.name"]); // 输出-> map
    
    LuaFunction func = lua["TestFunc"] as LuaFunction;
    func.Call();
    
    func = lua["varTable.func"] as LuaFunction;
    func.Call();
    func.Dispose();
    
    //cache成LuaTable访问
    LuaTable table = lua.GetTable("varTable");
    Debug.Log(table["default"]); // 输出-> 1
    // 利用Table获取name,有Bug
    Debug.Log(table["map.name"]); // 输出-> Null
    LuaTable table1 = table.GetTable<LuaTable>("map");
    // 利用Table的Table获取name
    Debug.Log(table1["name"]); // 输出-> map
    

    最后的结果为:
    Img

    图中显示代码table["map.name"]的结果为Null,这里是toLua的一个bug,在缓存成table的时候处理table中的table有些问题。解决方式上面的代码也给了,就是从table中再获取table,实在是非常的麻烦,还有个简单的方式为直接利用LuaState获取。

    坑~Lua函数解析

    这里利用的是toLua Examples 03的内容作了一些修改,首先还是看Lua代码:

    CallLuaFunction = {}
    
    function CallLuaFunction.func1()
        print("Call Lua Function")
    end
    
    function CallLuaFunction.func2(num1, num2)
        print("执行func2")
        return num1 + num2
    end
    
    function CallLuaFunction.func3(num)
        return num + 1
    end
    
    function CallLuaFunction.func4(num)
        print("执行func4 " .. num)
    end
    
    function CallLuaFunction:func5()
        self.a = 1
        self.b = 2
        print(self.a .. " " .. self.b)
    end
    

    这里一共有5个函数,我们分别针对这五个函数的解析做一些展示。首先是func1,这个函数没有任何的参数也没有返回值,所以调用它很简单(这里假设已经做完lua的建立和释放工作):

    // 获取Lua函数
    LuaFunction func = lua.GetFunction("CallLuaFunction.func1");
    // 执行函数,无返回值,最多支持9个参数
    func.Call();
    

    对于func2的函数,有两个参数和返回值,这里给出了示例中的4种执行方式,这里坑就来了

    // 注:必须进行委托初始化才能执行方式1,3,4
    DelegateFactory.Init();
    
    func = lua.GetFunction("CallLuaFunction.func2");
    if (func != null)
    {
        // 第一种方式
        // 执行函数,有返回值,最多支持9个参数1个返回值
        int num = func.Invoke<int, int, int>(10, 20);
        Debug.Log(num);
    
        // 第二种方式
        num = CallFunc(func);
        Debug.Log(num);
    
        // 第三种方式
        // 向LuaFunction中添加委托,利用委托实现函数返回
        // 注:此委托ToLua作者并没有给,所以得自己补充
        // 作者只给了Func<int, int>的委托
        Func<int, int, int> luaFunc = func.ToDelegate<Func<int, int, int>>();
        num = luaFunc(10, 20);
        Debug.Log(num);
    
        // 第四种方式
        // 直接利用LuaState执行函数,最多6个参数1个返回值
        num = lua.Invoke<int, int, int>("CallLuaFunction.func2", 10, 20, true);
        Debug.Log(num);
    }
    
    private int CallFunc(LuaFunction func)
    {
        // 函数开始
        func.BeginPCall();
        // 传第一个参
        func.Push(10);
        // 传第二个参
        func.Push(20);
        // 执行函数
        func.PCall();
        // 检查返回值
        int num = (int)func.CheckNumber();
        // 结束函数
        func.EndPCall();
        return num;
    }
    

    再执行到第三种方式的时候会报错,提示no register(未注册),这是为什么呢?因为代码中会注册委托,再进行调用,而Func<int, int, int>并没有被注册,所以报错了。这里我们知道原因了,那就自己加一个注册呗,说干就干:

    DelegateFactory脚本的Register方法控制的委托注册在里面加上一行:

    DelegateTraits<System.Func<int,int,int>>.Init(factory.System_Func_int_int_int);
    

    同是在DelegateFactory脚本中我们添加System_Func_int_int_int函数:

    public System.Func<int, int, int> System_Func_int_int_int(LuaFunction func, LuaTable self, bool flag)
    {
        if (func == null)
        {
            System.Func<int, int, int> fn = delegate (int param0, int param1) { return 0; };
            return fn;
        }
    
        if (!flag)
        {
            System_Func_int_int_int_Event target = new System_Func_int_int_int_Event(func);
            System.Func<int, int, int> d = target.Call;
            target.method = d.Method;
            return d;
        }
        else
        {
            System_Func_int_int_int_Event target = new System_Func_int_int_int_Event(func, self);
            System.Func<int, int, int> d = target.CallWithSelf;
            target.method = d.Method;
            return d;
        }
    }
    

    到这里我们还缺少System_Func_int_int_int_Event类,也同是在DelegateFactory脚本中添加:

    class System_Func_int_int_int_Event : LuaDelegate
    {
        public System_Func_int_int_int_Event(LuaFunction func) : base(func) { }
        public System_Func_int_int_int_Event(LuaFunction func, LuaTable self) : base(func, self) { }
    
        public int Call(int param0, int param1)
        {
            func.BeginPCall();
            func.Push(param0);
            func.Push(param1);
            func.PCall();
            int ret = (int)func.CheckNumber();
            func.EndPCall();
            return ret;
        }
    
        public int CallWithSelf(int param0, int param1)
        {
            func.BeginPCall();
            func.Push(self);
            func.Push(param0);
            func.Push(param1);
            func.PCall();
            int ret = (int)func.CheckNumber();
            func.EndPCall();
            return ret;
        }
    }
    

    坑就这样踩过去了,运行以下,你会发现不会报错并且执行也是正确的了。

    接下来是func3函数,一个参数,这个就不用多说了与func2一样的。

    之后是func4函数,这里就展示一下func2中的最后一种执行方式,不过没有返回值:

    lua.Call<int>("CallLuaFunction.func4", 10, true);
    

    最后的func5就很神奇了,哪里神奇呢?你注意到表名和函数名的连接处用的是":"了么,这里与"."的区别就是会传入self,相当于C#中的this,那又会说了func5不是没有参数么,因为self是隐式的传入,如果你从C#调用它,必须得传这个参数。坑,这里用GetTable解决一下,感觉很麻烦,不过toLua应该是内置了":"函数的形式。

    LuaTable table = lua.GetTable("CallLuaFunction");
    lua.Call<LuaTable>("CallLuaFunction.func5", table, true);
    Debug.Log(table["a"]);
    
  • 相关阅读:
    POJ 1228 Grandpa's Estate | 凸包
    POJ 2187 Beauty Contest | 旋转卡壳
    POJ 3348 Cows | 凸包模板题
    POJ 1375 Intervals | 解析几何
    POJ 2074 | 线段相交
    POJ 1039 Pipe | 线段相交
    POJ 3304 Segments | 线段相交
    POJ 2318 TOYS | 二分+判断点在多边形内
    jpg、jpeg、png... 的区别
    xhr.readyState就绪状态
  • 原文地址:https://www.cnblogs.com/SHOR/p/9465185.html
Copyright © 2020-2023  润新知