1. Helloworld
1 using UnityEngine; 2 using XLua; 3 4 public class Helloworld : MonoBehaviour { 5 // Use this for initialization 6 void Start () { 7 LuaEnv luaenv = new LuaEnv(); 8 // 執行代碼塊,輸出 hello world 9 luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')"); 10 // 釋放資源 11 luaenv.Dispose(); 12 } 13 }
該案例實現了在 Unity 控制台輸出 hello world。
2. U3DScripting
lua 代碼如下:
1 local speed = 10 2 local lightCpnt = nil 3 4 function start() 5 print("lua start...") 6 -- 訪問環境變量 7 print("injected object", lightObject) 8 -- 查找 Light 組件 9 lightCpnt= lightObject:GetComponent(typeof(CS.UnityEngine.Light)) 10 end 11 12 function update() 13 local r = CS.UnityEngine.Vector3.up * CS.UnityEngine.Time.deltaTime * speed 14 -- 繞y軸旋轉 15 self.transform:Rotate(r) 16 -- 修改光線顏色 17 lightCpnt.color = CS.UnityEngine.Color(CS.UnityEngine.Mathf.Sin(CS.UnityEngine.Time.time) / 2 + 0.5, 0, 0, 1) 18 end 19 20 function ondestroy() 21 print("lua destroy") 22 end
注意,如果要插入中文注釋,需要將 txt 編碼格式改為 UTF-8,否則無法執行。
C# 代碼如下:
1 using UnityEngine; 2 using XLua; 3 using System; 4 5 [System.Serializable] 6 public class Injection 7 { 8 public string name; 9 public GameObject value; 10 } 11 12 [LuaCallCSharp] 13 public class LuaBehaviour : MonoBehaviour { 14 public TextAsset luaScript; // lua腳本文件 15 public Injection[] injections; // 需要注入到環境變量的物體 16 17 internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only! 18 internal static float lastGCTime = 0; 19 internal const float GCInterval = 1;//1 second 20 21 private Action luaStart; 22 private Action luaUpdate; 23 private Action luaOnDestroy; 24 25 private LuaTable scriptEnv; 26 27 void Awake() 28 { 29 scriptEnv = luaEnv.NewTable(); 30 31 LuaTable meta = luaEnv.NewTable(); 32 meta.Set("__index", luaEnv.Global); 33 scriptEnv.SetMetaTable(meta); 34 meta.Dispose(); 35 36 // 配置環境變量,在lua代碼里能直接調用 37 scriptEnv.Set("self", this); 38 foreach (var injection in injections) 39 { 40 scriptEnv.Set(injection.name, injection.value); 41 } 42 // 參數1:Lua代碼的字符串 43 // 參數2:發生error時的debug顯示信息時使用 44 // 參數3:代碼塊的環境變量 45 luaEnv.DoString(luaScript.text, "LuaBehaviour", scriptEnv); 46 47 // 訪問函數 48 Action luaAwake = scriptEnv.Get<Action>("awake"); 49 scriptEnv.Get("start", out luaStart); 50 scriptEnv.Get("update", out luaUpdate); 51 scriptEnv.Get("ondestroy", out luaOnDestroy); 52 53 // 執行事件 54 if (luaAwake != null) 55 { 56 luaAwake(); 57 } 58 } 59 60 // Use this for initialization 61 void Start () 62 { 63 if (luaStart != null) 64 { 65 luaStart(); 66 } 67 } 68 69 // Update is called once per frame 70 void Update () 71 { 72 if (luaUpdate != null) 73 { 74 luaUpdate(); 75 } 76 if (Time.time - LuaBehaviour.lastGCTime > GCInterval) 77 { 78 // 清楚lua未手動釋放的LuaBase對象,需定期調用,這里是1s調用一次 79 luaEnv.Tick(); 80 LuaBehaviour.lastGCTime = Time.time; 81 } 82 } 83 84 void OnDestroy() 85 { 86 if (luaOnDestroy != null) 87 { 88 luaOnDestroy(); 89 } 90 luaOnDestroy = null; 91 luaUpdate = null; 92 luaStart = null; 93 scriptEnv.Dispose(); 94 injections = null; 95 } 96 }
該場景實現了 lua 代碼控制 U3D 物體,以實現物體的旋轉和顏色變化。
三、UIEvent
lua 代碼如下:
1 function start() 2 print("lua start...") 3 -- 給button添加事件 4 -- 點擊輸出 input 輸入內容 5 self:GetComponent("Button").onClick:AddListener(function() 6 print("clicked, you input is '" ..input:GetComponent("InputField").text .."'") 7 end) 8 end
該場景實現了 lua 代碼為 button 添加事件響應函數,以實現點擊按鈕輸出輸入框內容。
注意,lua 中 . 和 : 的區別:
-
- 定義的時候:Class:test() 與 Class.test(self) 是等價的
-
調用的時候:
object
:test() 與
object
.test(
object
) 等價
在這里,調用類的方法使用 :,調用屬性用 . 。
C# 代碼還是上一場景的 LuaBehaviour.cs。
四、InvokeLua
C# 代碼如下:
1 using UnityEngine; 2 using XLua; 3 4 public class InvokeLua : MonoBehaviour 5 { 6 [CSharpCallLua] 7 public interface ICalc 8 { 9 int Add(int a, int b); 10 int Mult { get; set; } 11 } 12 13 [CSharpCallLua] 14 public delegate ICalc CalcNew(int mult, params string[] args); 15 16 private string script = @" 17 local calc_mt = { 18 __index = { 19 Add = function(self, a, b) 20 return (a + b) * self.Mult 21 end 22 } 23 } 24 25 Calc = { 26 -- 多參數函數 27 New = function (mult, ...) 28 print(...) 29 return setmetatable({Mult = mult}, calc_mt) 30 end 31 } 32 "; 33 // Use this for initialization 34 void Start() 35 { 36 LuaEnv luaenv = new LuaEnv(); 37 Test(luaenv);//調用了帶可變參數的delegate,函數結束都不會釋放delegate,即使置空並調用GC 38 luaenv.Dispose(); 39 } 40 41 void Test(LuaEnv luaenv) 42 { 43 luaenv.DoString(script); 44 // 訪問 lua 函數 45 CalcNew calc_new = luaenv.Global.GetInPath<CalcNew>("Calc.New"); 46 ICalc calc = calc_new(10, "hi", "john"); //constructor 47 Debug.Log("sum(*10) =" + calc.Add(1, 2)); // (1+2)*10 48 calc.Mult = 100; 49 Debug.Log("sum(*100)=" + calc.Add(1, 2)); // (1+2)*100 50 } 51 }
該場景實現了 C# 調用 lua 代碼的函數,table。注意要加上 [CSharpCallLua] 。
五、NoGc
看不懂。
六、Coroutine
總共有四個代碼文件,關鍵代碼如下。
1. CoroutineTest.cs
1 LuaEnv luaenv = null; 2 // Use this for initialization 3 void Start() 4 { 5 luaenv = new LuaEnv(); 6 // 執行 coruntine_test 7 luaenv.DoString("require 'coruntine_test'"); 8 }
2. coruntine_test.lua
1 local util = require 'xlua.util' 2 3 local yield_return = (require 'cs_coroutine').yield_return 4 5 local co = coroutine.create(function() 6 print('coroutine start!') 7 local s = os.time() 8 -- 協程等待3秒 9 yield_return(CS.UnityEngine.WaitForSeconds(3)) 10 print('wait interval:', os.time() - s) 11 12 local www = CS.UnityEngine.WWW('http://www.cnblogs.com/coderJiebao/p/unity3d22.html') 13 -- 協程加載網頁 14 yield_return(www) 15 if not www.error then 16 print(www.bytes) 17 else 18 print('error:', www.error) 19 end 20 end) 21 22 assert(coroutine.resume(co))
3. cs_coroutine.lua
1 local util = require 'xlua.util' 2 3 -- 新建物體 4 local gameobject = CS.UnityEngine.GameObject('Coroutine_Runner') 5 -- 設置不自動銷毀 6 CS.UnityEngine.Object.DontDestroyOnLoad(gameobject) 7 -- 添加組件 8 local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.Coroutine_Runner)) 9 10 local function async_yield_return(to_yield, cb) 11 cs_coroutine_runner:YieldAndCallback(to_yield, cb) -- 調用 C# 函數 12 end 13 14 return { 15 yield_return = util.async_to_sync(async_yield_return) 16 }
4. Coroutine_Runner.cs
1 [LuaCallCSharp] 2 public class Coroutine_Runner : MonoBehaviour 3 { 4 public void YieldAndCallback(object to_yield, Action callback) 5 { 6 // 開啟協程,回調callback 7 StartCoroutine(CoBody(to_yield, callback)); 8 } 9 10 private IEnumerator CoBody(object to_yield, Action callback) 11 { 12 if (to_yield is IEnumerator) 13 yield return StartCoroutine((IEnumerator)to_yield); 14 else 15 yield return to_yield; 16 callback(); 17 } 18 }
該場景實現了協程等待3s和加載網頁的功能。
調用流程為:CoroutineTest.Start -> coruntine_test(創建協程,調用 yield_return 方法)-> cs+coroutine.async_yield_return -> Coroutine_Runner.YieldAndCallback。
七、AsyncTest
繼續看不懂,后期補上。
八、Hotfix
1. 使用方式
(1) 在 github 上下載 xlua 源碼后,將 Asserts 文件夾內的文件以及 Tools 文件夾直接拖到工程,這時候會報錯,刪除 Tools 文件夾下的 System.dll 和 System.core.dll 即可。
(2) 添加 HOTFIX_ENABLE 和 INJECT_WITHOUT_TOOL 兩個宏(在 File->Build Setting->Player Setting->Scripting Define Symbols)
(3) 執行XLua/Generate Code菜單
(4) 編寫代碼,注意在需要熱更新的地方添加[Hotfix]標簽
(5) 注入,構建手機包這個步驟會在構建時自動進行,編輯器下開發補丁需要手動執行"XLua/Hotfix Inject In Editor"菜單。注入成功會打印“hotfix inject finish!”或者“had injected!”。
2. 常用函數
xlua.hotfix(class, [method_name], fix)
- 描述 : 注入lua補丁
- class : C#類,兩種表示方法,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName",字符串格式和C#的Type.GetType要求一致,如果是內嵌類型(Nested Type)是非Public類型的話,只能用字符串方式表示"Namespace.TypeName+NestedTypeName";
- method_name : 方法名,可選;
- fix : 如果傳了method_name,fix將會是一個function,否則通過table提供一組函數。table的組織按key是method_name,value是function的方式。
xlua.private_accessible(class)
- 描述 : 讓一個類的私有字段,屬性,方法等可用
- class : 同xlua.hotfix的class參數
util.hotfix_ex(class, method_name, fix)
- 描述 : xlua.hotfix的增強版本,可以在fix函數里頭執行原來的函數,缺點是fix的執行會略慢。
- method_name : 方法名;
- fix : 用來替換C#方法的lua function。
base(csobj)
- 描述:子類 override 函數通過 base 調用父類實現
- csobj:對象
- 返回值:新對象
3. 打補丁
xlua 可以用 lua 函數替換 C# 的構造函數,函數,屬性,事件的替換。
(1) 函數
可以指定一個函數,也可以傳遞由多個函數組成的 table。
1 -- 注入lua補丁,替換HotfixCalc.Add 2 xlua.hotfix(CS.HotfixCalc, 'Add', function(self, a, b) 3 -- 原來為 a-b 4 return a + b 5 end)
1 -- 通過table提供一組函數 2 -- table的組織按key是methodname,value是function的方式 3 xlua.hotfix(CS.HotfixCalc, { 4 Test1 = function(self) 5 print('Test1', self) 6 return 1 7 end; 8 Test2 = function(self, a, b) 9 print('Test2', self, a, b) 10 return a + 10, 1024, b 11 end; 12 -- static 函數不需要加self 13 Test3 = function(a) 14 print(a) 15 return 10 16 end; 17 Test4 = function(a) 18 print(a) 19 end; 20 -- 多參數 21 Test5 = function(self, a, ...) 22 print('Test4', self, a, ...) 23 end 24 })
(2) 構造函數
構造函數對應的 method_name 是 ".ctor",和普通函數不一樣的是,構造函數的熱補丁並不是替換,而是執行原有邏輯后調用 lua。
1 -- 構造函數 2 ['.ctor'] = function(csobj) 3 return {evt = {}, start = 0} 4 end;
(3) 屬性
每一個屬性都對應一個get,set函數。
1 -- 屬性AProp的賦值和取值 2 set_AProp = function(self, v) 3 print('set_AProp', v) 4 self.AProp = v 5 end; 6 get_AProp = function(self) 7 return self.AProp 8 end;
(4) [] 操作符
賦值對應 set_Item,取值對應 set_Item。
1 -- []操作符,賦值和取值 2 get_Item = function(self, k) 3 print('get_Item', k) 4 return 1024 5 end; 6 set_Item = function(self, k, v) 7 print('set_Item', k, v) 8 end;
對於其他操作符,C#的操作符都有一套內部表示,比如+號的操作符函數名是op_Addition。
(5) 事件
+= 操作符是 add_...,-= 操作符是 remove_... ,函數第一個參數是自身,第二個參數是操作符右邊的 delegate。
1 -- 事件AEvent += 2 add_AEvent = function(self, cb) 3 print('add_AEvent', cb) 4 table.insert(self.evt, cb) 5 end; 6 -- 事件AEvent -= 7 remove_AEvent = function(self, cb) 8 print('remove_AEvent', cb) 9 for i, v in ipairs(self.evt) do 10 if v == cb then 11 table.remove(self.evt, i) 12 break 13 end 14 end 15 end;
(6) 析構函數
函數名是 Finalize,傳一個 self 參數。和普通函數不一樣的是,析構函數的熱補丁並不是替換,而是開頭調用 lua 函數后繼續原有邏輯。
1 -- 析構函數 2 Finalize = function(self) 3 print('Finalize', self) 4 end
(7) 泛化類型
每個泛化類型都是一個獨立的類型,需要對實例化后的類型分別打補丁。
1 xlua.hotfix(CS['GenericClass`1[System.Double]'], { 2 ['.ctor'] = function(obj, a) 3 print('GenericClass<double>', obj, a) 4 end; 5 Func1 = function(obj) 6 print('GenericClass<double>.Func1', obj) 7 end; 8 Func2 = function(obj) 9 print('GenericClass<double>.Func2', obj) 10 return 1314 11 end 12 })
(8) 子類調用父類
1 -- 子類調用父類的方法 2 xlua.hotfix(CS.BaseTest, 'Foo', function(self, p) 3 print('BaseTest', p) 4 base(self):Foo(p) 5 end) 6 xlua.hotfix(CS.BaseTest, 'ToString', function(self) 7 return '>>>' .. base(self):ToString() 8 end)