一、Lua 文件加載
1. 執行字符串
1 using UnityEngine; 2 using XLua; 3 4 public class ByString : MonoBehaviour { 5 LuaEnv luaenv = null; 6 // Use this for initialization 7 void Start () { 8 luaenv = new LuaEnv(); 9 // 執行代碼塊,輸出 hello world 10 luaenv.DoString("print('hello world')"); 11 } 12 13 // Update is called once per frame 14 void Update () { 15 if (luaenv != null) 16 { 17 // 清楚 Lua 未手動釋放的 LuaBase 對象 18 luaenv.Tick(); 19 } 20 } 21 22 void OnDestroy() 23 { 24 // 銷毀 25 luaenv.Dispose(); 26 } 27 }
其中 Dostring 函數返回值即為代碼塊里 return 語句的返回值。
2. 加載 Lua 文件
1 luaenv = new LuaEnv(); 2 // 加載 byfile Lua 文件 3 luaenv.DoString("require 'byfile'");
其中 Lua 文件代碼為:
print('hello world')
需要注意的是因為 Resource 只支持有限的后綴,放 Resources 下 lua 文件得加上 txt 后綴,如:byfile.lua.txt。
3. 自定義 Loader
1 void Start() 2 { 3 luaenv = new LuaEnv(); 4 // 自定義 loader 5 luaenv.AddLoader((ref string filename) => { 6 // 若要加載 InMemory 7 if (filename == "InMemory") 8 { 9 string script = "return {ccc = 9999}"; 10 // 將字符串轉換成byte[] 11 return System.Text.Encoding.UTF8.GetBytes(script); 12 } 13 return null; 14 }); 15 // 執行代碼塊,訪問table中的常量ccc 16 luaenv.DoString("print('InMemory.ccc=', require('InMemory').ccc)"); 17 }
通過 Addloader 可以注冊個回調,該回調參數是字符串,返回一個 byte 數組。lua 代碼里調用 require 時,參數就會傳給回調。
注意,require 返回一個由模塊常量和函數組成的table。
二、C# 訪問 Lua
其中 lua 代碼如下:
1 a = 1 2 b = 'hello world' 3 c = true 4 5 d = { 6 f1 = 12, f2 = 34, 7 1, 2, 3, 8 add = function(self, a, b) 9 print('d.add called') 10 return a + b 11 end 12 } 13 14 function e() 15 print('i am e') 16 end 17 18 function f(a, b) 19 print('a', a, 'b', b) 20 return 1, {f1 = 1024} 21 end 22 23 function ret_e() 24 print('ret_e called') 25 return e 26 end
其中包含常量,表和函數。C# 代碼如下:
1 public class DClass 2 { 3 public int f1; // 屬性與Xlua里的對應 4 public int f2; 5 } 6 7 [CSharpCallLua] 8 public interface ItfD 9 { 10 int f1 { get; set; } 11 int f2 { get; set; } 12 int add(int a, int b); 13 } 14 15 // 生成代碼 16 // 若有多個參數,可用 out 屬性接收剩下的返回 17 [CSharpCallLua] 18 public delegate int FDelegate(int a, string b, out DClass c); 19 20 [CSharpCallLua] 21 public delegate Action GetE(); 22 23 // Use this for initialization 24 void Start() 25 { 26 luaenv = new LuaEnv(); 27 luaenv.DoString(script); 28 29 // 訪問全局常量 30 Debug.Log("_G.a = " + luaenv.Global.Get<int>("a")); // 1 31 Debug.Log("_G.b = " + luaenv.Global.Get<string>("b")); // hello world 32 Debug.Log("_G.c = " + luaenv.Global.Get<bool>("c")); // Ture 33 34 // 訪問全局的 table 35 // 映射到普通的class或struct 36 //映射到有對應字段的class,值拷貝,class字段的修改不會影響到table,反之也不會 37 DClass d = luaenv.Global.Get<DClass>("d"); 38 Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}"); // 12 34 39 40 // 映射有 key 的 41 Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");//映射到Dictionary<string, double>,值拷貝 42 Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count); // 12 34 2 43 44 // 映射沒有 key 的 45 List<double> d2 = luaenv.Global.Get<List<double>>("d"); //映射到List<double>,值拷貝 46 Debug.Log("_G.d.len = " + d2.Count); // 3 47 48 // 映射到一個 interface 49 // 要在 interface 定義前加 [CSharpCallLua] 50 ItfD d3 = luaenv.Global.Get<ItfD>("d"); //映射到interface實例,by ref,這個要求interface加到生成列表,否則會返回null,建議用法 51 d3.f2 = 1000; // 外部修改會影響 Lua 內的值 52 Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}"); // 12 1000 53 Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2)); // d.add called 54 55 //映射到LuaTable,by ref 56 LuaTable d4 = luaenv.Global.Get<LuaTable>("d"); 57 Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}"); 58 59 // 訪問一個全局的函數 60 // 映射到 delegate 61 Action e = luaenv.Global.Get<Action>("e");//映射到一個delgate,要求delegate加到生成列表,否則返回null,建議用法 62 e(); // i am e 63 64 FDelegate f = luaenv.Global.Get<FDelegate>("f"); 65 DClass d_ret; 66 // 多值返回,可用out接收多余的參數 67 // 輸出 a 100 b John 68 int f_ret = f(100, "John", out d_ret);//lua的多返回值映射:從左往右映射到c#的輸出參數,輸出參數包括返回值,out參數,ref參數 69 // table只含有常量f1,所以f2賦值為0 70 Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret); // 1024 0 1 71 72 GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更復雜的類型,甚至是另外一個delegate 73 e = ret_e(); 74 e(); 75 76 // 映射到 LuaFunction 77 LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e"); 78 // Call 函數可以傳任意類型,任意個數的參數 79 d_e.Call(); 80 81 }
訪問 lua 全局數據,特別是 table 以及 function,代價比較大,建議盡量少做,比如在初始化時調用獲取一次后,保存下來,后續直接使用即可。
三、Lua 調用 C#
其中 C# 代碼如下:
1 namespace Tutorial 2 { 3 [LuaCallCSharp] 4 public class BaseClass 5 { 6 public static void BSFunc() 7 { 8 Debug.Log("Driven Static Func, BSF = "+ BSF); 9 } 10 11 public static int BSF = 1; 12 13 public void BMFunc() 14 { 15 Debug.Log("Driven Member Func, BMF = " + BMF); 16 } 17 18 public int BMF { get; set; } 19 } 20 21 public struct Param1 22 { 23 public int x; 24 public string y; 25 } 26 27 [LuaCallCSharp] 28 public enum TestEnum 29 { 30 E1, 31 E2 32 } 33 34 [LuaCallCSharp] 35 public class DrivenClass : BaseClass 36 { 37 [LuaCallCSharp] 38 public enum TestEnumInner 39 { 40 E3, 41 E4 42 } 43 44 public void DMFunc() 45 { 46 Debug.Log("Driven Member Func, DMF = " + DMF); 47 } 48 49 public int DMF { get; set; } 50 51 public double ComplexFunc(Param1 p1, ref int p2, out string p3, Action luafunc, out Action csfunc) 52 { 53 Debug.Log("P1 = {x=" + p1.x + ",y=" + p1.y + "},p2 = "+ p2); 54 luafunc(); 55 p2 = p2 * p1.x; 56 p3 = "hello " + p1.y; 57 csfunc = () => 58 { 59 Debug.Log("csharp callback invoked!"); 60 }; 61 return 1.23; 62 } 63 64 public void TestFunc(int i) 65 { 66 Debug.Log("TestFunc(int i)"); 67 } 68 69 public void TestFunc(string i) 70 { 71 Debug.Log("TestFunc(string i)"); 72 } 73 74 public static DrivenClass operator +(DrivenClass a, DrivenClass b) 75 { 76 DrivenClass ret = new DrivenClass(); 77 ret.DMF = a.DMF + b.DMF; 78 return ret; 79 } 80 81 public void DefaultValueFunc(int a = 100, string b = "cccc", string c = null) 82 { 83 UnityEngine.Debug.Log("DefaultValueFunc: a=" + a + ",b=" + b + ",c=" + c); 84 } 85 86 public void VariableParamsFunc(int a, params string[] strs) 87 { 88 UnityEngine.Debug.Log("VariableParamsFunc: a =" + a); 89 foreach (var str in strs) 90 { 91 UnityEngine.Debug.Log("str:" + str); 92 } 93 } 94 95 public TestEnum EnumTestFunc(TestEnum e) 96 { 97 Debug.Log("EnumTestFunc: e=" + e); 98 return TestEnum.E2; 99 } 100 101 public Action<string> TestDelegate = (param) => 102 { 103 Debug.Log("TestDelegate in c#:" + param); 104 }; 105 106 public event Action TestEvent; 107 108 public void CallEvent() 109 { 110 TestEvent(); 111 } 112 113 public ulong TestLong(long n) 114 { 115 return (ulong)(n + 1); 116 } 117 118 class InnerCalc : ICalc 119 { 120 public int add(int a, int b) 121 { 122 return a + b; 123 } 124 125 public int id = 100; 126 } 127 128 public ICalc GetCalc() 129 { 130 return new InnerCalc(); 131 } 132 133 public void GenericMethod<T>() 134 { 135 Debug.Log("GenericMethod<" + typeof(T) + ">"); 136 } 137 } 138 139 [LuaCallCSharp] 140 public interface ICalc 141 { 142 int add(int a, int b); 143 } 144 145 [LuaCallCSharp] 146 public static class DrivenClassExtensions 147 { 148 public static int GetSomeData(this DrivenClass obj) 149 { 150 Debug.Log("GetSomeData ret = " + obj.DMF); 151 return obj.DMF; 152 } 153 154 public static int GetSomeBaseData(this BaseClass obj) 155 { 156 Debug.Log("GetSomeBaseData ret = " + obj.BMF); 157 return obj.BMF; 158 } 159 160 public static void GenericMethodOfString(this DrivenClass obj) 161 { 162 obj.GenericMethod<string>(); 163 } 164 } 165 }
其中可變參數可用 params string[] strs 實現。要在 Lua 直接訪問什么,記得在定義前加上 [LuaCallCSharp]
對應的 lua 代碼為:
1 function demo() 2 -- new C#對象 3 -- 沒有new,所有C#相關的都放在CS下 4 local newGameObj = CS.UnityEngine.GameObject() 5 -- 創建一個名為helloworld的物體 6 local newGameObj2 = CS.UnityEngine.GameObject('helloworld') 7 print(newGameObj, newGameObj2) 8 9 --訪問靜態屬性,方法 10 local GameObject = CS.UnityEngine.GameObject 11 print('UnityEngine.Time.deltaTime:', CS.UnityEngine.Time.deltaTime) --讀靜態屬性 12 CS.UnityEngine.Time.timeScale = 0.5 --寫靜態屬性 13 -- 查找物體 helloworld 14 print('helloworld', GameObject.Find('helloworld')) --靜態方法調用 15 16 --訪問成員屬性,方法 17 local DrivenClass = CS.Tutorial.DrivenClass 18 local testobj = DrivenClass() 19 testobj.DMF = 1024--設置成員屬性 20 print(testobj.DMF)--讀取成員屬性 21 -- 輸出 DMF=1024 22 testobj:DMFunc()--成員方法 使用冒號 23 24 --基類屬性,方法 25 print(DrivenClass.BSF)--讀基類靜態屬性 1 26 DrivenClass.BSF = 2048--寫基類靜態屬性 27 DrivenClass.BSFunc();--基類靜態方法 2048 28 -- BMF 初始為0 29 print(testobj.BMF)--讀基類成員屬性 0 30 testobj.BMF = 4096--寫基類成員屬性 31 testobj:BMFunc()--基類方法調用 4096 32 33 --復雜方法調用 34 -- 參數處理規則:C#的普通參數和ref修飾的算一個參數,out不算,從左往右順序 35 -- 返回值處理規則:返回值(如果有)算第一個,out,ref修飾的參數算一個,從左往右 36 local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = 'john'}, 100, function() 37 print('i am lua callback') 38 end) 39 print('ComplexFunc ret:', ret, p2, p3, csfunc) 40 csfunc() 41 42 --重載方法調用 43 testobj:TestFunc(100) 44 testobj:TestFunc('hello') 45 46 --操作符 47 local testobj2 = DrivenClass() 48 testobj2.DMF = 2048 49 -- 輸出DMF=1024+2048=3072 50 print('(testobj + testobj2).DMF = ', (testobj + testobj2).DMF) 51 52 --默認值 53 testobj:DefaultValueFunc(1) 54 testobj:DefaultValueFunc(3, 'hello', 'john') 55 56 --可變參數 57 testobj:VariableParamsFunc(5, 'hello', 'john') 58 59 --Extension methods 60 print(testobj:GetSomeData()) 61 print(testobj:GetSomeBaseData()) --訪問基類的Extension methods 62 testobj:GenericMethodOfString() --通過Extension methods實現訪問泛化方法 63 64 --枚舉類型 65 -- 返回E2 66 local e = testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1) 67 -- 輸出枚舉類型格式為 E2:1 68 print(e, e == CS.Tutorial.TestEnum.E2) 69 -- 整數或者字符串到枚舉類型的轉換 70 print(CS.Tutorial.TestEnum.__CastFrom(1), CS.Tutorial.TestEnum.__CastFrom('E1')) 71 print(CS.Tutorial.DrivenClass.TestEnumInner.E3) 72 assert(CS.Tutorial.BaseClass.TestEnumInner == nil) 73 74 --委托 75 testobj.TestDelegate('hello') --直接調用 76 local function lua_delegate(str) 77 print('TestDelegate in lua:', str) 78 end 79 testobj.TestDelegate = lua_delegate + testobj.TestDelegate --combine,這里演示的是C#delegate作為右值,左值也支持 80 testobj.TestDelegate('hello') 81 testobj.TestDelegate = testobj.TestDelegate - lua_delegate --remove 82 testobj.TestDelegate('hello') 83 84 --事件 85 local function lua_event_callback1() print('lua_event_callback1') end 86 local function lua_event_callback2() print('lua_event_callback2') end 87 -- 增加回調事件 88 testobj:TestEvent('+', lua_event_callback1) 89 testobj:CallEvent() 90 testobj:TestEvent('+', lua_event_callback2) 91 testobj:CallEvent() 92 -- 移除回調事件 93 testobj:TestEvent('-', lua_event_callback1) 94 testobj:CallEvent() 95 testobj:TestEvent('-', lua_event_callback2) 96 97 --64位支持 98 local l = testobj:TestLong(11) 99 print(type(l), l, l + 100, 10000 + l) 100 101 --typeof 102 -- 增加粒子系統 103 newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem)) 104 105 --cast 強轉 106 -- 返回 InnerCalc 類 107 local calc = testobj:GetCalc() 108 print('assess instance of InnerCalc via reflection', calc:add(1, 2)) 109 assert(calc.id == 100) 110 -- 強轉 111 cast(calc, typeof(CS.Tutorial.ICalc)) 112 print('cast to interface ICalc', calc:add(1, 2)) 113 assert(calc.id == nil) 114 end 115 116 demo() 117 118 --協程下使用 119 local co = coroutine.create(function() 120 print('------------------------------------------------------') 121 demo() 122 end) 123 assert(coroutine.resume(co))
其中 assert 函數用於有錯誤時拋出異常。
注意,C# 的 int, float, double 都對應於 lua 的 number,重載時會無法區分。