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"]);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM