簡介
在基於lua進行熱更新的專案中,我們通常會通過luaBehaviour來讓lua檔案模擬MonoBehaviour,可以讓lua檔案擁有一些MonoBehaviour的生命週期,如Enable、Disable、Update。
同時可以注入一些UnityEngine.Object。在lua中方便的呼叫Unity中的物件。方便開發者進行開發。
本文進行介紹的luaBehaviour就是基於這個思路設計的,除了上面提到的特性之外還通過Json支援了更多型別的注入,Editor介面更人性化的展示,整個開發過程更接近MonoBehaviour的開發體驗。
使用
1.新建繼承LuaBehaviour的lua檔案,通過AddDefineList新增需要序列化的資訊。
2.直接把這個lua檔案拖到CS中的LuaBehaviour上即可。
接下來可以像MonoBehaviour一樣對資料進行編輯了。
實現
指令碼的序列化
luaBehaviour在使用的時候一般是通過記錄名字或路徑的方式來序列化lua檔案的的。打包後通過這個路徑從AssetBundle中進行lua檔案的載入(這個是lua指令碼熱更新常用的策略,在這邊就不多說了)。
這裡還是通過路徑來記錄lua檔案,但是不需要手動輸入,而是通過直接拖動的方式間接記錄lua檔案路徑。
注入的方式
不通過C#側定義註冊資訊,而是通過在Lua中先定義好要註冊的型別和名稱(更符合MonoBehaviour的開發姿勢)。開發時,Editor讀取lua中定義的型別和名稱資訊,展示在Inspector中,供開發者編輯。
在執行時再將序列化的資料注入到lua例項中。
序列化的實現
UnityEngine.Object型別物件的序列化
這種型別沒啥特別的,在LuaBehaviour指令碼中定義一個記錄key和UnityEngine.Object的List即可。
其他型別的序列化
非UnityEngine.Object型別就沒有一個統一的格式,無法通過一個列表簡單的進行記錄。但是我們檢視Prefab的例項可以發現,Prefab序列化的資料其實和Json很像。對在MonoBehaviour中定義的各個欄位的序列化姿勢也和Json很像。
所以這裡考慮使用Json來序列化非UnityEngine.Object型別的物件。剛好Unity由提供了一套簡單高效的Json工具JsonUtility。JsonUtility內部就是通過Unity serializer實現的,所以穩定性很有保證。
但是JsonUtility有個缺點,只能序列化一部分型別,不能序列化如Int、List這種型別。
為了解決這個問題。這裡在序列化的時候通過泛型為每個型別生成一個Wrap型別。即可通用的實現各種型別的序列化。
注入的實現
UnityEngine.Object型別物件的注入
也沒啥好說的,直接根據key向Lua例項中Set即可。
其他型別的注入
先例項化為Wrap物件,然後再取出其中的需要注入的物件,Set到Lua物件中即可。
Inspector介面中的展示
UnityEngine.Object型別物件的展示
UnityEngine.Object型別統一使用EditorGUILayout.ObjectField繪製即可。
其他型別的展示
因為沒有找到一個通用的可以表現所有物件的繪製方式,所以這裡也做了一個轉換。先通過Emit生成一個繼承自ScriptableObject的類(因為ScriptableObject是UntiyEngine.Object,所以可以使用SerializedObject來繪製。同時可以直接通過ScriptableObject.CreateInstance進行例項的建立)。把需要繪製的物件放到這個類裡面,然後通過EditorGUILayout.PropertyField繪製即可。
Enable、Update等函式的呼叫
這裡把Update、FixedUpdate等高頻或者很少使用的函式拆分出去,只有在Lua中定義了這些函式,才新增對應的Assistant指令碼對這些函式進行呼叫。
Enable、Disable、Destroy這三個常用函式,就直接放在LuaBehaviour指令碼中進行呼叫。
Tips
- 這篇文章只起到大致思路和關鍵點的說明,具體細節可以直接看程式碼,程式碼比較少也比較清晰。
- 通過Wrap的方式序列化各種物件的方式其實也可以考慮用到一些使用者資料在客戶端的持久化。
- 因為這邊主要展示LuaBehanviour的功能,所以AssetBundle的生成和從AssetBundle中載入lua檔案都寫得很臨時,僅作展示用。
- 打包測試之前要用AssetBundles->Build AssetBundle For Lua生成一下bundle。