新博客:https://yinl.fun
歡迎關注,同步更新java
最近工做得用Lua實現邏輯,橋樑用的toLua,踩了不少坑,在這裏記錄一下。緩存
首先咱們給出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,下面是解析全局變量的代碼:ui
LuaState lua = new LuaState(); Debug.Log(lua["全局變量名"]);
緩存成函數類LuaFunction:this
LuaFunction func = lua.GetFunction("函數名");
緩存成表類LuaTable:lua
LuaTable table = lua.GetTable("表名");
那麼下面開始踩坑,上代碼:code
// 聲明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
最後的結果爲:
對象
圖中顯示代碼table["map.name"]的結果爲Null,這裏是toLua的一個bug,在緩存成table的時候處理table中的table有些問題。解決方式上面的代碼也給了,就是從table中再獲取table,實在是很是的麻煩,還有個簡單的方式爲直接利用LuaState獲取。blog
這裏利用的是toLua Examples 03的內容做了一些修改,首先仍是看Lua代碼:get
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"]);