在導入了xlua或者tolua後,我們能在lua代碼裏寫CS.UnityEngine.xxx來調用unity的API。那麼這個功能是如何實現的呢?爲了解開這個疑惑,我們需要了解lua裏面的註冊表和引用系統。
註冊表(Registry)是一個全局的table,它只能被C代碼訪問。通常,可以用它來保存那種需要在幾個模塊中共享的數據。註冊表由一個叫LUA_REGISTRYINDEX的索引可以找到。
它在lua的c源碼裏被定義如下。
它是一個僞索引(Pseudo-Index)。爲什麼叫僞索引?因爲這個索引對應的值不在棧上。而我們平常看見的-1,-2,1,2這些索引的值都在棧上。爲了不跟這些正常索引衝突,僞索引被設得很小。
除了註冊表之外,lua還提供了一個引用系統,方便我們往註冊表裏註冊值和取值。
先說註冊:
int luaL_ref (lua_State *L, int t)
L傳入luaState的指針,t傳入LUA_REGISTRYINDEX。這個函數的作用是彈出棧頂的值,並且用一個新分配的整數key把這個值註冊到註冊表裏,然後返回這個整數key。這個key被稱爲"引用"。
再來看怎麼取值:
int lua_rawgeti (lua_State *L, int index, lua_Integer n)
L傳入luaState的指針,index傳入LUA_REGISTRYINDEX,n傳入上一個函數返回的整數key。作用是把註冊表裏的key對應的值壓棧。
最後是釋放該值:
void luaL_unref (lua_State *L, int t, int ref)
L傳入luaState的指針,t傳入LUA_REGISTRYINDEX,ref傳入luaL_ref返回的整數key。
需要注意的是LUA_REGISTRYINDEX在c端和c#端使用的需要是同一個值 。要麼像tolua在c端把lua源碼的LUA_REGISTRYINDEX改了,要麼像xlua,在c端提供一個api把值傳給c#端。這裏我參照(照抄)xlua的做法。
LUA_API int jlua_get_registry_index() {
return LUA_REGISTRYINDEX;
}
接下來我們在c#端寫一個測試案例:
LuaState lua = new LuaState();
LuaIndexes.LUA_REGISTRYINDEX = LuaDLL.jlua_get_registry_index();
lua.DoFile( "05" );
//把'foo'這個函數壓棧
LuaDLL.lua_getglobal( lua.L, "foo" );
Debug.LogFormat( "StackSize:{0},top type is {1}", LuaDLL.lua_gettop( lua.L ), LuaDLL.lua_type( lua.L, -1 ) );
//存放函數到註冊表
int key = LuaDLL.luaL_ref( lua.L, LuaIndexes.LUA_REGISTRYINDEX );
Debug.LogFormat( "StackSize:{0}", LuaDLL.lua_gettop( lua.L ) );
LuaDLL.lua_rawgeti( lua.L, LuaIndexes.LUA_REGISTRYINDEX, key );
Debug.LogFormat( "StackSize:{0},top type is {1}", LuaDLL.lua_gettop( lua.L ), LuaDLL.lua_type( lua.L, -1 ) );
LuaDLL.lua_pcall( lua.L, 0, 0, 0 );
Debug.LogFormat( "StackSize:{0}", LuaDLL.lua_gettop( lua.L ) );
LuaDLL.luaL_unref( lua.L, LuaIndexes.LUA_REGISTRYINDEX, key );
lua.Dispose();
lua = null;
其中的05.lua裏的代碼如下:
function foo()
print("helloworld")
end
最終打印的結果如下:
這就是本節全部內容。
github工程
對應的是Examples/05_Registry
關於作者:
- 水曜日雞,簡稱水雞,ACG宅。曾參與索尼中國之星項目研發,具有2D聯網多人動作遊戲開發經驗。
CSDN博客:https://blog.csdn.net/j756915370
知乎專欄:https://zhuanlan.zhihu.com/c_1241442143220363264
交流學習羣:891809847