02_U3DScripting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;

namespace XLuaTest
{
[System.Serializable]
public class Injection
{
public string name;
public GameObject value;
}

[LuaCallCSharp]
public class LuaBehaviour : MonoBehaviour
{
public TextAsset luaScript;
public Injection[] injections;

internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only!
internal static float lastGCTime = 0;
internal const float GCInterval = 1;//1 second

private Action luaStart;
private Action luaUpdate;
private Action luaOnDestroy;

private LuaTable scriptEnv;

void Awake()
{
scriptEnv = luaEnv.NewTable();

// 为每个脚本设置一个独立的环境,可一定程度上防止脚本间全局变量、函数冲突
LuaTable meta = luaEnv.NewTable();
meta.Set("__index", luaEnv.Global);
scriptEnv.SetMetaTable(meta);
meta.Dispose();

scriptEnv.Set("self", this);
foreach (var injection in injections)
{
scriptEnv.Set(injection.name, injection.value);
}

//执行一个代码块。
//object[] DoString(string chunk, string chunkName = "chuck", LuaTable env = null)
//chunk: Lua代码的字符串;
//chunkName: 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
//env :这个代码块的环境变量;
luaEnv.DoString(luaScript.text, "LuaTestScript", scriptEnv);

Action luaAwake = scriptEnv.Get<Action>("awake");
scriptEnv.Get("start", out luaStart);
scriptEnv.Get("update", out luaUpdate);
scriptEnv.Get("ondestroy", out luaOnDestroy);

if (luaAwake != null)
{
luaAwake();
}
}

// Use this for initialization
void Start()
{
if (luaStart != null)
{
luaStart();
}
}

// Update is called once per frame
void Update()
{
if (luaUpdate != null)
{
luaUpdate();
}
if (Time.time - LuaBehaviour.lastGCTime > GCInterval)
{
luaEnv.Tick();
LuaBehaviour.lastGCTime = Time.time;
}
}

void OnDestroy()
{
if (luaOnDestroy != null)
{
luaOnDestroy();
}
luaOnDestroy = null;
luaUpdate = null;
luaStart = null;
scriptEnv.Dispose();
injections = null;
}
}
}

scriptEnv.Set(injection.name, injection.value)会按键值对的方式,将value塞入lua中。
在lua中可以使用name来获取到value,如:print(“injected object”, lightObject)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local speed = 10
local lightCpnt = nil

function start()
print("lua start...")
print("injected object", lightObject)
lightCpnt= lightObject:GetComponent(typeof(CS.UnityEngine.Light))
end

function update()
local r = CS.UnityEngine.Vector3.up * CS.UnityEngine.Time.deltaTime * speed
self.transform:Rotate(r)
lightCpnt.color = CS.UnityEngine.Color(CS.UnityEngine.Mathf.Sin(CS.UnityEngine.Time.time) / 2 + 0.5, 0, 0, 1)
end

function ondestroy()
print("lua destroy")
end

03_UIEvent

1
2
3
4
5
6
7
function start()
print("lua start...")

self:GetComponent("Button").onClick:AddListener(function()
print("clicked, you input is '" ..input:GetComponent("InputField").text .."'")
end)
end

04_LuaObjectOrented

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class InvokeTest : MonoBehaviour
{
[CSharpCallLua]
public interface ICalc
{
int Add(int a,int b);
int Mult { get; set; }
}

[CSharpCallLua]
public delegate ICalc CalcNew(int mult, params string[] args);

private string script = @"local calc_mt = {
__index = {
Add = function(self,a,b)
return (a+b)*self.Mult
end
}
}

Calc = {
--多参数函数
New = function(mult,...)
print(...)
return setmetatable({Mult = mult},calc_mt)
end
}";

private void Start()
{
LuaEnv luaEnv = new LuaEnv();
Test(luaEnv);//调用了带可变参数的delegate,函数结束都不会释放delegate,即使置空并调用GC
luaEnv.Dispose();
}

