目录

1.准备工作

2.运行例子

01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。

02.ScriptsFromFile:在C#中,对一个lua文件的执行调用

03.CallLuaFunction:在C#中,对lua函数的操作

04.AccessingLuaVariables:在C#中,对lua变量的操作

05.LuaCoroutine:在Lua中,Tolua重写后的协程语法

06.LuaCoroutine2:另一种协程写法,略。

07.LuaThread:在C#中,获取到原生的lua线程,并进行lua线程的激活操作

08.AccessingArray:在lua中,操作 C#中的数组对象的基本的几种访问方法

09.Dictionary:在lua中,操作 C#中的Dictionary对象以及在lua中暴露C#类型

1. 实现在lua中对于TestAccount类的访问

2.Wrap类的实现

10.Enum:在lua中,操作C#中的枚举类型

11.Delegate:在lua中操作C#中的delegate 以及 在C#中操作lua函数为delegate

1. Lua中对Delegate的赋值:Delegate = lua方法

2. Lua中Delegate = Delegate + 方法

3.C#中DelegateFactory.CreateDelegate (RemoveDelegate)

4.在lua中重载函数和创建委托

5.在lua中 event = event + 方法  event = event - 方法  ,不能直接调用 =

6.其他

12.GameObject:在lua中,操作 GameObject

参考


1.准备工作

下载 LuaFramework_NGUI

新建Unity工程,把Assets下的移动到Unity工程的Assets下。

2.运行例子

运行每个例子,学习api

01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。

1.在C#中执行lua的流程,LuaState对象的创建和销毁。

2.调用LuaState.DoString 执行lua语句

【需要修改:需要在Assets下新建一个Lua文件夹,否则有提示错误信息】

using UnityEngine;
using LuaInterface;// LuaState 类public class HelloWorld : MonoBehaviour
{void Awake(){// 创建一个lua虚拟机// 1. 初始化类LuaState lua = new LuaState();// 2. 开始工作lua.Start();// 写 lua 内容string hello =@"                print('hello tolua#')print(b)";// 执行string。待解决疑问:第二个参数什么意思?去掉执行结果一样lua.DoString(hello, "HelloWorld.cs");// 销毁虚拟机// 1. 进行lua虚拟机栈的判空lua.CheckTop();// 2. 析构掉lua虚拟机lua.Dispose();// 3. 置空lua = null;}
}

02.ScriptsFromFile:在C#中,对一个lua文件的执行调用

1.LuaState.AddSearchPath,进行添加lua文件所在路径。

2.加载lua文件,进行执行的api:LuaState.RequireLuaState.DoFile ,区别:

1)Require 的参数 不需要加.lua 后缀。

2)Require 已经加载过了就不会重新加载了。

3.C# 事件记得+=  -= 成对出现。

【需要修改: string fullPath 路径需要设置下 】

