找回密码
 立即注册
查看: 184|回复: 0

Tolua使用笔记(上)

[复制链接]
发表于 2022-11-3 07:25 | 显示全部楼层 |阅读模式
目录
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.Require LuaState.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 静态事件 #else        Application.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;#else        Application.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 + 1            end            test = {}            test.luaFunc = luaFunc        ";    LuaFunction luaFunc = null;    LuaState lua = null;    string tips = null;                void Start ()     {#if UNITY_5 || UNITY_2017 || UNITY_2018        Application.logMessageReceived += ShowTips;#else        Application.RegisterLogCallback(ShowTips);#endif                lua = 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);            // 方法二 看 CallFunc            num = 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_GC    void OnGUI()    {        GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height / 2 - 150, 400, 300), tips);    }#endif    void OnDestroy()    {        if (luaFunc != null)        {            // 销毁 LuaFunction            luaFunc.Dispose();            luaFunc = null;        }        lua.Dispose();        lua = null;#if UNITY_5 || UNITY_2017 || UNITY_2018        Application.logMessageReceived -= ShowTips;#else        Application.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 = 42            varTable = {1,2,3,4,5}            varTable.default = 1            varTable.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_2018        Application.logMessageReceived += ShowTips;#else        Application.RegisterLogCallback(ShowTips);#endif        LuaState lua = new LuaState();        lua.Start();        // 创建lua虚拟机的全局变量 相当于写上一句 lua 语言: Objs2Spawn = 5        lua["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,但是需要手动 Dispose        func.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 字符串只能是key        table["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);        }        // 对 LuaTable 型的变量,在使用完之后需要手动释放内存        table.Dispose();                   lua.CheckTop();        lua.Dispose();        }    private void OnApplicationQuit()    {#if UNITY_5 || UNITY_2017 || UNITY_2018        Application.logMessageReceived -= ShowTips;#else        Application.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, 1    while n > 0 do        a, b = b, a + b        n = n - 1    end    return aendfunction CoFunc()    print('Coroutine started')    print("current frameCount1: "..Time.frameCount)        for i = 0, 5, 1 do        print(fib(i))        --如果没有下面的 coroutine.wait(0.1) frameCount1 == frameCount2        coroutine.wait(0.1)                                                    end                print("current frameCount2: "..Time.frameCount)        -- 一个 step 跳过一个帧, frameCount2 + 2 = frameCount3        coroutine.step()        coroutine.step()        -- Time 被强制导出为静态类,所以不需要 UnityEngine 了。而WWW需要 UnityEngine.WWW        print("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 = 1        while true do                coroutine.wait(1)                 print("Count: "..c)                c = c + 1        endendfunction 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_2018        Application.logMessageReceived += ShowTips;#else        Application.RegisterLogCallback(ShowTips);#endif                lua  = new LuaState();        lua.Start();        // 注册类到lua中        LuaBinder.Bind(lua);        // 挂上一个组件 LuaLooper 组件, 设置其 luaState 的值         looper = gameObject.AddComponent<LuaLooper>();        looper.luaState = lua;            // 执行 luaFile.text 即string        lua.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_2018        Application.logMessageReceived -= ShowTips;#else        Application.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, 1                while n > 0 do                    a, b = b, a + b                    n = n - 1                end                return a            end            function CoFunc(len)                print('Coroutine started')                                local i = 0                for i = 0, len, 1 do                    -- i = 0 第1次 resume 遇到 yield 返回的是 true 和 0                     -- i = 1 第2次 resume 遇到 yield 返回的是 true 和 1                    local flag = coroutine.yield(fib(i))                                                if not flag then                        break                    end                                                      end                print('Coroutine ended')            end            function Test()                                local co = coroutine.create(CoFunc)                                                return co            end                    ";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_2018        Application.logMessageReceived += ShowTips;#else        Application.RegisterLogCallback(ShowTips);#endif        state = 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_2018        Application.logMessageReceived -= ShowTips;#else        Application.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.Length                for i = 0, len - 1 do                    print('Array: '..tostring(array))                end                -- 2.调用 array:GetEnumerator(), 将其转化成迭代器                local iter = array:GetEnumerator()                -- iter:Current 来获取当前元素                -- iter:MoveNext() 来将迭代器的所指位置移至下一个                while iter:MoveNext() do                    print('iter: '..iter.Current)                end                -- 3.调用 array:ToTable() 实现将数组对象转化为对应的lua中的Table表的形式                local t = array:ToTable()                                for i = 1, #t do                    print('table: '.. tostring(t))                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', true            end                    ";部分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() do                    local v = iter.Current.Value                    print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)                                                end                local flag, account = map:TryGetValue(1, nil)                if flag then                    print('TryGetValue result ok: '..account.name)                end                local keys = map.Keys                iter = keys:GetEnumerator()                print('------------print dictionary keys---------------')                while iter:MoveNext() do                    print(iter.Current)                end                print('----------------------over----------------------')                local values = map.Values                iter = values:GetEnumerator()                print('------------print dictionary values---------------')                while iter:MoveNext() do                    print(iter.Current.name)                end                print('----------------------over----------------------')                                print('kick '..map[2].name)                map:Remove(2)                iter = map:GetEnumerator()                 while iter:MoveNext() do                    local v = iter.Current.Value                    print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)                                                end            end                                ";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.sex        TestAccountWrap.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.Value        System_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 = nil            function TestEnum(e)                        print('Enum is:'..tostring(e))                        if space:ToInt() == 0 then                    print('enum ToInt() is ok')                                end                if not space:Equals(0) then                    print('enum compare int is ok')                                end                if space == e then                    print('enum compare enum is ok')                end                local s = UnityEngine.Space.IntToEnum(0)                if space == s then                    print('IntToEnum change type is ok')                end            end            function ChangeLightType(light, type)                print('change light type to '..tostring(type))                light.type = type            end        ";    LuaState state = null;    void Start ()     {#if UNITY_5 || UNITY_2017 || UNITY_2018        Application.logMessageReceived += ShowTips;#else        Application.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 = type        LuaBinder.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 then                    listener.onClick:Destroy()                end                listener.onClick = DoClick1                     end            function 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 then                    listener.onClick = listener.onClick + DoClick1                                                                    else                    listener.onClick = DoClick1                                      end                            end            function AddClick2(listener)                if listener.onClick then                    listener.onClick = listener.onClick + DoClick2                                      else                    listener.onClick = DoClick2                end                            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 then                    listener.onClick = listener.onClick + TestEventListener.OnClick(t.TestSelffunc, t)                else                    listener.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 + TestEvent            end                        function RemoveEvent(listener)                listener.onClickEvent = listener.onClickEvent - TestEvent            end6.其他

    {        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.Color            local GameObject = UnityEngine.GameObject            local ParticleSystem = UnityEngine.ParticleSystem             function OnComplete()                print('OnComplete CallBack')            end                                               -- 创建了一个游戏物体 挂着一个 ParticleSystem 的组件 游戏物体的位置设置为(1,1,1)            local go = GameObject('go')            go:AddComponent(typeof(ParticleSystem))            local node = go.transform            node.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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 22:39 , Processed in 0.091286 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表