private void Test(LuaEnv luaEnv)
{
luaEnv.DoString(script);
//访问lua函数
//和Get的区别是,这个函数会识别path里头的“.”,比如var i = tbl.GetInPath<int>(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c,
//避免仅为了获取中间变量而多次调用Get,执行效率更高。
CalcNew calc_new = luaEnv.Global.GetInPath<CalcNew>("Calc.New");
ICalc calc = calc_new(10,"hi","AFool");//constructor
Debug.Log("sum(*10) =" + calc.Add(1, 2)); // (1+2)*10
calc.Mult = 100;
Debug.Log("sum(*100)=" + calc.Add(1, 2)); // (1+2)*100
}

拓展 lua的__index

1
2
3
4
5
6
7
8
9
10
11
12
13
local t = {
name = "AFool",
}
print(t.money);
--在使用加法操作时,会查找__add元方法,那么,在调用table不存在的字段时,会调用__index元方法,这是一样的规则。
local mt = {
__index = function(table,key)
print("table中不存在的键"..key);
end
}
--设置一个自定义的元表
setmetatable(t,mt);
print(t.money)

输出结果为:
nil
table中不存在的键money
nil

利用__index模拟继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local man = {
name = "normalMan",
age = 0,
money = 0,

sayHello = function()
print("sayHello")
end
}

local richMan = {
name = "richMan",
sayHello = function()
print("richMan never sayHello")
end
};
local newMan = {};

local mt = {__index = man};

setmetatable(richMan,mt);
setmetatable(newMan,mt);

print(richMan.name);
print(newMan.name);

richMan.sayHello();
newMan.sayHello();

输出结果为:
richMan
normalMan
richMan never sayHello
sayHello

05_NoGc