public class ScriptsFromFile : MonoBehaviour
{LuaState lua = null;private string strLog = "";    void Start () {
#if UNITY_5 || UNITY_2017 || UNITY_2018     // Event that is fired if a log message is received.Application.logMessageReceived += Log;// logMessageReceived 静态事件
#elseApplication.RegisterLogCallback(Log);
#endif         lua = new LuaState();                lua.Start();// 通过此方法添加lua文件的路径,只有添加了文件路径之后,在该路径上的lua文件才可以被读取string fullPath = Application.dataPath + "/LuaFramework/ToLua/Examples/02_ScriptsFromFile";lua.AddSearchPath(fullPath);        }void Log(string msg, string stackTrace, LogType type){strLog += msg;strLog += "\r\n";}void OnGUI(){GUI.Label(new Rect(100, Screen.height / 2 - 100, 600, 400), strLog);if (GUI.Button(new Rect(50, 50, 120, 45), "DoFile")){strLog = "";// 每次调用都会重新加载使用// 每次都会执行lua脚本lua.DoFile("ScriptsFromFile.lua");                        }else if (GUI.Button(new Rect(50, 150, 120, 45), "Require")){strLog = "";// 检查该文件是否被加载过,如果被加载过,则直接返回一个索引 // 否则则加载并返回一个索引,不再执行了 lua.Require("ScriptsFromFile");            }// 进行垃圾回收 待解决疑问:为什么上一个例子不用调用 Collect ?lua.Collect();lua.CheckTop();}void OnApplicationQuit(){lua.Dispose();lua = null;
#if UNITY_5 || UNITY_2017 || UNITY_2018 Application.logMessageReceived -= Log;
#elseApplication.RegisterLogCallback(null);
#endif }
}

03.CallLuaFunction:在C#中,对lua函数的操作

1.通过LuaState.GetFunction("lua中的方法名"),初始化一个LuaFunction类型的对象,从而可以执行函数。

2.C#中执行lua函数的4种方法。

一: luaFunc.Invoke

二: luaFunc.BeginPCall(); ……luaFunc.EndPCall();

三: luaFunc.ToDelegate

以上三种方法都是 LuaFunction的接口,下面这个用的 LuaState的接口

四: lua.Invoke

public class CallLuaFunction : MonoBehaviour
{private string script =@"  function luaFunc(num)                        return num + 1endtest = {}test.luaFunc = luaFunc";LuaFunction luaFunc = null;LuaState lua = null;string tips = null;void Start () {
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived += ShowTips;
#elseApplication.RegisterLogCallback(ShowTips);
#endiflua = new LuaState();lua.Start();lua.DoString(script);//Get the function object, 获取lua文件中对应的函数对象,通过这个调用这个函数对象就行执行luaFunc = lua.GetFunction("test.luaFunc");if (luaFunc != null){// 方法一 1个int型输入参数和一个int型返回参数 int num = luaFunc.Invoke<int, int>(123456);Debugger.Log("generic call return: {0}", num);// 方法二 看 CallFuncnum = CallFunc();Debugger.Log("expansion call return: {0}", num);// 方法三 赋值给C#泛型委托Func,进行执行DelegateFactory.Init();Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();num = Func(123456);Debugger.Log("Delegate call return: {0}", num);}// 方法四 不需要 LuaFunction 对象  待解决疑问:这四种方法的优缺点?int num1 = lua.Invoke<int, int>("test.luaFunc", 123456, true);Debugger.Log("luastate call return: {0}", num1);lua.CheckTop();}void ShowTips(string msg, string stackTrace, LogType type){tips += msg;tips += "\r\n";}#if !TEST_GCvoid OnGUI(){GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height / 2 - 150, 400, 300), tips);}
#endifvoid OnDestroy(){if (luaFunc != null){// 销毁 LuaFunctionluaFunc.Dispose();luaFunc = null;}lua.Dispose();lua = null;#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived -= ShowTips;
#elseApplication.RegisterLogCallback(null);
#endif}int CallFunc(){// 跟 luaFunc.Invoke 里很像。luaFunc.BeginPCall();                luaFunc.Push(123456);luaFunc.PCall();        int num = (int)luaFunc.CheckNumber();luaFunc.EndPCall();return num;}
}

04.AccessingLuaVariables:在C#中,对lua变量的操作

1.通过创建一个LuaTable类型的对象,进行对lua表的操作。

1)增加table类型的value: LuaTable.AddTable(key值)

2)获取元表:LuaTable.GetMetaTable()  ,  获取数组 LuaTable.Array()

3) 手动释放内存:  LuaTable.Dispose

2.又一种执行lua函数的方法:LuaFunction.Call方法

3. 如何创建Lua虚拟机的全局变量 

public class AccessingLuaVariables : MonoBehaviour
{private string script =@"print('Objs2Spawn is: '..Objs2Spawn)var2read = 42varTable = {1,2,3,4,5}varTable.default = 1varTable.map = {}varTable.map.name = 'map'varTable[6] = 'forTest'meta = {name = 'meta'}setmetatable(varTable, meta)function TestFunc(strs)print('get func by variable'..strs)end";void Start () {
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived += ShowTips;
#elseApplication.RegisterLogCallback(ShowTips);
#endifLuaState lua = new LuaState();lua.Start();// 创建lua虚拟机的全局变量 相当于写上一句 lua 语言: Objs2Spawn = 5lua["Objs2Spawn"] = 5;lua.DoString(script);// 访问 lua 变量: 通过 LuaState 访问 Debugger.Log("Read var from lua: {0}", lua["var2read"]);Debugger.Log("Read table var from lua: {0}", lua["varTable.default"]);  //LuaState 拆串式table// 另一种获得lua函数对象的方法 通过强制转换 (上一种: lua.GetFunction("方法名");)LuaFunction func = lua["TestFunc"] as LuaFunction;// 另一种最简单的 调用 lua 函数的方法 Call,但是需要手动 Disposefunc.Call(" stringPara");func.Dispose();func = null;// cache成LuaTable进行访问 // 通过调用虚拟机的方法lua.GetTable ,同理另一种强制转换的方法也可以// LuaTable table = lua["varTable"] as LuaTable;LuaTable table = lua.GetTable("varTable");// 通过 LuaTable 获取 value 值Debugger.Log("Read varTable from lua, default: {0} name: {1}", table["default"], table["map.name"]);// 设置 value 值  table 字符串只能是keytable["map.name"] = "new";  Debugger.Log("Modify varTable name: {0}", table["map.name"]);// 添加 key-value对 相当于 varTable.newmap = {}table.AddTable("newmap");// value 是也是一个 Table  LuaTable table1 = (LuaTable)table["newmap"];table1["name"] = "table1";Debugger.Log("varTable.newmap name: {0}", table1["name"]);// 对 LuaTable 型的变量,在使用完之后需要手动释放内存table1.Dispose();// MetaTable 也是 LuaTable 类型table1 = table.GetMetaTable();if (table1 != null){Debugger.Log("varTable metatable name: {0}", table1["name"]);}// 读取 table 的数组 键值是数字的就转化成数组object[] list = table.ToArray();for (int i = 0; i < list.Length; i++){Debugger.Log("varTable[{0}], is {1}", i, list[i]);}// 对 LuaTable 型的变量,在使用完之后需要手动释放内存table.Dispose();   lua.CheckTop();lua.Dispose();}private void OnApplicationQuit(){
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived -= ShowTips;
#elseApplication.RegisterLogCallback(null);
#endif}string tips = null;void ShowTips(string msg, string stackTrace, LogType type){tips += msg;tips += "\r\n";}void OnGUI(){GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);}
}

05.LuaCoroutine:在Lua中,Tolua重写后的协程语法

1. 在lua中调用经过Tolua中重写部分的方法

协程函数的开启:colObj = coroutine.start(CoFunc)   CoFunc是协程函数,colObj是协程对象(类型是thread)

协程函数的延时单位秒:coroutine.wait(0.1)   停0.1s

协程函数的挂起coroutine.step()  停止执行

协程函数的结束coroutine.stop(colObj )    colObj是协程对象  协程函数关闭的协程对象是对应的协程开启函数返回值(colObj = coroutine.start(CoFunc)

协程下载:coroutine.www(www)

function fib(n)local a, b = 0, 1while n > 0 doa, b = b, a + bn = n - 1endreturn a
endfunction CoFunc()print('Coroutine started')print("current frameCount1: "..Time.frameCount)    for i = 0, 5, 1 doprint(fib(i))--如果没有下面的 coroutine.wait(0.1) frameCount1 == frameCount2coroutine.wait(0.1)                      end print("current frameCount2: "..Time.frameCount)-- 一个 step 跳过一个帧, frameCount2 + 2 = frameCount3coroutine.step()coroutine.step()-- Time 被强制导出为静态类,所以不需要 UnityEngine 了。而WWW需要 UnityEngine.WWWprint("yield frameCount3: "..Time.frameCount)local www = UnityEngine.WWW("https://www.baidu.com")coroutine.www(www)local s = tolua.tolstring(www.bytes)print(s:sub(1, 128))print('Coroutine ended')
endfunction TestCortinue()  coroutine.start(CoFunc)
endlocal coDelay = nilfunction Delay()local c = 1while true docoroutine.wait(1) print("Count: "..c)c = c + 1end
endfunction StartDelay()coDelay = coroutine.start(Delay)
endfunction StopDelay()coroutine.stop(coDelay)
end

【需要修改:TestLuaCoroutine.lua.bytes 中的 http://www.baidu.com 改成 https://www.baidu.com】

2.注意到在这个lua中使用C#中的类型,TestLuaCoroutine.lua.bytes:

1) Time.frameCount

2) UnityEngine.WWW 

