tolua之wrap檔案的原理與使用
什麼是wrap檔案
每個wrap檔案都是對一個c#類的包裝,在lua中,通過對wrap類中的函式呼叫,間接的對c#例項進行操作。
wrap類檔案生成和使用的總體流程
生成一個wrap檔案的流程
這部分主要通過分析類的反射資訊完成。
wrap檔案內容解析
使用UnityEngine_GameObjectWrap.cs進行舉例。
註冊部分
public static void Register(LuaState L)
{
L.BeginClass(typeof(UnityEngine.GameObject), typeof(UnityEngine.Object));
L.RegFunction("CreatePrimitive", CreatePrimitive);
L.RegFunction("GetComponent", GetComponent);
L.RegFunction("GetComponentInChildren", GetComponentInChildren);
L.RegFunction("GetComponentInParent", GetComponentInParent);
L.RegFunction("GetComponents", GetComponents);
L.RegFunction("GetComponentsInChildren", GetComponentsInChildren);
L.RegFunction("GetComponentsInParent", GetComponentsInParent);
L.RegFunction("SetActive", SetActive);
L.RegFunction("CompareTag", CompareTag);
L.RegFunction("FindGameObjectWithTag", FindGameObjectWithTag);
L.RegFunction("FindWithTag", FindWithTag);
L.RegFunction("FindGameObjectsWithTag", FindGameObjectsWithTag);
L.RegFunction("Find", Find);
L.RegFunction("AddComponent", AddComponent);
L.RegFunction("BroadcastMessage", BroadcastMessage);
L.RegFunction("SendMessageUpwards", SendMessageUpwards);
L.RegFunction("SendMessage", SendMessage);
L.RegFunction("New", _CreateUnityEngine_GameObject);
L.RegFunction("__eq", op_Equality);
L.RegFunction("__tostring", ToLua.op_ToString);
L.RegVar("transform", get_transform, null);
L.RegVar("layer", get_layer, set_layer);
L.RegVar("activeSelf", get_activeSelf, null);
L.RegVar("activeInHierarchy", get_activeInHierarchy, null);
L.RegVar("isStatic", get_isStatic, set_isStatic);
L.RegVar("tag", get_tag, set_tag);
L.RegVar("scene", get_scene, null);
L.RegVar("gameObject", get_gameObject, null);
L.EndClass();
}
這部分程式碼由GenRegisterFunction()生成,可以看到,這些程式碼分為了4部分:
1.BeginClass部分,負責類在lua中的初始化部分
2.RegFunction部分,負責將函式註冊到lua中
3.RegVar部分,負責將變數和屬性註冊到lua中
4.EndClass部分,負責類結束註冊的收尾工作
BeginClass部分
①用於建立類和類的元表,如果類的元表的元表(類的元表是承載每個類方法和屬性的實體,類的元表的元表就是類的父類)
②將類新增到loaded表中。
③設定每個類的元表的通用的元方法和屬性,__gc,name,ref,__cal,__index,__newindex。
RegFunction部分
每一個RefFunction做的事都很簡單,將每個函式轉化為一個指標,然後新增到類的元表中去,與將一個c函式註冊到lua中是一樣的。
RegVar部分
每一個變數或屬性或被包裝成get_xxx,set_xxx函式註冊新增到類的元表的gettag,settag表中去,用於呼叫和獲取。
EndClass部分
做了兩件事:
①設定類的元表
②把該類加到所在模組代表的表中(如將GameObject加入到UnityEngine表中)
每個函式的實體部分
由於建構函式,this[],get_xxx,set_xxx的原理都差不多,都是通過反射的資訊生成的,所以放在一起用一個例項講一下(使用GameObject的GetComponent函式進行說明)。
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static int GetComponent(IntPtr L)
{
try
{
//獲取棧中引數的個數
int count = LuaDLL.lua_gettop(L);
//根據棧中元素的個數和元素的型別判斷該使用那一個過載
if (count == 2 && TypeChecker.CheckTypes<string>(L, 2))
{
//將棧底的元素取出來,這個obj在棧中是一個fulluserdata,需要先將這個fulluserdata轉化成對應的c#例項,也就是呼叫這個GetComponent函式的GameObject例項
UnityEngine.GameObject obj = (UnityEngine.GameObject)ToLua.CheckObject(L, 1, typeof(UnityEngine.GameObject));
//將棧底的上一個元素取出來,也就是GetComponent(string type)的引數
string arg0 = ToLua.ToString(L, 2);
//通過obj,arg0直接第呼叫GetCompent(string type)函式
UnityEngine.Component o = obj.GetComponent(arg0);
//將呼叫結果壓棧
ToLua.Push(L, o);
//返回引數的個數
return 1;
}
//另一個GetComponent的過載,跟上一個差不多,就不詳細說明了
else if (count == 2 && TypeChecker.CheckTypes<System.Type>(L, 2))
{
UnityEngine.GameObject obj = (UnityEngine.GameObject)ToLua.CheckObject(L, 1, typeof(UnityEngine.GameObject));
System.Type arg0 = (System.Type)ToLua.ToObject(L, 2);
UnityEngine.Component o = obj.GetComponent(arg0);
ToLua.Push(L, o);
return 1;
}
//引數數量或型別不對,沒有找到對應的過載,丟擲錯誤
else
{
return LuaDLL.luaL_throw(L, "invalid arguments to method: UnityEngine.GameObject.GetComponent");
}
}
catch (Exception e)
{
return LuaDLL.toluaL_exception(L, e);
}
}
可以看到,GetComponent函式的內容,其實就是通過反射分析GetComponent的過載個數,每個過載的引數個數,型別生成的。具體內容和lua呼叫c函式差不多。
每個函式實際的呼叫過程
假如說在lua中有這麼一個呼叫:
local tempGameObject = UnityEngine.GameObject("temp")
local transform = tempGameObject.GetComponent("Transform")
第二行程式碼對應的實際呼叫過程是:
1.先去tempGameObject的元表GameObject元表中嘗試去取GetComponent函式,取到了。
2.呼叫取到的GetComponent函式,呼叫時會將tempGameObject,"Transform"作為引數先壓棧,然後呼叫GetComponent函式。
3.接下來就進入GetComponent函式內部進行操作,因為生成了新的ci,所以此時棧中只有tempGameOjbect,"Transfrom"兩個元素。
4.根據引數的數量和型別判斷需要使用的過載。
5.通過tempGameObject代表的c#例項的索引,在objects表中找到對應的例項。同時取出"Transform"這個引數,準備進行真正的函式呼叫。
6.執行obj.GetComponent(arg0),將結果包裝成一個fulluserdata後壓棧,結束呼叫。
7.lua中的transfrom變數賦值為這個壓棧的fulluserdata。
8.結束。
其中3-7的操作都在c#中進行,也就是wrap檔案中的GetComponent函式。
一個類通過wrap檔案註冊進lua虛擬機器後是什麼樣子的
使用GameObjectWrap進行舉例
可以看到GameObject的所有功能都是通過一個元表實現的,通過這個元表可以呼叫GameObjectWrap檔案中的各個函式來實現對GameObject例項的操作,這個元表對使用者來說是不可見的,因為我們平時只會在程式碼中呼叫GameObject類,GameObject例項,並不會直接引用到這個元表,接下來來分析一下GameObject類,GameObject例項與這個元表的關係:
①GameObject類:其實只是一個放在_G表中供人呼叫的一個充當索引的表,我們通過它來觸發GameObject元表的各種元方法,實現對c#類的使用。
②GameObject的例項:是一個fulluserdata,內容為一個整數,這個整數代表了這個例項在objects表中的索引(objects是一個用list實現的回收連結串列,lua中呼叫的c#類例項都存在這個裡面,後面會講這個objects表),每次在lua中呼叫一個c#例項的方法時,都會通過這個索引找到這個索引在c#中對應的例項,然後進行操作,最後將操作結果轉化為一個fulluserdata(或lua的內建型別,如bool等)壓棧,結束呼叫。
在lua中呼叫一個c#例項中的函式或變數的過程
local tempGameObject = UnityEngine.GameObject("temp")
local instanceID = tempGameObject.GetInstanceID()
在瞭解了GameObject元表後,這些只是一些基礎的元表操作,就不多做解釋。
lua中c#例項的真正儲存位置
前面說了每一個c#例項在lua中是一個內容為整數索引的fulluserdata,在進行函式呼叫時,通過這個整數索引查詢和呼叫這個索引代表的例項的函式和變數。
生成或使用一個代表c#例項的lua變數的過程大概是這樣的。
還用這個例子來說明:
local tempGameObject = UnityEngine.GameObject("temp")
local transform = tempGameObject.GetComponent("Transform")
所以說lua中呼叫和建立的c#例項實際都是存在c#中的objects表中,lua中的變數只是一個持有該c#例項索引位置的fulluserdata,並沒有直接對c#例項進行引用。
對c#例項進行函式的呼叫和變數的修改都是通過元表呼叫操作wrap檔案中的函式進行的。
以上就是c#類如何通過wrap類在lua中進行使用的原理。
相關文章
- struts2的工作原理與檔案結構
- python檔案打包利器之pyinstaller的使用Python
- Unity熱更學習toLua使用--[1]toLua的匯入和預設載入執行lua指令碼Unity指令碼
- C檔案與檔案的操作
- 任意檔案讀取與下載的原理及修復
- 修復損壞的gzip壓縮檔案之原理篇
- 使用PowerShell比較本地文字檔案與Web上的文字檔案是否相同Web
- Windows 與 Linux (CentOS7) 之間的檔案共享WindowsLinuxCentOS
- 分散式檔案系統之MogileFS的安裝使用分散式
- 檔案共享之SMB/CIFS協議及Samba的使用協議Samba
- Cycript檔案的使用
- coredump檔案的使用
- HTTP檔案上傳原理HTTP
- vivado常規操作之燒寫bit檔案_固化mcs檔案_除錯介面debug之ila與vio的操作除錯
- Java SE 檔案上傳和檔案下載的底層原理Java
- 檔案系統(八):Linux JFFS2檔案系統工作原理、優勢與侷限Linux
- toLua中Lua呼叫C#中的類C#
- 專案中常用的 .env 檔案原理原始碼分析原始碼
- 檔案系統(七):檔案系統崩潰一致性、方法、原理與侷限
- Android與Python之批量修改AndroidManifest.xml檔案AndroidPythonXML
- epoll使用與原理
- php檔案操作之提取檔案/目錄的名稱PHP
- 【SpringBoot】使用RestTemplate在服務之間進行MultipartFile格式檔案的傳遞【檔案上傳】Spring BootREST
- ThreadLocal的正確使用與原理thread
- cadence 開啟原理圖檔案
- 檔案系統(五):exFAT 檔案系統原理詳解
- 檔案上傳踩坑記及檔案清理原理探究
- JVM載入Class檔案的原理機制JVM
- Linux----12 檔案與檔案操作Linux
- hive迷案之消失的分割槽檔案Hive
- 加速檔案傳輸:檔案和UDP之間的區別UDP
- Flutter Wrap & ChipFlutter
- JavaWeb之實現檔案上傳與下載工具JavaWeb
- JavaWeb之實現檔案上傳與下載元件JavaWeb元件
- JavaWeb之實現檔案上傳與下載示例JavaWeb
- mac下用scp命令實現本地檔案與伺服器Linux檔案之間的相互傳輸Mac伺服器Linux
- Golang 專案之配置檔案Golang
- linux與使用者賬號有關的系統檔案Linux