没有GC的调用方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
namespace XLuaTest
{
[GCOptimize]
[LuaCallCSharp]
public struct Pedding
{
public byte c;
}

[GCOptimize]
[LuaCallCSharp]
public struct MyStruct
{
public MyStruct(int p1, int p2)
{
a = p1;
b = p2;
c = p2;
e.c = (byte)p1;
}
public int a;
public int b;
public decimal c;
public Pedding e;
}

[LuaCallCSharp]
public enum MyEnum
{
E1,
E2
}

[CSharpCallLua]
public delegate int IntParam(int p);

[CSharpCallLua]
public delegate Vector3 Vector3Param(Vector3 p);

[CSharpCallLua]
public delegate MyStruct CustomValueTypeParam(MyStruct p);

[CSharpCallLua]
public delegate MyEnum EnumParam(MyEnum p);

[CSharpCallLua]
public delegate decimal DecimalParam(decimal p);

[CSharpCallLua]
public delegate void ArrayAccess(Array arr);

[CSharpCallLua]
public interface IExchanger
{
void exchange(Array arr);
}

[LuaCallCSharp]
public class NoGc : MonoBehaviour
{
LuaEnv luaenv = new LuaEnv();

IntParam f1;
Vector3Param f2;
CustomValueTypeParam f3;
EnumParam f4;
DecimalParam f5;

ArrayAccess farr;
Action flua;
IExchanger ie;
LuaFunction add;

[NonSerialized]
public double[] a1 = new double[] { 1, 2 };
[NonSerialized]
public Vector3[] a2 = new Vector3[] { new Vector3(1, 2, 3), new Vector3(4, 5, 6) };
[NonSerialized]
public MyStruct[] a3 = new MyStruct[] { new MyStruct(1, 2), new MyStruct(3, 4) };
[NonSerialized]
public MyEnum[] a4 = new MyEnum[] { MyEnum.E1, MyEnum.E2 };
[NonSerialized]
public decimal[] a5 = new decimal[] { 1.00001M, 2.00002M };

public float FloatParamMethod(float p)
{
return p;
}

public Vector3 Vector3ParamMethod(Vector3 p)
{
return p;
}

public MyStruct StructParamMethod(MyStruct p)
{
return p;
}

public MyEnum EnumParamMethod(MyEnum p)
{
return p;
}

public decimal DecimalParamMethod(decimal p)
{
return p;
}

// Use this for initialization
void Start()
{
luaenv.DoString(@"
function id(...)
return ...
end

function add(a, b)
return a + b
end

function array_exchange(arr)
arr[0], arr[1] = arr[1], arr[0]
end

local v3 = CS.UnityEngine.Vector3(7, 8, 9)
local vt = CS.XLuaTest.MyStruct(5, 6)

function lua_access_csharp()
monoBehaviour:FloatParamMethod(123) --primitive
monoBehaviour:Vector3ParamMethod(v3) --vector3
local rnd = math.random(1, 100)
local r = monoBehaviour:Vector3ParamMethod({x = 1, y = 2, z = rnd}) --vector3
assert(r.x == 1 and r.y == 2 and r.z == rnd)
monoBehaviour:StructParamMethod(vt) --custom struct
r = monoBehaviour:StructParamMethod({a = 1, b = rnd, e = {c = rnd}})
assert(r.b == rnd and r.e.c == rnd)
monoBehaviour:EnumParamMethod(CS.XLuaTest.MyEnum.E2) --enum
monoBehaviour:DecimalParamMethod(monoBehaviour.a5[0])
monoBehaviour.a1[0], monoBehaviour.a1[1] = monoBehaviour.a1[1], monoBehaviour.a1[0] -- field
end

exchanger = {
exchange = function(self, arr)
array_exchange(arr)
end
}

A = { B = { C = 789}}
GDATA = 1234;
");

luaenv.Global.Set("monoBehaviour", this);

luaenv.Global.Get("id", out f1);
luaenv.Global.Get("id", out f2);
luaenv.Global.Get("id", out f3);
luaenv.Global.Get("id", out f4);
luaenv.Global.Get("id", out f5);

// c# call lua function with value type but no gc (using delegate)
Debug.Log(f1(1)); // primitive type
Debug.Log(f2(new Vector3(1, 2, 3))); // vector3
MyStruct mystruct1 = new MyStruct(5, 6);
Debug.Log(f3(mystruct1)); // custom complex value type
Debug.Log(f4(MyEnum.E1)); //enum
decimal dec1 = -32132143143100109.00010001010M;
Debug.Log(f5(dec1)); //decimal

luaenv.Global.Get("array_exchange", out farr);

// lua access c# value type array no gc
farr(a1); //primitive value type array
Debug.Log(a1[0]); Debug.Log(a1[1]);
farr(a2); //vector3 array
Debug.Log(a2[0]); Debug.Log(a2[1]);
farr(a3); //custom struct array
Debug.Log(a3[0]); Debug.Log(a3[1]);
farr(a4); //enum arry
Debug.Log(a4[0]); Debug.Log(a4[1]);
farr(a5); //decimal arry
Debug.Log(a5[0]); Debug.Log(a5[1]);

luaenv.Global.Get("lua_access_csharp", out flua);
luaenv.Global.Get("exchanger", out ie);
luaenv.Global.Get("add", out add);



luaenv.Global.Set("g_int", 123);
luaenv.Global.Set(123, 456);
int i;
luaenv.Global.Get("g_int", out i);
Debug.Log("g_int:" + i);
luaenv.Global.Get(123, out i);
Debug.Log("123:" + i);
}


void Update()
{
MyStruct mystruct1 = new MyStruct(5, 6);
decimal dec1 = -32132143143100109.00010001010M;

// using LuaFunction.Func<T1, T2, TResult>
add.Func<int, int, int>(34, 56); // LuaFunction.Func<T1, T2, TResult>


// lua call c# no gc with value type
flua();

//c# call lua using interface
ie.exchange(a2);

//no gc LuaTable use
luaenv.Global.Set("g_int", 456);
int i;
luaenv.Global.Get("g_int", out i);

luaenv.Global.Set(123.0001, mystruct1);
MyStruct mystruct2;
luaenv.Global.Get(123.0001, out mystruct2);

decimal dec2 = 0.0000001M;
luaenv.Global.Set((byte)12, dec1);
luaenv.Global.Get((byte)12, out dec2);

int gdata = luaenv.Global.Get<int>("GDATA");
luaenv.Global.SetInPath("GDATA", gdata + 1);

int abc = luaenv.Global.GetInPath<int>("A.B.C");
luaenv.Global.SetInPath("A.B.C", abc + 1);

luaenv.Tick();
}

void OnDestroy()
{
f1 = null;
f2 = null;
f3 = null;
f4 = null;
f5 = null;
farr = null;
flua = null;
ie = null;
add = null;
luaenv.Dispose();
}
}

06_Coroutine

协程的使用。

C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace XLuaTest
{
public class CoroutineTest : MonoBehaviour
{
LuaEnv luaenv = null;
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("require 'coruntine_test'");
}

// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}

void OnDestroy()
{
luaenv.Dispose();
}
}
}

Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
--cs_coroutine.lua
local util = require 'xlua.util'

local gameobject = CS.UnityEngine.GameObject('Coroutine_Runner')
CS.UnityEngine.Object.DontDestroyOnLoad(gameobject)
local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.XLuaTest.Coroutine_Runner))