可以使用的原因是这两个类的Wrap类都被注册到了lua中,查看 LuaBinder.Bind 函数(在Awake函数中调用)可以搜到:

UnityEngine_TimeWrap.Register(L);UnityEngine_WWWWrap.Register(L);

而这两个Wrap类是tolua自动生成的,是在CustomSetting.cs中写了要把哪些类导成Wrap,同样可以搜到如下代码:

_GT(typeof(WWW)),_GT(typeof(Time)),   以及将类Time强制导出为静态类typeof(UnityEngine.Time),

如果自定定义的类需要在lua中使用,也是这么操作,具体例子见09。

3. LuaLooper组件的作用:可以正常的上述Lua脚本中的协程功能了,组件会在c#每一帧驱动lua的协同完成所有的协同功能,这里的协同已经不单单是lua自身功能,而是tolua#模拟unity的所有的功能

public class TestCoroutine : MonoBehaviour
{public TextAsset luaFile = null;private LuaState lua = null;private LuaLooper looper = null;void Awake () {
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived += ShowTips;
#elseApplication.RegisterLogCallback(ShowTips);
#endif        lua  = new LuaState();lua.Start();// 注册类到lua中LuaBinder.Bind(lua);// 挂上一个组件 LuaLooper 组件, 设置其 luaState 的值 looper = gameObject.AddComponent<LuaLooper>();looper.luaState = lua;// 执行 luaFile.text 即stringlua.DoString(luaFile.text, "TestLuaCoroutine.lua");// 执行 lua 函数LuaFunction f = lua.GetFunction("TestCortinue");f.Call();f.Dispose();f = null;        }void OnApplicationQuit(){looper.Destroy();lua.Dispose();lua = null;
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived -= ShowTips;
#elseApplication.RegisterLogCallback(null);
#endif}string tips = null;void ShowTips(string msg, string stackTrace, LogType type){tips += msg;tips += "\r\n";}void OnGUI(){GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);if (GUI.Button(new Rect(50, 50, 120, 45), "Start Counter")){tips = null;// 多次点击 Start Counter 按钮,会相应启动多个协程LuaFunction func = lua.GetFunction("StartDelay");func.Call();func.Dispose();func = null;}else if (GUI.Button(new Rect(50, 150, 120, 45), "Stop Counter")){// 只会清除最后一次启动的协程LuaFunction func = lua.GetFunction("StopDelay");func.Call();func.Dispose();func = null;}else if (GUI.Button(new Rect(50, 250, 120, 45), "GC")){// 手动执行垃圾回收函数 没起到什么效果啊 ?lua.DoString("collectgarbage('collect')", "TestCoroutine.cs");Resources.UnloadUnusedAssets();}}
}

06.LuaCoroutine2:另一种协程写法,略。

因为是对类unity原生的调用,大量使用效率低

C#端的脚本 需要继承 类LuaClient (其继承自 MonoBehaviour

LuaClient中就封装了 05 中所有的那些操作

Lua脚本中用协程的使用方法和C#很相似

//例子5和6展示的两套协同系统勿交叉使用,例子5为推荐方案

07.LuaThread:在C#中,获取到原生的lua线程,并进行lua线程的激活操作

下面是一段用 用string 定义的一段 lua 程序,原生lua协程的语法:

协程创建 coroutine.create

协程挂起 coroutine.yield

对于协程的启动,将再C#中调用。

