Lua实现print调用Unity输出日志

在 tolua 和 xlua 中,调用 print 会直接在 Unity 控制台输出日志。而实际上 Lua 的源码中 print 只是输出日志,想要传递到 C# 需要额外做一些操作。

tolua的源码中,ToLua.cs文件里的构造函数和OpenLibs函数:

1
2
3
4
5
6
7
8
9
10
11
12
L = LuaNewState();   
ToLua.OpenLibs(L);

//......

public static void OpenLibs(IntPtr L)
{
//......
LuaDLL.tolua_pushcfunction(L, Print);
LuaDLL.lua_setglobal(L, "print");
//......
}

一开始构造函数中核心代码是两句,首先初始化lua状态机,然后打开lua标准库,将库中的函数放入全局变量。

lua标准库中是package、table、string、math等lua源码自带的函数,这些代码的加载在lua源码中的linit.c文件里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{NULL, NULL}
};

LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}

LuaDLL.cs就是通过调用lua源码中的 luaL_openlibs 函数打开标准库。

接着 LuaDLL.tolua_pushcfunction 将 C# 的 Print 函数压入栈顶,lua源码中的lua_pushcfunction是一个宏函数,不能直接通过dll调用,在LuaDll中是通过DLLImport导入这个函数然后封装实现调用的:

1
2
3
4
5
6
[DllImport( LUADLL, CallingConvention = CallingConvention.Cdecl )]
public static extern void lua_pushcclosure( IntPtr L, LuaCSFunction f, int n );
public static void lua_pushcfunction( IntPtr L, LuaCSFunction f )
{
lua_pushcclosure( L, f, 0 );
}

LuaCSFuncton是一个参数为Intptr,返回值为int的委托。

tolua在 C# 层另外实现了一个 Print 方法,并且通过 lua_setglobal 把 lua 层的全局函数 print 替换掉:

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
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static int Print(IntPtr L)
{
try
{
int n = LuaDLL.lua_gettop(L);

using (CString.Block())
{
CString sb = CString.Alloc(256);
#if UNITY_EDITOR
int line = LuaDLL.tolua_where(L, 1);
string filename = LuaDLL.lua_tostring(L, -1);
LuaDLL.lua_settop(L, n);
int offset = filename[0] == '@' ? 1 : 0;

if (!filename.Contains("."))
{
sb.Append('[').Append(filename, offset, filename.Length - offset).Append(".lua:").Append(line).Append("]:");
}
else
{
sb.Append('[').Append(filename, offset, filename.Length - offset).Append(':').Append(line).Append("]:");
}
#endif

for (int i = 1; i <= n; i++)
{
if (i > 1) sb.Append(" ");

if (LuaDLL.lua_isstring(L, i) == 1)
{
sb.Append(LuaDLL.lua_tostring(L, i));
}
else if (LuaDLL.lua_isnil(L, i))
{
sb.Append("nil");
}
else if (LuaDLL.lua_isboolean(L, i))
{
sb.Append(LuaDLL.lua_toboolean(L, i) ? "true" : "false");
}
else
{
IntPtr p = LuaDLL.lua_topointer(L, i);

if (p == IntPtr.Zero)
{
sb.Append("nil");
}
else
{
sb.Append(LuaDLL.luaL_typename(L, i)).Append(":0x").Append(p.ToString("X"));
}
}
}

Debugger.Log(sb.ToString()); //203行与_line一致
}
return 0;
}
catch (Exception e)
{
return LuaDLL.toluaL_exception(L, e);
}
}