Unity XLua 官方教程学习

一、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,重载时会无法区分。

 

posted @ 2018-04-23 13:09  Just_for_Myself  阅读(14351)  评论(1编辑  收藏  举报