    string script =@"function fib(n)local a, b = 0, 1while n > 0 doa, b = b, a + bn = n - 1endreturn aendfunction CoFunc(len)print('Coroutine started')                local i = 0for i = 0, len, 1 do-- i = 0 第1次 resume 遇到 yield 返回的是 true 和 0 -- i = 1 第2次 resume 遇到 yield 返回的是 true 和 1local flag = coroutine.yield(fib(i))                     if not flag thenbreakend                                      endprint('Coroutine ended')endfunction Test()                local co = coroutine.create(CoFunc)                                return coend            ";

1. 创建一个LuaThread对象,获取lua中的线程,  在C#端调用LuaThread.Resume()函数进行协程启动

2. 由于设置了 state.LogGC = true; 会多出一些日志:

21:47:50.466-1: Alloc LuaFunction name Test, id 88    【在 state.GetFunction("Test"); 】

21:47:50.470-1: collect lua reference name Test, id 88 in main   【func.Dispose(); 调用Collect

21:48:31.505-175: collect lua reference name LuaThread, id 89 in main 【thread.Resume(true, out ret) 返回0的时候 或者 在thread.Dispose(); 调用Collect

    LuaState state = null;LuaThread thread = null;string tips = null;void Start () {
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived += ShowTips;
#elseApplication.RegisterLogCallback(ShowTips);
#endifstate = new LuaState();state.Start();// 开启记录 gc 的日志, 在 GetLua 和 GetTable 的时候state.LogGC = true;state.DoString(script);// 03中 方法二 lua 函数调用LuaFunction func = state.GetFunction("Test");func.BeginPCall();// 执行 Test() 函数,即创建了一个 协程对象 func.PCall();// 获取返回值, 赋值给 thread, 是一个协程对象thread = func.CheckLuaThread();thread.name = "LuaThread";func.EndPCall();// 会调用 LuaState.Collect()func.Dispose();func = null;// 启动协程thread.Resume(10);}void OnApplicationQuit(){if (thread != null){thread.Dispose();thread = null;}state.Dispose();state = null;
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived -= ShowTips;
#elseApplication.RegisterLogCallback(null);
#endif}void ShowTips(string msg, string stackTrace, LogType type){tips += msg;tips += "\r\n";}// 在对lua线程操作的时候,还要在C#的Update中调用如下代码。待解决疑问:一定要写吗?void Update(){state.CheckTop();state.Collect();}void OnGUI(){GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);if (GUI.Button(new Rect(10, 50, 120, 40), "Resume Thead")){int ret = -1;// 每点一次都是 都是继续启动协程 直到协程结束// 当 Resume 返回 0 的时候,会调用 Dispose ,最终会调用 LuaState.Collect()函数if (thread != null && thread.Resume(true, out ret) == (int)LuaThreadStatus.LUA_YIELD){                Debugger.Log("lua yield: " + ret);}}else if (GUI.Button(new Rect(10, 150, 120, 40), "Close Thread")){if (thread != null){                thread.Dispose();                thread = null;}}}

08.AccessingArray:在lua中,操作 C#中的数组对象的基本的几种访问方法

下面是一段用 用string 定义的一段 lua 程序,介绍了5个lua中操作C#数组的方法

    private string script =@"function TestArray(array)-- 1.在lua中对于C#中的数组,可以直接通过下标索引访问 以及 .Length 获得长度local len = array.Lengthfor i = 0, len - 1 doprint('Array: '..tostring(array[i]))end-- 2.调用 array:GetEnumerator(), 将其转化成迭代器local iter = array:GetEnumerator()-- iter:Current 来获取当前元素-- iter:MoveNext() 来将迭代器的所指位置移至下一个while iter:MoveNext() doprint('iter: '..iter.Current)end-- 3.调用 array:ToTable() 实现将数组对象转化为对应的lua中的Table表的形式local t = array:ToTable()                for i = 1, #t doprint('table: '.. tostring(t[i]))end-- 4. 获取到数组对应位置的元素 array 索引从 0 开始local pos = array:BinarySearch(3)print('array BinarySearch: pos: '..pos..' value: '..array[pos])-- 5. 获取到指定元素的下标的值pos = array:IndexOf(4)print('array index of 4 pos is: '..pos)return 1, '123', trueend            ";

部分C#代码:

        lua = new LuaState();lua.Start();lua.DoString(script, "AccessingArray.cs");tips = "";// 方法一,直接将数组作为参数传入int[] array = { 1, 2, 3, 4, 5};        func = lua.GetFunction("TestArray");func.BeginPCall();func.Push(array);func.PCall();// 获取返回值,必须按顺序写,与lua中的返回值一一对应 double arg1 = func.CheckNumber();string arg2 = func.CheckString();bool arg3 = func.CheckBoolean();Debugger.Log("return is {0} {1} {2}", arg1, arg2, arg3);func.EndPCall();//方法二已过时,调用通用函数需要转换一下类型,避免可变参数拆成多个参数传递object[] objs = func.LazyCall((object)array);if (objs != null){Debugger.Log("return is {0} {1} {2}", objs[0], objs[1], objs[2]);}lua.CheckTop();  

09.Dictionary:在lua中,操作 C#中的Dictionary对象以及在lua中暴露C#类型

下面是一段用 用string 定义的一段 lua 程序 ,在lua代码中访问C#中的Dictionary对象:

// v.id  v.name   v.sex

// map:GetEnumerator()  map:TryGetValue(1, nil) map:Remove(2)
// map.Keys map.Values    map[2]

// iter.Current.Value

// iter = keys:GetEnumerator()

// iter = values:GetEnumerator()

(C#中的NULL在lua中会被识别成nil,这样就方便了lua中对于NULL的检测)

    string script =@"              function TestDict(map)                        local iter = map:GetEnumerator() while iter:MoveNext() dolocal v = iter.Current.Valueprint('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)                                endlocal flag, account = map:TryGetValue(1, nil)if flag thenprint('TryGetValue result ok: '..account.name)endlocal keys = map.Keysiter = keys:GetEnumerator()print('------------print dictionary keys---------------')while iter:MoveNext() doprint(iter.Current)endprint('----------------------over----------------------')local values = map.Valuesiter = values:GetEnumerator()print('------------print dictionary values---------------')while iter:MoveNext() doprint(iter.Current.name)endprint('----------------------over----------------------')                print('kick '..map[2].name)map:Remove(2)iter = map:GetEnumerator() while iter:MoveNext() dolocal v = iter.Current.Valueprint('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)                                endend                        ";

1. 实现在lua中对于TestAccount类的访问

05例子中提过,通常做法

1)需要在CustomSettings.cs 文件中 customTypeList 字段中 添加需要注册的类型(把注释打开(第7-11行))。

(Dictionary<int, TestAccount> map,泛型的Dictionary

    //在这里添加你要导出注册到 lua 的类型列表public static BindType[] customTypeList ={                //------------------------为例子导出--------------------------------//_GT(typeof(TestEventListener)),//_GT(typeof(TestProtol)),//_GT(typeof(TestAccount)),//_GT(typeof(Dictionary<int, TestAccount>)).SetLibName("AccountMap"),//_GT(typeof(KeyValuePair<int, TestAccount>)),//_GT(typeof(Dictionary<int, TestAccount>.KeyCollection)),//_GT(typeof(Dictionary<int, TestAccount>.ValueCollection)),//_GT(typeof(TestExport)),//_GT(typeof(TestExport.Space)),//-------------------------------------------------------------------       

2)执行编辑器脚本生成对应的Wrap.cs文件LuaBinder.cs文件。(Gen LuaWrap + Binder)。

自行查看一下是否生成相应类的Wrap类型 以及 在 LuaBinder.Bind函数 中是否对相应Wap类进行了注册。

3)在C#中调用:

