ToLua框架使用 下载和安装 从Github拉取ToLua 框架代码。Github地址:https://github.com/topameng/tolua
可以看到下载拉下来的目录结构如下:
创建一个空的Unity工程,然后把Assets目录里的内容复制到Demo工程的Assets目录下,会弹出下面的提示框:
因为是第一次尝试,所以我选择取消,为的是能尽可能熟悉这个菜单下面的过程。
在Source/Generate
目录下,有两个文件,一个是DelegateFactory
,另一个是LuaBinder
,暂时不知道有什么用,先保留。
生成Wrap Files 先来看菜单Lua/Gen Lua Wrap Files
,这个步骤的代码实现如下:
通过阅读这段代码,和它所调用的函数,可以知道这个步骤从CustomSettings.customTypeList
中获得要导出的类型,如果有自定义的类型需要导出,需要在这里手动添加。GenBindTypes
函数是对typeList
进行过滤,过滤掉以下几种类型:
重复的类型(出现重复会抛异常,中断流程)
在ToLuaMenu.dropType
中添加的类型(不需要的类型)
在ToLuaMenu.baseType
中添加的类型(自定义的list不需要添加,下一行代码会加回来)
整理完之后,会把整理的结果丢到ToLuaExport.allTypes
里。然后逐个导出,最后清理。默认的保存路径定义在CustomSettings.saveDir
这个变量内,为Assets/Source/Generate
点击生成,可以看到Assets/Source/Generate
下生成了很多C#的Wrap
代码。
生成Lua Delegates 除了导出的类型,还需要导出Delegate
,导出列表定义在CustomSettings.customDelegatesList
中,并且从CustomSettings.customTypeList
中整理出方法,函数和定义的内部Delegate
等等一并导出。
最后归并到一个集合内,批量导出。最后将结果写到Source/Generate/DelegateFactory.cs
中:
生成 Lua Binder 生成Lua Binder的过程比较复杂,总的来说也是根据先前导出的类型,构建命名空间树,然后生成Binder,将结果写到Source/Generate/LuaBinder.cs
中。
从下图可以看到,LuaBinder就是将类型注册到LuaState
中。
至此生成完毕,Generate All
的代码,就是把这三个步骤按顺序调用,不过不一定是我写的顺序,可以看看代码:
使用例子说明 在ToLua中,已经给出了一些使用示例,通过阅读这些代码,可以很好熟悉ToLua的使用,下面我就捡一些主要的样例来逐个说明。
从C#中调用Lua 在C#中执行Lua代码,是通过LuaState
类来进行的,在使用之前,需要先调用LuaState
的Start
方法,然后通过DoFile
来导入指定的Lua文件中的代码,再使用Call方法来调用指定的函数,C#这一边的示例如下:
它执行的是Assets/Lua/Main.lua
文件,改文件的内容如下:
LuaState默认的SearchPath
就是Assets/Lua
,所以这里不用加路径也可以找到Main
设置和访问Lua中定义的变量 在这里通过DoString
把样例代码加载到LuaState
中,然后就可以像字典一样设置和访问里面的变量、表,以及访问定义的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 using LuaInterface;using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameManager : MonoBehaviour { LuaState state; private string script = @" print('Objs2Spawn is: '..Objs2Spawn) var2read = 42 varTable = {1,2,3,4,5} varTable.default = 1 varTable.map = {} varTable.map.name = 'map' meta = {name = 'meta'} setmetatable(varTable, meta) function TestFunc(strs) print('get func by variable') end " ; void Start () { state = new LuaState(); state.Start(); state["Objs2Spawn" ] = 5 ; state.DoString(script); Debugger.Log("Read var from lua: " + state["var2read" ]); Debugger.Log("Read table var from lua: " + state["varTable.default" ]); LuaFunction func = state.GetFunction("TestFunc" ); func.Call(); func.Dispose(); LuaTable table = state["varTable" ] as LuaTable; LuaTable map = table["map" ] as LuaTable; Debugger.Log($"Read varTable from lua, default: {table["default" ]} map name: {map["name" ]} " ); map["name" ] = "New" ; Debugger.Log("Modify varTable name: {0}" , map["name" ]); map.Dispose(); table.AddTable("newmap" ); LuaTable newmap = table["newmap" ] as LuaTable; newmap["name" ] = "newmap" ; Debugger.Log($"varTable.newmap name {newmap["name" ]} " ); newmap.Dispose(); LuaTable metaTable = table.GetMetaTable(); if (metaTable != null ) { Debugger.Log("varTable metatable name: {0}" , metaTable["name" ]); } object [] list = table.ToArray(); for (int i = 0 ; i < list.Length; i++) { Debugger.Log("varTable[{0}], is {1}" , i, list[i]); } table.Dispose(); } private void OnDestroy () { state.CheckTop(); state.Dispose(); } }
函数调用、参数和返回值 调用函数有三种形式,一种是调用LuaFunc
的Call
函数,支持无参函数和多个参数的调用形式,但是没有返回值。第二种是PCall
的调用形式,这种调用形式比较麻烦,调用语句和获取返回值要写的代码比较多,另一种是LazyCall
的形式,支持可变参数的传递并返回多个返回值,其实是对PCall
的封装,由于有GC的影响,已经不推荐使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 using LuaInterface;using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameManager : MonoBehaviour { LuaState state; private string script = @" function TestArray(array) local len = array.Length for i = 0, len - 1 do print('Array: '..tostring(array[i])) end local iter = array:GetEnumerator() while iter:MoveNext() do print('iter: '..iter.Current) end local t = array:ToTable() for i = 1, #t do print('table: '.. tostring(t[i])) end local pos = array:BinarySearch(3) print('array BinarySearch: pos: '..pos..' value: '..array[pos]) pos = array:IndexOf(4) print('array indexof bbb pos is: '..pos) return 1, '123', true end " ; void Start () { state = new LuaState(); state.Start(); state.DoString(script); LuaFunction func = state.GetFunction("TestArray" ); int [] array = { 1 , 2 , 3 , 4 , 5 }; func.BeginPCall(); func.Push(array); func.PCall(); double arg1 = func.CheckNumber(); string arg2 = func.CheckString(); bool arg3 = func.CheckBoolean(); Debugger.Log("return is {0} {1} {2}" , arg1, arg2, arg3); func.EndPCall(); func.Dispose(); } private void OnDestroy () { state.CheckTop(); state.Dispose(); } }
自定义加载器 在ToLua的例子中,有一个例子是说明如何定义自己的加载器。例子中创建了一个TestCustomLoader
类,并集成自LuaClient
,重写InitLoader
方法,该方法返回自己定义的修改器LuaResLoader
。
1 2 3 4 5 6 7 8 9 public class TestCustomLoader : LuaClient { string tips = "Test custom loader" ; protected override LuaFileUtils InitLoader () { return new LuaResLoader(); } }
一开始我还以为LuaResLoader
是ToLua
提供的一些Helper
类,阅读其中的代码才知道,它是继承自LuaFileUtils
的类,重写ReadFile
方法。这可以很方便地去管理Lua文件的加载路径。甚至可以想象这样一种场景:在网络延迟开销影响不大的情况下,通过HTTP URL
访问的方式去获取Lua
文件的内容并加载到运行环境中。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件