return {
start = function(...)
return cs_coroutine_runner:StartCoroutine(util.cs_generator(...))
end;

stop = function(coroutine)
cs_coroutine_runner:StopCoroutine(coroutine)
end
}

--------------------------------------------------------
--coruntine_test.lua
local cs_coroutine = (require 'cs_coroutine')

local a = cs_coroutine.start(function()
print('coroutine a started')

coroutine.yield(cs_coroutine.start(function()
print('coroutine b stated inside cotoutine a')
coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
print('i am coroutine b')
end))
print('coroutine b finish')

while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
print('i am coroutine a')
end
end)

cs_coroutine.start(function()
print('stop coroutine a after 5 seconds')
coroutine.yield(CS.UnityEngine.WaitForSeconds(5))
cs_coroutine.stop(a)
print('coroutine a stoped')
end)

07_AsyncTest

异步的使用。

C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AsyncTest : MonoBehaviour
{
LuaEnv luaenv = null;

void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("require 'async_test'");
}

// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
namespace XLuaTest
{
public class MessageBox : MonoBehaviour
{

public static void ShowAlertBox(string message, string title, Action onFinished = null)
{
var alertPanel = GameObject.Find("Canvas").transform.Find("AlertBox");
if (alertPanel == null)
{
alertPanel = (Instantiate(Resources.Load("AlertBox")) as GameObject).transform;
alertPanel.gameObject.name = "AlertBox";
alertPanel.SetParent(GameObject.Find("Canvas").transform);
alertPanel.localPosition = new Vector3(-6f, -6f, 0f);
}

alertPanel.Find("title").GetComponent<Text>().text = title;
alertPanel.Find("message").GetComponent<Text>().text = message;

var button = alertPanel.Find("alertBtn").GetComponent<Button>();
UnityAction onclick = () =>
{
if (onFinished != null)
{
onFinished();
}
button.onClick.RemoveAllListeners();
alertPanel.gameObject.SetActive(false);
};
//防止消息框未关闭时多次被调用
button.onClick.RemoveAllListeners();
button.onClick.AddListener(onclick);
alertPanel.gameObject.SetActive(true);
}

public static void ShowConfirmBox(string message, string title, Action<bool> onFinished = null)
{
var confirmPanel = GameObject.Find("Canvas").transform.Find("ConfirmBox");
if (confirmPanel == null)
{
confirmPanel = (Instantiate(Resources.Load("ConfirmBox")) as GameObject).transform;
confirmPanel.gameObject.name = "ConfirmBox";
confirmPanel.SetParent(GameObject.Find("Canvas").transform);
confirmPanel.localPosition = new Vector3(-8f, -18f, 0f);
}

confirmPanel.Find("confirmTitle").GetComponent<Text>().text = title;
confirmPanel.Find("conmessage").GetComponent<Text>().text = message;

var confirmBtn = confirmPanel.Find("confirmBtn").GetComponent<Button>();
var cancelBtn = confirmPanel.Find("cancelBtn").GetComponent<Button>();
Action cleanup = () =>
{
confirmBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.RemoveAllListeners();
confirmPanel.gameObject.SetActive(false);
};

UnityAction onconfirm = () =>
{
if (onFinished != null)
{
onFinished(true);
}
cleanup();
};

UnityAction oncancel = () =>
{
if (onFinished != null)
{
onFinished(false);
}
cleanup();
};

//防止消息框未关闭时多次被调用
confirmBtn.onClick.RemoveAllListeners();
confirmBtn.onClick.AddListener(onconfirm);
cancelBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.AddListener(oncancel);
confirmPanel.gameObject.SetActive(true);
}
}

public static class MessageBoxConfig
{
[CSharpCallLua]
public static List<Type> CSharpCallLua = new List<Type>()
{
typeof(Action),
typeof(Action<bool>),
typeof(UnityAction),
};
}
}

Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
-------------------------async_test.lua-----------------------------
local util = require 'xlua.util'
local message_box = require 'message_box'