        // 调用 LuaBinder 的静态方法  LuaBinder.Bind(lua)LuaBinder.Bind(lua);

本例做法:略有差异

在这个例子中,不是通过 LuaBinder.Bind 进行调用绑定的,而是通过调用自定义的函数 BindMap的方法。写的内容其实跟 LuaBinder.Bind 函数中一样。(LuaBinder.Bind  通过编辑器脚本 Gen LuaBinder File 生成)

因为本例中只有用了 TestAccout类,没有用 其他的C#类,所以其他类绑定注册也不所谓。

    //示例方式,方便删除,正常导出无需手写下面代码void BindMap(LuaState L){L.BeginModule(null);// v.id  v.name   v.sexTestAccountWrap.Register(L);L.BeginModule("System");L.BeginModule("Collections");L.BeginModule("Generic");// map:GetEnumerator()  map:TryGetValue(1, nil) map:Remove(2)// map.Keys map.Values    map[2]System_Collections_Generic_Dictionary_int_TestAccountWrap.Register(L);// iter.Current.ValueSystem_Collections_Generic_KeyValuePair_int_TestAccountWrap.Register(L);L.BeginModule("Dictionary");// iter = keys:GetEnumerator()System_Collections_Generic_Dictionary_int_TestAccount_KeyCollectionWrap.Register(L);// iter = values:GetEnumerator()System_Collections_Generic_Dictionary_int_TestAccount_ValueCollectionWrap.Register(L);L.EndModule();L.EndModule();L.EndModule();L.EndModule();L.EndModule();}

2.Wrap类的实现

1. Register函数:

这个函数在Bind函数中被调用,

首先注册一个TestAccount类 以及 父类 Object类。

New 为 lua中的函数名,实际执行的内容就是  _CreateTestAccout 。后面几行类似。

……暴露一些列的函数和变量……

然后该类注册结束。

