熱更新應用–熱修補程式Hotfix學習筆記
一.熱修補程式簡介
熱修補程式主要是用於將純C#工程在不重做的情況下通過打修補程式的形式改造成具備lua熱更新功能工程,主要是讓原來腳本中Start函數和Update函數等函數程式碼塊重定向到lua程式碼。
二.第一個熱修補程式
1.C#程式碼端:
1)創建一個腳本,並掛載到遊戲中的任意物體上(實際使用過程中一般修改已有腳本,這裡測試隨意掛載就好)
2)在腳本中定義好測試用的方法,在Start函數中執行Lua文件(LuaManager類及C#調用lua程式碼的方式詳見xlua學習筆記,LuaManager類在:四.C#調用lua-3.lua解析器管理器)
public class HotfixMain : MonoBehaviour { void Start() { LuaManager.Instance.DoLuaFile("Main"); //調用定義的方法,這些方法被lua中的熱修補程式重新定義了 Debug.Log(Add(1, 2)); Speak("我很帥"); } //預備給熱修補程式覆蓋的成員方法 public int Add(int a,int b) { return 0; } //預備給熱修補程式覆蓋的靜態方法 public static void Speak(string str) { Debug.Log("hahaha"); } }
2.lua端程式碼
1)將lua文件放在LuaManager類能夠重定向到的文件夾中,或者添加LuaManager類中的重定向方法使lua文件能被找到,這裡放在Assets目錄下的Lua文件夾下,LuaManager中已經添加了這個文件的重定向方法。
2).C#程式碼調用了Main,所以在文件夾中添加Main.lua這個lua文件,這個文件使lua的主入口文件,相當於C#工程中的Main方法,主要用於執行其他lua文件、定義一些通用全局變數、初始化等。這裡Main文件中執行Hotfix1文件,程式碼就一句:
require("Hotfix1")
3)Hotfix1.lua文件中定義第一個熱修補程式的程式碼,主要調用方法xlu.hotfix重寫C#中的方法:
--熱修補程式 --lua中的熱修補程式固定寫法 --通過xlua的hotfix函數進行熱修補程式更新,參數是:類名、"函數名",lua函數 --成員方法將self作為第一個參數傳入 xlua.hotfix(CS.HotfixMain,"Add",function(self,a,b) return a + b end) --靜態方法不需要傳入self參數 xlua.hotfix(CS.HotfixMain,"Speak",function(a) print(a) end) --熱修補程式還需要在Unity腳本中作以下操作 --加特性、加宏、生成程式碼、hotfix注入 --熱修補程式缺陷:只要修改了熱修補程式的程式碼,都需要重新做hotfix注入
3.Unity中的操作
1)加特性:在需要被熱修補程式更新的C#類前面加上[Hotfix]特性,這裡給剛才1中創建的腳本加上特性,其他非mono腳本也是一樣的做法:
//加上特性以生成熱修補程式程式碼 [Hotfix] public class HotfixMain : MonoBehaviour { void Start() { LuaManager.Instance.DoLuaFile("Main"); //調用定義的方法,這些方法被lua中的熱修補程式重新定義了 Debug.Log(Add(1, 2)); Speak("我很帥"); } //預備給熱修補程式覆蓋的成員方法 public int Add(int a,int b) { return 0; } //預備給熱修補程式覆蓋的靜態方法 public static void Speak(string str) { Debug.Log("hahaha"); } }
2)加宏:打開Unity中Edit->ProjectSetting
在Player->otherSetting->Scripting Define Symbols中輸入HOTFIX_ENABLE
3)加宏之後生成程式碼,點擊XLua選項下Generate Code選項生成程式碼。注意:加宏成功後XLua選項下會出現Hotfix Inject In Editor選項,這個是hotfix注入使用的選項,如果沒有的話說明剛才的宏沒有成功加上。
4)點擊Hotfix Inject In Editor進行hotfix注入,如果報錯please install the Tools,將xlua工程源文件中的Tools文件夾拷貝到自己的工程中。注意:不是拷貝到Assets目錄下,源工程文件夾中Tools文件夾和Assets文件夾同級,所以將Tools文件夾拷貝到自己的工程文件中和Assets文件夾同級文件夾下而不是Assets目錄下(三張圖片分別是xlua源文件夾中Tools文件夾所在位置、打開工程所在文件夾、拷貝後在自己的工程中Tools文件夾所在位置):
5)運行工程
4.最後:注意每次熱修補程式的程式碼修改後,都需要重新生成程式碼和hotfix注入。
三.hotfix重定向其他內容
1.多函數替換和構造析構函數熱修補程式(構造函數和析構函數重定向後原程式碼邏輯會先執行,再執行lua中重定向的程式碼邏輯,這一點和其他成員函數及靜態函數不同)
//加上特性以生成熱修補程式程式碼 [Hotfix] public class HotfixMain : MonoBehaviour { void Start() { LuaManager.Instance.DoLuaFile("Main"); //調用定義的方法,這些方法被lua中的熱修補程式重新定義了 Debug.Log(Add(1, 2)); Speak("我很帥"); } private void Update() { } //預備給熱修補程式覆蓋的成員方法 public int Add(int a,int b) { return 0; } //預備給熱修補程式覆蓋的靜態方法 public static void Speak(string str) { Debug.Log("hahaha"); } } [Hotfix] public class HotfixTest { public HotfixTest() { Debug.Log("HotfixTest構造函數"); } public void Speak(string str) { Debug.Log(str); } ~HotfixTest() { } }
--多函數替換 --將多個函數寫成一個表作為參數傳入 xlua.hotfix(CS.HotfixMain,{ Update = function(self) print(os.time()) end, Add = function(self,a,b) return a + b end, Speak = function(a) print(a) end }) --構造函數熱修補程式 xlua.hotfix(CS.HotfixTest,{ --構造函數的熱修補程式固定寫法 [".ctor"] = function() print("Lua熱修補程式構造函數") end, Speak = function(self,a) print("Lua熱修補程式Speak函數") end, --析構函數的熱修補程式固定寫法 Finalize = function() print("Lua熱修補程式析構函數") end })
2.協程函數替換
[Hotfix] public class HotfixMain : MonoBehaviour { void Start() { LuaManager.Instance.DoLuaFile("Main"); StartCoroutine(TestCoroutine()); } IEnumerator TestCoroutine() { while (true) { yield return new WaitForSeconds(1f); Debug.Log("c#協程列印一次"); } } }
--協程函數替換 --使用協程必須引入xlua.util util = require("xlua.util") xlua.hotfix(CS.HotfixMain,{ TestCoroutine = function(self) return util.cs_generator(function() while true do coroutine.yield(CS.UnityEngine.WaitForSeconds(1)) print("lua熱修補程式協程函數") end end) end })
3.索引器和屬性替換
[Hotfix] public class HotfixMain : MonoBehaviour { private int[] array = new int[] { 5, 4, 3, 2, 1 }; void Start() { LuaManager.Instance.DoLuaFile("Main"); Debug.Log(this.Age); this.Age = 200; Debug.Log(this[0]); this[2] = 10000; } //定義屬性 public int Age { get { return 0; } set { Debug.Log(value); } } //定義索引器 public int this[int index] { get { if (index >= 0 && index < 5) return array[index]; return 0; } set { if (index >= 0 && index < 5) array[index] = value; } } }
xlua.hotfix(CS.HotfixMain,{ --屬性熱修補程式的固定寫法 --使用set_屬性名替換設置屬性的方法,使用get_屬性名替換獲取屬性值的方法 set_Age = function(self,v) print("Lua熱修補程式設置屬性") end, get_Age = function(self) return 10 end, --索引器在類中是唯一的,固定寫法和屬性類似 --使用set_Item替換索引器的set方法,使用get_Item替換索引器的set方法 get_Item = function(self,index) print("Lua熱修補程式重定向索引器get") return 1000; end, set_Item = function(self,index,v) print("Lua熱修補程式重定向索引器set") end })
4.事件替換
[Hotfix] public class HotfixMain : MonoBehaviour { event UnityAction customEvent; void Start() { LuaManager.Instance.DoLuaFile("Main"); StartCoroutine(EventAddRemove()); } private void Update() { if (customEvent != null) customEvent(); } /// <summary> /// 添加到委託中的函數 /// </summary> private void Test() { Debug.Log("event test running"); } /// <summary> /// 使用協程添加和刪除委託函數 /// </summary> /// <returns></returns> private IEnumerator EventAddRemove() { customEvent += Test; yield return new WaitForSeconds(5f); customEvent -= Test; } }
--事件熱修補程式 xlua.hotfix(CS.HotfixMain,{ --add_事件名 代表添加事件 --remove_事件名 代表移除事件 add_customEvent = function(self,del) print(del) print("添加事件函數") --在添加事件時,不要把傳入的委託往事件中存,否則會死循環 --self:customEvent("+",del) end, remove_customEvent = function(self,del) print(del) print("移除事件函數") end })
5.泛型類替換
[Hotfix] public class HotfixTest2<T> { public void Test(T str) { Debug.Log(str); } }
void Start() { LuaManager.Instance.DoLuaFile("Main"); new HotfixTest2<string>().Test("movin"); new HotfixTest2<int>().Test(10000); }
--泛型類中泛型T可以變化,所以要一個類型一個類型地替換 --在第一個參數後面加上括弧,括弧中書寫一個類型,代表如果泛型是這個類型時地替換方法 xlua.hotfix(CS.HotfixTest2(CS.System.String),{ Test = function(self,str) print("泛型為string時的熱修補程式,參數是"..str) end }) xlua.hotfix(CS.HotfixTest2(CS.System.Int32),{ Test = function(self,i) print("泛型為int時的熱修補程式,參數是"..i) end })