首先一个最简单的调用:
using UnityEngine;
using LuaInterface;
public class Test : MonoBehaviour {
public void Start()
{
LuaState state = new LuaState ();
state.Start ();
state.DoString ("print(\"Hello lua\")");
state.Dispose();
}
}
理论上来讲,可以使用DoString执行任何Lua脚本,只要传入的字符串足够长。不过这样也就没有使用Lua脚本的意义了。
我们可以使用DoFile方法来执行lua文件。
例如一个名为Test.lua的文件中包含下列Lua代码:
print("Hello lua in file")
state.DoFile ("Test1.lua");
但是只是执行文件中的代码意义并不是很大,我们希望可以有选择的执行文件中的某个函数。
例如lua文件中添加函数:
function testToLua1(t)
print(t)
return {t + 1 , t - 2 , t * 3, t / 4}
end
传入一个数字t,然后对它分别进行四则运算,返回一个数组形式的table。
那么在Test.cs中添加:
LuaFunction func1 = state.GetFunction ("testToLua1");
func1.BeginPCall ();
func1.Push (1024);
func1.PCall ();
if (state.lua_istable (-1)) {
int n = state.LuaGetN (-1);
for (int i = 0; i < n; i++) {
state.LuaRawGetI (-1, i + 1);
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
}
}
func1.EndPCall ();
首先获取testToLua1函数,然后执行BeginPCall,将1024这个参数Push到Lua栈里,执行PCall,就会在Lua打印出这个数字,然后会将返回值加入Lua栈中。
lua栈中,最先入栈的的下标为1,然后递增。也可以用负数表示,-1表示栈顶,然后递减。
可以通过lua_istable判断栈顶是否是table,然后使用LuaGetN方法获取数组table的长度,然后遍历数组元素。
遍历的时候使用LuaRawGetI获取table中对应下标的元素,会将这个元素入栈,接着使用LuaToNumber将栈顶元素转换成数(double)并打印出来,然后需要LuaPop将这个数出栈,后面的循环才成立(否则需要修改LuaRawGetI和LuaToNumber,将第一个参数改为对应的栈位置)。
最后EndPCall会将执行函数用到的栈位置清空(还原回执行之前的状态)。
再例如lua文件中再添加一个函数:
testToLua2 = function(t)
print(t)
return {a=t + 1 , b=t - 2 , c=t * 3, d=t / 4}
end
调用它的C#代码:
LuaFunction fun2 = state.GetFunction ("testToLua2");
fun2.BeginPCall ();
fun2.Push (1024);
fun2.PCall ();
if (state.lua_istable (-1)) {
state.LuaGetField (-1, "a");
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
state.LuaGetField (-1, "b");
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
state.Push ("c");
state.LuaRawGet (-2);
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
state.Push ("d");
state.LuaRawGet (-2);
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
}
fun2.EndPCall ();
一种是LuaGetField,只需要传入table所在的栈位置和key,就会将value入栈。
另一种是先将key入栈,然后使用LuaRawGet方法(关于rawget参考Lua语法小贴士(五)元表与元方法)获取value(这时候会将Key出栈)。
(数组形式的table也可以用LuaRawGet方法获取value,只需要先把对应的下标用state.Push入栈)
最后我们看一下可变参数和多个返回值。
Lua代码:
testToLua3 = function(...)
print(...)
return ...
end
C#代码:
LuaFunction fun3 = state.GetFunction ("testToLua3");
fun3.BeginPCall ();
int oldTop = state.LuaGetTop ();
for (int i = 0; i < 10; i++) {
fun3.Push (i);
}
fun3.PCall ();
for (int i = 0, imax = state.LuaGetTop () - oldTop + 1; i < imax; i++) {
Debug.Log (state.LuaToNumber (-1));
state.LuaPop (1);
}
fun3.EndPCall ();
for (int i = 0, imax = state.LuaGetTop () - oldTop + 1; i < imax; i++) {
Debug.Log (state.LuaToNumber (-imax+i));
}
我们可以看到imax = state.LuaGetTop () - oldTop + 1,这是为什么呢?
我们可以在fun3.PCall ();上下分别添加Debug.Log (state.LuaGetTop ());
可以看到PCall之前栈深度是12,PCall之后是11。也就是说PCall这个方法从栈中弹出了一个元素。
我们可以添加一个静态方法用来遍历Lua栈:
static public void DumpLuaStack(LuaState luaState)
{
for (int i = 1, imax = luaState.LuaGetTop (); i <= imax; i++) {
LuaTypes luaType = luaState.LuaType (i);
switch (luaType) {
case LuaTypes.LUA_TBOOLEAN:
Debug.Log (luaState.LuaToBoolean (i));
break;
case LuaTypes.LUA_TFUNCTION:
Debug.Log (luaState.GetFunction (i).ToString());
break;
case LuaTypes.LUA_TNIL:
Debug.Log ("NIL");
break;
case LuaTypes.LUA_TNUMBER:
Debug.Log (luaState.LuaToNumber (i));
break;
case LuaTypes.LUA_TSTRING:
Debug.Log (luaState.LuaToString (i));
break;
case LuaTypes.LUA_TTABLE:
Debug.Log (luaState.GetTable (i).ToString ());
break;
}
}
}
在PCall之前添加DumpLuaStack(state);
这样既可以看到栈中除了10个参数之外,还有两个LuaFunction,也就是两个testToLua3。
了解这些方法之后,我们可以给C#脚本中通过变量来指定调用哪个Lua文件的哪个函数了。