 public static void Register(LuaState L){L.BeginClass(typeof(TestAccount), typeof(System.Object));L.RegFunction("New", _CreateTestAccount);L.RegFunction("__tostring", ToLua.op_ToString);L.RegVar("id", get_id, set_id);L.RegVar("name", get_name, set_name);L.RegVar("sex", get_sex, set_sex);L.EndClass();}

2.具体函数实现:

_CreateTestAccount 首先获取栈的深度,如果数量为3,获取参数,创建对象,并把它压入栈中。否则(栈深度不为3),便扔出异常。(其他函数实现也是类似,很容易看懂)

 [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]static int _CreateTestAccount(IntPtr L){try{int count = LuaDLL.lua_gettop(L);if (count == 3){int arg0 = (int)LuaDLL.luaL_checknumber(L, 1);string arg1 = ToLua.CheckString(L, 2);int arg2 = (int)LuaDLL.luaL_checknumber(L, 3);TestAccount obj = new TestAccount(arg0, arg1, arg2);ToLua.PushSealed(L, obj);return 1;}else{return LuaDLL.luaL_throw(L, "invalid arguments to ctor method: TestAccount.New");}}catch (Exception e){return LuaDLL.toluaL_exception(L, e);}}

10.Enum:在lua中,操作C#中的枚举类型

1. 创建 new LuaState 的时候,会注册进一些System相关的类,包括 System_EnumWrap.Register(this);

2. 通过LuaBinder.Bind(state); 会注册进一些UnityEngine相关的类,包括 UnityEngine_SpaceWrap.Register(L),

UnityEngine_LightWrap.Register(L)

    string script =@"space = nilfunction TestEnum(e)        print('Enum is:'..tostring(e))        if space:ToInt() == 0 thenprint('enum ToInt() is ok')                endif not space:Equals(0) thenprint('enum compare int is ok')                endif space == e thenprint('enum compare enum is ok')endlocal s = UnityEngine.Space.IntToEnum(0)if space == s thenprint('IntToEnum change type is ok')endendfunction ChangeLightType(light, type)print('change light type to '..tostring(type))light.type = typeend";LuaState state = null;void Start () {
#if UNITY_5 || UNITY_2017 || UNITY_2018Application.logMessageReceived += ShowTips;
#elseApplication.RegisterLogCallback(ShowTips);
#endif// 会调用到 System_EnumWrap.Register(this);    所以 lua中可以写 space:ToInt()  space:Equals(0) space == e  tostring(e)state = new LuaState();state.Start();// 会调用到 UnityEngine_SpaceWrap.Register(L); 所以 lua中可以写 UnityEngine.Space.IntToEnum(0)// 会调用到UnityEngine_LightWrap.Register(L) ; 所以 lua中可以写 light.type = typeLuaBinder.Bind(state);state.DoString(script);state["space"] = Space.World;LuaFunction func = state.GetFunction("TestEnum");func.BeginPCall();func.Push(Space.World);func.PCall();func.EndPCall();func.Dispose();        func = null;}

11.Delegate:在lua中操作C#中的delegate 以及 在C#中操作lua函数为delegate

1. Lua中对Delegate的赋值:Delegate = lua方法

lua中的方法赋值给 C#中的定义的委托onClick,从而可以在C#中执行lua方法

首先看lua中的SetClick1函数:

            function SetClick1(listener)if listener.onClick thenlistener.onClick:Destroy()endlistener.onClick = DoClick1         endfunction DoClick1(go)                print('click1 gameObject is '..go.name)                    end

当点击界面的 “ = OnClick1”按钮的时候,把这个 TestEventListener 组件作为参数传入,执行 上述的 SetClick1 lua函数:

为对这个listeneronClick委托赋值为一个 lua方法--DoClick1

        // 1. 直接以TestEventListener组件为参数来调用lua的方法 SetClick1(listener)if (GUI.Button(new Rect(10, 10, 120, 40), " = OnClick1")){// 执行 lua 中的CallLuaFunction(SetClick1);}

再点击界面OnClick的时候, 就会执行lua方法--DoClick1了,输出:

16:31:09.328-926: [LuaState.cs:3]:click1 gameObject is TestDelegate

        else if (GUI.Button(new Rect(10, 360, 120, 40), "OnClick")){if (listener.onClick != null){listener.onClick(gameObject);}else{Debug.Log("empty delegate!!");}}

2. Lua中Delegate = Delegate + 方法

下面两个函数 是为 onClick 绑定额外的方法,相当于C#中的+=操作lua中不支持+=运算符

            function AddClick1(listener)       if listener.onClick thenlistener.onClick = listener.onClick + DoClick1                                                    elselistener.onClick = DoClick1                      end                endfunction AddClick2(listener)if listener.onClick thenlistener.onClick = listener.onClick + DoClick2                      elselistener.onClick = DoClick2end                end

可以通过 点击 “+Click1”  “ + Click2” 按钮, 再点击 OnClick 按钮,查看输出进行验证。

(同理 取消绑定功能就是  Delegate = Delegate - 方法。可以通过 点击 “-Click1”  “ - Click2” 按钮, 再点击 OnClick 按钮,查看输出验证。

3.C#中DelegateFactory.CreateDelegate (RemoveDelegate)

在C#中进行添加lua方法删除绑定的lua方法。

其实就是将lua方法取出到C#中,得到LuaFunction ,然后转换成 TestEventListener.OnClick

两个接口

1)(Delegate类型名)DelegateTraits<Delegate类型名>.Create(LuaFunction变量名)

2)(Delegate类型名)DelegateFactory.CreateDelegate(typeof(Delegate类型名), LuaFunction变量名);