-------------------------async_recharge-----------------------------
local function async_recharge(num, cb) --模拟的异步充值
print('requst server...')
cb(true, num)
end

local recharge = util.async_to_sync(async_recharge)
-------------------------async_recharge end----------------------------
local buy = function()
message_box.alert("您余额不足,请充值!", "余额提醒")
if message_box.confirm("确认充值10元吗?", "确认框") then
local r1, r2 = recharge(10)
print('recharge result:', r1, r2)
message_box.alert("充值成功!", "提示")
else
print('cancel')
message_box.alert("取消充值!", "提示")
end
print('recharge finished')
end
--将按钮监听点击事件,绑定buy方法
CS.UnityEngine.GameObject.Find("Button"):GetComponent("Button").onClick:AddListener(util.coroutine_call(buy))

-------------------------message_box.lua-----------------------------
local util = require 'xlua.util'

local sync_alert = util.async_to_sync(CS.XLuaTest.MessageBox.ShowAlertBox)
local sync_confirm = util.async_to_sync(CS.XLuaTest.MessageBox.ShowConfirmBox)

--构造alert和confirm函数
return {
alert = function(message, title)
sync_alert(message, title)
end;

confirm = function(message, title)
local ret = sync_confirm(message, title)
return ret == true
end;
}

08_Hotfix

一个坑,tools要放在assets同级目录。蛋疼!也可以自行修改!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
namespace XLuaTest
{
[Hotfix]
public class HotfixTest : MonoBehaviour
{
LuaEnv luaenv = new LuaEnv();

private int tick = 0;

// Use this for initialization
void Start()
{
}

// Update is called once per frame
void Update()
{
if (++tick % 50 == 0)
{
Debug.Log(">>>>>>>>Update in C#, tick = " + tick);
}
}

void OnGUI()
{
if (GUI.Button(new Rect(10, 10, 300, 80), "Hotfix"))
{
luaenv.DoString(@"
xlua.hotfix(CS.XLuaTest.HotfixTest, 'Update', function(self)
self.tick = self.tick + 1
if (self.tick % 50) == 0 then
print('<<<<<<<<Update in lua, tick = ' .. self.tick)
end
end)
");
}

string chHint = @"在运行该示例之前,请细致阅读xLua文档,并执行以下步骤:

1.宏定义:添加 HOTFIX_ENABLE 到 'Edit > Project Settings > Player > Other Settings > Scripting Define Symbols'。
(注意:各平台需要分别设置)

2.生成代码:执行 'XLua > Generate Code' 菜单,等待Unity编译完成。

3.注入:执行 'XLua > Hotfix Inject In Editor' 菜单。注入成功会打印 'hotfix inject finish!' 或者 'had injected!' 。";
string enHint = @"Read documents carefully before you run this example, then follow the steps below:

1. Define: Add 'HOTFIX_ENABLE' to 'Edit > Project Settings > Player > Other Settings > Scripting Define Symbols'.
(Note: Each platform needs to set this respectively)

2.Generate Code: Execute menu 'XLua > Generate Code', wait for Unity's compilation.


3.Inject: Execute menu 'XLua > Hotfix Inject In Editor'.There should be 'hotfix inject finish!' or 'had injected!' print in the Console if the Injection is successful.";
GUIStyle style = GUI.skin.textArea;
style.normal.textColor = Color.red;
style.fontSize = 16;
GUI.TextArea(new Rect(10, 100, 500, 290), chHint, style);
GUI.TextArea(new Rect(10, 400, 500, 290), enHint, style);
}
}
}