lua ue_slua unreal分析(二)LuaActor与lua表互访
相關文章:
南京周潤發:slua unreal分析(一)LuaActor概覽?zhuanlan.zhihu.com南京周潤發:slua unreal分析( 三)slua與GC?zhuanlan.zhihu.com南京周潤發:slua unreal分析(四)LuaArray&LuaMap?zhuanlan.zhihu.com前文介紹了LuaActor,LuaActor在lua中有一個對應的表,這樣就能用lua代碼實現藍圖部分邏輯。我們可以通過在lua表中訪問LuaActor的屬性和方法,也可以在LuaActor中調用lua表的方法,實現相互訪問。南京周潤發:slua unreal分析( 三)slua與GC前文介紹了LuaActor,LuaActor在lua中有一個對應的表,這樣就能用lua代碼實現藍圖部分邏輯。我們可以通過在lua表中訪問LuaActor的屬性和方法,也可以在LuaActor中調用lua表的方法,實現相互訪問。
LuaActor訪問對應lua表
LuaActor有"CallLuaMember"方法,可以支持在C++中調用Lua定義的方法,需要提供方法名和參數列表,定義如下:
UFUNCTION(BlueprintCallable, Category = "slua") FLuaBPVar CallLuaMember(FString FunctionName, const TArray<FLuaBPVar>& Args) {return callMember(FunctionName, Args); }從定義可以看出,在藍圖中也能進行訪問,比較方便。
之前提過,在LuaActor中存儲了"luaSelfTable"變量,它就是lua中對應的表,因此理論上訪問該表上的方法并不復雜。過程如下:
其中C++和lua間的數據傳遞,都會用LuaVar進行封裝,用戶代碼直接使用LuaVar即可,不用關心gc之類的細節。
Lua表訪問LuaActor
從Lua表訪問LuaActor的屬性和方法的場景通常更多一些。在藍圖中,可以訪問UObject暴露給藍圖的變量和方法,在lua中也能實現類似的功能,而且個人覺得有以下兩個優點:
- 可以訪問所有UProperty,不限于BlueprintRead/Write標記
- lua中沒有類型,屬性方法獲取均基于反射,不需像藍圖一樣進行DynamicCast,代碼更加精簡
之前簡單介紹過LuaActor如何把自己指針傳遞給lua表,現在需要詳細分析一下里面的細節。
UserData
C++和lua是用棧進行數據傳遞的,當想把一個UObject指針傳遞給lua層時,slua會做一些中間處理,真正壓入棧的是UserData數據結構,它記錄了一些關于UObject的描述信息。lua可以使用UserData來索引LuaActor,而UE引擎可以使用UserData來進行正確的GC管理。
當LuaActor初始化,把自己指針壓棧時,會執行到
return pushGCObject<UObject*>(L,obj,"UObject",setupInstanceMT,gcObject,ref);這行代碼,該方法會創建UserData并傳遞給lua。其中有三個參數比較重要,obj是該UObject的指針,setupInstanceMT是設置元表方法的指針,gcObject是該UserData被lua銷毀時將會調用的方法。
setupInstanceMT真正實現了lua到UObject的屬性/方法訪問,關于lua元表的功能在此不贅述了,setupInstanceMT代碼如下:
int LuaObject::setupInstanceMT(lua_State* L) {lua_pushcfunction(L,instanceIndex);lua_setfield(L, -2, "__index");lua_pushcfunction(L,newinstanceIndex);lua_setfield(L, -2, "__newindex");lua_pushcfunction(L, objectToString);lua_setfield(L, -2, "__tostring");return 0; }可以看到,元表中只設置了三個方法,"__index"實現值獲取功能,而"__newindex"實現賦值功能。先看下"__index"方法的實現instanceIndex方法。
instanceIndex
我們在lua中寫類似"self.name"的語句時就會用到此方法,會把self和name都壓棧,然后instanceIndex方法就會根據這兩個參數來獲取name值。instanceIndex基于UE的反射機制工作,因此可以不限于BlueprintRead/Write標記,只要是UProperty和UFunction,都可以訪問。一次instanceIndex訪問流程如下:
其中用到了FuncCache做加速,因為通過反射訪問UClass上的Function和Property比較慢。
newinstanceIndex
newindex方法用于修改LuaActor上property的值,比如"self.name=1",不過這里的property要求不能被標記為BlurprintReadOnly。方法與之前的index類似,都是先通過反射獲取到property。獲取到property后就能通過反射機制繼續設置該property的值。
看到這里,就能大致了解lua表如何訪問LuaActor屬性和方法了,但是還有個小問題,就是以上的__index和__newindex都是UserData元表的方法,lua函數棧里面也只有UserData,但lua在遇到形如"self.name"的語句時只會把self(luatable)和name壓入函數棧,那self是如何變成UserData的呢?這要回到LuaActor初始化的地方。
// setup metatable if (!metaTable.isValid()) {luaL_newmetatable(L, typeName);lua_pushcfunction(L, __index);lua_setfield(L, -2, "__index");lua_pushcfunction(L, __newindex);lua_setfield(L, -2, "__newindex");metaTable.set(L, -1);lua_pop(L, 1); } metaTable.push(L); lua_setmetatable(L, -2);通過以上方式設置了lua表的metatable,其中包括了"__index"和"__newindex"方法。"__index"方法定義如下:
int LuaBase::__index(NS_SLUA::lua_State * L) {lua_pushstring(L, SLUA_CPPINST);lua_rawget(L, 1);if (!lua_isuserdata(L, -1))luaL_error(L, "expect LuaBase table at arg 1");// push keylua_pushvalue(L, 2);// get field from real actorlua_gettable(L, -2);return 1; }可以看到,它是先獲取到__cppInst,即UserData,然后對_cppInst進行key獲取,隨之調用UserData元表上的"__index"方法。UserData起到了轉發的作用。
綜上,LuaActor和lua表的相互訪問方式如下,綠線為LuaActor訪問lua表,黃線為lua表訪問LuaActor。
總結
以上是生活随笔為你收集整理的lua ue_slua unreal分析(二)LuaActor与lua表互访的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 详解EBS接口开发之采购订单导入
- 下一篇: 51单片机复位电路的设计
