Tolua使用笔记(上)

目录

1.准备工作

2.运行例子

01.HelloWorld:创建和销毁Lua虚拟机

02.ScriptsFromFile:lua文件执行

03.CallLuaFunction:C#调用lua函数

04.AccessingLuaVariables:C#中如何声明,获取,修改lua中的变量

05.LuaCoroutine:Tolua协程用法,在lua中调用C#类型

06.LuaCoroutine2:类unity原生,大量使用效率低

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

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

09.Dictionary:在lua代码中访问C#中的Dictionary对象

10.Enum:在lua代码中访问C#中的枚举类型

11.Delegate:lua文件执行

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中创建设置销毁游戏对象

参考


1.准备工作

下载 LuaFramework_NGUI 

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

2.运行例子

运行每个例子,学习api

01.HelloWorld:创建和销毁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: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[i]);
        }
        // 对 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:Tolua协程用法,在lua中调用C#类型

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 a
end

function 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()
	print("yield frameCount3: "..Time.frameCount)
    -- 待解决疑问:为什么这个UnityEngine必须要要有 上面的Time不需要UnityEngine.Time
	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')
end

function TestCortinue()	
    coroutine.start(CoFunc)
end

local coDelay = nil

function Delay()
	local c = 1

	while true do
		coroutine.wait(1) 
		print("Count: "..c)
		c = c + 1
	end
end

function StartDelay()
	coDelay = coroutine.start(Delay)
end

function StopDelay()
	coroutine.stop(coDelay)
end

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

2.如何给lua注册C#类型,需要在CustomSettings.cs 中 customTypeList添加需要注册的类型。

这个例子,TestLuaCoroutine.lua.bytes 中 用到了

    1) Time.frameCount

    2) UnityEngine.WWW 

所以把这两个类写入到 customTypeList 中,然后再执行编辑器脚本生成对应的wrap文件。(GenLuaWrapFiles)

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:如何获取到原生的lua线程,并在C#端进行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[i]))
                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[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', 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对象

下面是一段用 用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()

    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                        
        ";

 如何实现上述的访问呢,大概把对应的C#类型的方法和变量注册到了LuaState,然后再lua中就可以调用对应的注册进去的函数和变量

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

    //示例方式,方便删除,正常导出无需手写下面代码
    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();
    }

TestAccountWrap 这个类就是通过在 CustomSettings.customTypeList 中把 TestAccount 加入,然后执行编辑器脚本自动生成的 。

    //在这里添加你要导出注册到 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)),
        //-------------------------------------------------------------------       

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文件执行

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
            end

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: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

猜你喜欢

转载自blog.csdn.net/u012138730/article/details/81087645