toLua踩坑

Easter79
• 阅读 779

新博客: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

最后的结果为: toLua踩坑

图中显示代码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"]);
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k