一、下载ToLua工具:http://www.ulua.org/index.html
我用的UGUI比较多,所以下载这个
解压后,直接拷贝文件到项目里面。
二、ToLua自带的案例目录:
运行的demo报错,
打开LuaConst.cs脚本,修改
public static string luaDir = Application.dataPath + "/Scripts/LuaScripts"; //lua逻辑代码目录
游戏中的自己lua代码都在这个文件夹下面:
重新跑demo项目可以正常执行。
三、学习toLua自带的demo:
不想每个案例开一篇博客,就干脆都放在一个脚本里面学习吧。
using UnityEngine; using System.Collections; using LuaInterface; using System; public class StudytoLua : MonoBehaviour { LuaState lua = null; LuaFunction curLuaFunction = null; void Awake() { //Example_01(); //Example_02(); Example_03(); } /// <summary> /// 案例一:调用lua代码,学习如何在c#脚本里面运行lua字符串代码 /// DoString /// </summary> private void Example_01() { //创建一个lua的虚拟机 lua = new LuaState(); //必须先启动虚拟机,否则会报错 lua.Start(); string hello = @"print('hello toLua#')"; //运行lua代码 lua.DoString(hello, "hello"); //销毁虚拟机栈 lua.CheckTop(); lua.Dispose(); lua = null; } /// <summary> /// 案例二:如何添加lua脚本目录,运行lua脚本 /// DoFile:Dofile则是每次调用都会重新加载使用,相对来说对性能等的消耗都会大一些,而且感觉不利于一些后面代码的书写 /// Require:Require 读取文件是会检查该文件是否被加载过,如果被加载过,则直接返回一个索引,否则则加载并返回一个索引 /// </summary> private void Example_02() { lua = new LuaState(); lua.Start(); string fullPath = Application.dataPath + "/Scripts/LuaScripts"; lua.AddSearchPath(fullPath); } /// <summary> /// 案例三:调用lua脚本 /// 获取lua文件中的函数对象 LuaState.GetFunction() /// 调用lua文件中函数: /// 1)LuaFunction.PCall /// 2)LuaFunction.Invoke<传入参数类型, 传出参数类型>(传入参数) 这个其实就是执行了PCall,和1)是一致的 /// 3)声明一个委托事件 LuaFunction func = LuaFunction.ToDelegate<Func<int, int>>(); result = func(1); /// </summary> private void Example_03(){ string scripts = @" function luaFunc(num) return num + 1 end test = {} test.luaFunc = luaFunc "; lua = new LuaState(); lua.Start(); lua.DoString(scripts); curLuaFunction = lua.GetFunction("test.luaFunc"); //获取到lua文件中的函数对象 if (curLuaFunction != null) { int num = curLuaFunction.Invoke<int, int>(123456); Debugger.Log("result0: {0}", num); num = CallFunc(); Debugger.Log("result1: {0}", num); DelegateFactory.Init(); //这句话是初始化委托工厂 Func<int, int> func = curLuaFunction.ToDelegate<Func<int, int>>(); num = func(123456); Debugger.Log("result2: {0}", num); num = lua.Invoke<int, int>("test.luaFunc", 123456, true); Debugger.Log("result3: {0}", num); } curLuaFunction.Dispose(); lua.CheckTop(); lua.Dispose(); lua = null; } int CallFunc() { curLuaFunction.BeginPCall(); //开始 curLuaFunction.Push(123456); //传入参数 curLuaFunction.PCall(); //执行参数 int num = (int)curLuaFunction.CheckNumber(); //获取返回值 curLuaFunction.EndPCall(); //结束 return num; } void OnGUI() { if (GUI.Button(new Rect(50, 50, 120, 45), "DoFile")) { lua.DoFile("ScriptsFromFile.lua"); } else if (GUI.Button(new Rect(50, 150, 120, 45), "Require")) { lua.Require("ScriptsFromFile"); } } }
案例三:LuaFunction.Invoke<传入参数类型, 传出参数类型>(传入参数) 这个方法,进入里面,执行的还是CallFun()的流程,只是包了一层壳,方便使用。
案例四:发现table["map.name"]并没有取出数据,官方案例也有问题,所以想了一下
/// <summary> /// 案例四:调用lua的变量 /// LuaState["变量名字"] 取出数据 /// LuaState.GetTable("tablename") 获取table表 /// </summary> private void Example_04() { 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 "; lua = new LuaState(); lua.Start(); //创建Objs2Spawn变量,并且赋值 lua["Objs2Spawn"] = 5; lua.DoString(script); Debugger.Log("read var from lua: {0}", lua["var2read"]); Debugger.Log("read table var from lua:{0}", lua["varTable.default"]); //获取lua的函数 LuaFunction func = lua["TestFunc"] as LuaFunction; func.Call(); func.Dispose(); LuaTable table = lua.GetTable("varTable"); Debugger.Log("read varTable from lua, default:{0} name:{1}", table["default"], table["map.name"]); LuaTable curTable = table.GetTable<LuaTable>("map");//这个是正确获取map.name的值 Debugger.Log("Read varTable from lua, default: {0} name: {1}", table["default"], curTable["name"]); table["map.name"] = "new_map01"; Debugger.Log("Modify varTable name:{0}", table["map.name"]); //添加新的表 table.AddTable("new_map"); LuaTable table1 = (LuaTable)table["new_map"]; table1["name"] = "table1"; Debugger.Log("varTable.new_map name: {0}", table1["name"]); table1.Dispose(); table1 = table.GetMetaTable(); if (table1 != null) { Debugger.Log("varTable metatable name: {0}", table1["name"]); } object[] list = table.ToArray(); for (int i = 0; i < list.Length; i++) { Debugger.Log("varTable[{0}], is {1}", i, list[i]); } table.Dispose(); lua.CheckTop(); lua.Dispose(); }案例五:协程代码,推荐使用这个
#region Example_05 /// <summary> /// 案例5:tolua封装的协程 /// lua脚本中: /// 1)协程函数的开启 coDelay = coroutine.start(协程函数名字) /// 延时 coroutine.wait(单位s,时间) /// 挂起 coroutine.step() /// 结束 coroutine.stop(协程对象coDelay) 即结束的参数为开启的返回的对象 /// 下载 coroutine.www(UnityEngine.WWW("http://www.baidu.com")) /// 注意适用范围,start、stop之外,其他只能在协同函数里面使用 /// 2)collectgarbage('count') 返回当前使用的内存,单位为千字节 http://www.runoob.com/lua/lua-garbage-collection.html /// 3)a.b 调用静态方法 a:b 调用非静态方法 /// </summary> private void Example_05() { lua = new LuaState(); lua.Start(); //如果执行的lua文件需要使用unity中的类型,需要这个类给luastate绑定 LuaBinder.Bind(lua); DelegateFactory.Init(); //用于执行协同 LuaLooper looper = gameObject.AddComponent<LuaLooper>(); looper.luaState = lua; lua.DoString(luaFile.text, "TestLuaCoroutine.lua"); LuaFunction f = lua.GetFunction("TestCortinue"); f.Call(); f.Dispose(); f = null; } void OnGUI() { if (GUI.Button(new Rect(50, 50, 120, 45), "StartDelay")) { LuaFunction func = lua.GetFunction("StartDelay"); func.Call(); func.Dispose(); } else if (GUI.Button(new Rect(50, 150, 120, 45), "StopDelay")) { LuaFunction func = lua.GetFunction("StopDelay"); func.Call(); func.Dispose(); } else if (GUI.Button(new Rect(50, 250, 120, 45), "GC")) { string garbageCountStr = @" local result = collectgarbage('count') print('>>>>>>>>before count:',result) "; lua.DoString(garbageCountStr); lua.DoString("collectgarbage('collect')", "StudytoLua.cs"); lua.DoString(garbageCountStr); Resources.UnloadUnusedAssets(); } } #endregion
对应的lua代码:
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') for i = 0, 10, 1 do print(fib(i)) coroutine.wait(0.1) end print("current frameCount: "..Time.frameCount) coroutine.step() print("yield frameCount: "..Time.frameCount) local www = UnityEngine.WWW("http://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
案例六:协同的另一种实现方法,用类unity原生,但是效率低,不推荐使用,就不说了。
案例七:Lua原生的协同
#region Example_07 /// <summary> /// 案例7:lua的原生的协同:https://www.runoob.com/lua/lua-coroutine.html /// </summary> private void Example_07() { 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 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 "; lua = new LuaState(); lua.Start(); lua.LogGC = true; lua.DoString(script); //获取到Test方法 LuaFunction func = lua.GetFunction("Test"); func.BeginPCall(); func.PCall(); luaThread = func.CheckLuaThread(); luaThread.name = "LuaTestThread"; func.EndPCall(); func.Dispose(); func = null; luaThread.Resume(10); } void OnGUI() { if (GUI.Button(new Rect(50, 50, 120, 45), "Resume Thead")) { int ret = -1; if (luaThread != null && luaThread.Resume(true, out ret) == (int)LuaThreadStatus.LUA_YIELD) { Debugger.Log("lua yield: " + ret); } } else if (GUI.Button(new Rect(50, 150, 120, 45), "Close Thread")) { if (luaThread != null) { luaThread.Dispose(); luaThread = null; } } } #endregion