        else if (GUI.Button(new Rect(10, 260, 120, 40), "+ Click1 in C#")){tips = "";LuaFunction func = state.GetFunction("DoClick1");TestEventListener.OnClick onClick = (TestEventListener.OnClick)DelegateTraits<TestEventListener.OnClick>.Create(func);// 或者可以用// TestEventListener.OnClick onClick = (TestEventListener.OnClick)DelegateFactory.CreateDelegate(typeof(TestEventListener.OnClick), func);listener.onClick += onClick;}        else if (GUI.Button(new Rect(10, 310, 120, 40), " - Click1 in C#")){tips = "";LuaFunction func = state.GetFunction("DoClick1");listener.onClick = (TestEventListener.OnClick)DelegateFactory.RemoveDelegate(listener.onClick, func);// 删除以后记得释放func.Dispose();func = null;}

4.在lua中重载函数和创建委托

Tolua#是完全兼容重载函数

在lua中创建委托的写法:

1. 委托类型(方法名) 即创建了一个新的委托变量

            --测试重载问题function TestOverride(listener)listener:SetOnFinished(TestEventListener.OnClick(DoClick1))listener:SetOnFinished(TestEventListener.VoidDelegate(DoClick2))end

2. 委托类型(t.TestSelffunc, t)

            local t = {name = 'byself'}function t:TestSelffunc()print('callback with self: '..self.name)end       function AddSelfClick(listener)if listener.onClick thenlistener.onClick = listener.onClick + TestEventListener.OnClick(t.TestSelffunc, t)elselistener.onClick = TestEventListener.OnClick(t.TestSelffunc, t)end   end  

5.在lua中 event = event + 方法  event = event - 方法  ,不能直接调用 =

            function TestEvent()print('this is a event')end-- 待解决疑问:事件onClickEvent的委托类型 是 void (GameObject) ,而 TestEvent 是 void (),这样可以注册到事件中吗?function AddEvent(listener)listener.onClickEvent = listener.onClickEvent + TestEventendfunction RemoveEvent(listener)listener.onClickEvent = listener.onClickEvent - TestEventend

6.其他

    {state = new LuaState();// 为虚拟机加载一些标准库。state.Start();// 为该虚拟机中加载一些基本的class类型// 之前在CustomSetting中添加并Warp的类型LuaBinder.Bind(state);// 为lua虚拟机加载一些该例子特有的类型,// 平时我们完全可以不通过该方法添加,直接在CustomSetting中添加即可。Bind(state);} void Bind(LuaState L){L.BeginModule(null);TestEventListenerWrap.Register(state);L.EndModule();DelegateFactory.dict.Add(typeof(TestEventListener.OnClick), TestEventListener_OnClick);DelegateFactory.dict.Add(typeof(TestEventListener.VoidDelegate), TestEventListener_VoidDelegate);DelegateTraits<TestEventListener.OnClick>.Init(TestEventListener_OnClick);DelegateTraits<TestEventListener.VoidDelegate>.Init(TestEventListener_VoidDelegate);}

12.GameObject:在lua中,操作 GameObject

GameObject:AddComponent(typeof(Component))  在lua中给游戏物体添加组件

GameObject("游戏物体名")  在lua中创建新的游戏物体

GameObject.Destroy(游戏对象, 延迟时间)  延时销毁的指定的游戏物体

    private string script =@"                                                local Color = UnityEngine.Colorlocal GameObject = UnityEngine.GameObjectlocal ParticleSystem = UnityEngine.ParticleSystem function OnComplete()print('OnComplete CallBack')end                       -- 创建了一个游戏物体 挂着一个 ParticleSystem 的组件 游戏物体的位置设置为(1,1,1)local go = GameObject('go')go:AddComponent(typeof(ParticleSystem))local node = go.transformnode.position = Vector3.one                  print('gameObject is: '..tostring(go))                 --go.transform:DOPath({Vector3.zero, Vector3.one * 10}, 1, DG.Tweening.PathType.Linear, DG.Tweening.PathMode.Full3D, 10, nil)--go.transform:DORotate(Vector3(0,0,360), 2, DG.Tweening.RotateMode.FastBeyond360):OnComplete(OnComplete)            GameObject.Destroy(go, 2)                  go.name = '123'--print('delay destroy gameobject is: '..go.name)                                           ";

参考

https://blog.csdn.net/qq_30168505/article/category/6444876

Tolua使用笔记(上)相关推荐

  1. nodejs学习笔记(上)

    nodejs学习笔记 (上) 通过学习需要做到的是 了解 前后端是如何进行交互的 nodejs也是使用javaScript进行编写的 javaScript在不同的运行环境中有不同的作用 在浏览器内核中 ...

  2. md笔记上传到CSDN---Typora+SMMS+PicGo

    当把md笔记上传到CSDN时,出现图片显示不出来的问题: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传] 1.typora设置 文件->偏好设置->图像 勾选上传服 ...

  3. 学海灯塔新增学习笔记上传功能

    又经过一天的努力,学海灯塔学习笔记上传功能实现.欢迎访问我们的学海灯塔 学习笔记这一模块的功能和课程文件类似,由同学们上传自己的学习笔记,用户可以下载,并且可以对笔记进行打分,后期将增加文件讨论功能. ...

  4. Cris 的 Go 笔记(上)

    Cris 的 Go 笔记(上) Author:Cris 1. Go 的介绍和安装 1.1 Go 语言的特点 Go 语言保证了既能到达静态编译语言的安全和性能,又达到了动态语言开发维护的高效率 ,使用一 ...

  5. Maltab在数学建模中的应用(第二版)——读书笔记上

    Maltab在数学建模中的应用(第二版)--读书笔记上 1.MATLAB与数据文件的交互 1.1数据拟合 1.2数据拟合实例 1.3数据可视化 1.4层次分析法 2.规划问题的MATLAB求解(多约束 ...

  6. 如何将本地的离线笔记上传到gitee上

    如何将本地的离线笔记上传到gitee上 方式1:gitee直接操作 第一步: 第二步: 第三步: 注意:这种方式提交,每个小时之内只能上传10个文件,不好用 方式二:配合git使用更香 第一步:在本地 ...

  7. 软件设计师——设计模式笔记上

    软件设计师--设计模式笔记上(创造型5种) 设计模式的主要目的 设计模式的原则 1.工厂方法模式(类模式) 意图 适用性 代码实现 2.抽象工厂模式(对象模式) 意图 适用性 代码实现 3.生成器模式 ...

  8. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单. 2. 高 ...

  9. 小红书笔记上精选方法技巧有哪些

    其实我们都知道,在写笔记之前一个必须要做的事情就是图片的上传了.所以在写内容之前我们一定要将自己需要发布的图片事先就要准备好,然后就可以来写自己的笔记了.那么要知道在上传的时候是能够上传九张的图片的, ...

  10. 《软技能》读书笔记(上)

    前言 这是一本真正从"人"(而非技术也非管理)的角度关注软件开发人员自身发展的书.书中论述的内容既涉及生活习惯,又包括思维方式,凸显技术中"人"的因素,全面讲解 ...

最新文章

  1. echarts词云图形状_怎么用Python画出好看的词云图?
  2. AFF镜像工具集afflib-tools
  3. mysql group by日期_深入研究mysql中group by与order by取分类最新时间内容
  4. Android Fragment 监听返回键
  5. jQuery中map方法
  6. oracle plsql创建表空间,Oracle在PLSQL Developer上创建表空间和用户脚本 - 龙卷风的日志 - 网易博客...
  7. Win10 监控小软件
  8. 普林斯顿微积分读本07第五章--可导性
  9. C#动态库(dll)查看代码工具(反编译工具)
  10. 数据结构实验:数制转换
  11. ※设计模式※→☆创建型模式☆============Builder模式(五)
  12. 如何将PayPal中的美元以人民币的形式提现到建设银行卡中?
  13. 2020年,技术圈十大“翻车”事件!
  14. 嵌入式软件开发需要学习的知识点
  15. 图像的形状因子计算方法
  16. 计算机无法识别相机,电脑无法读取相机内存卡怎么办_相机内存卡插入电脑读不出来解决教程...
  17. 小鹿课堂:5个迹象看透孩子隐藏的“心理疲劳”
  18. 消息的传输控制拒绝和英达
  19. hive常用函数(一)
  20. 【技术干货】GD32VF103C-START 入门

热门文章

  1. VMware安装统信UOS
  2. 怎样才能提取图片中的文字
  3. 一键获取系统特殊权限 - TrustedInstaller权限,可以直接修改hosts等系统文件
  4. 如何安装nginx并代理下载服务器文件
  5. 新手在Kail Linux中使用pdfcrack 来破解pdf密码
  6. pdf去除密码 html,pdf加密文件如何去除密码?求解密pdf文件的技巧
  7. hbase版本与Hadoop版本支持关系(官方)
  8. AutoRunner 功能自动化测试项目实训之认识自动化测试工具AutoRunner(二)
  9. JavaScript --------WebS APIs学习之本地存储
  10. 文本分类(2)-基于传统机器学习方法进行文本分类