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)