2010-11-05

Unity 3D + Lua 實作筆記


幾天前實作使用 Lua 當 Untiy 3D 的腳本語言,好處是只要用記事本修改 Lua Script 文字檔就可更改程式執行流程,不用重新編譯程式。這裡使用的方法是將 Lua 以 DLL Plugin 的方式掛在 Unity 3D 裡執行,底下是整個實作的流程 (  iOS 平台的實作請參考另一篇 ) :

1. 重新編譯讓 C# 可以 Import 的 Lua DLL 檔。
因為 C 和 .net 的 calling convention 方式不同以致於需要編譯一個 .net 使用的 calling convention 方式的 DLL 檔,可以參考這篇文章

1.1 到 http://www.lua.org/ftp/ 下載 lua5.1 原始碼,此時的最新版是 lua-5.1.4.tar.gz ,解開後可以看到原始碼在 src 目錄下。

1.2 用 VC 開啟新專案 (Visual C++ 的 Win32 Console Application),Application Type 的部分選擇 DLL,Additional options 的部分選擇 Empty project。

開啟新專案

Application Settings

1.3 引入 src 目錄內的原始檔(除 Makefile、lua.c、luac.c 外)。
1.4 設定 Calling Convention 為 _stdcall(在 Project Property -> Configuration Properties -> C/C++ -> Advanced -> Calling Convention)。

Calling Convention

1.5 在 Preprocessor Definitions ( Project Property -> Configuration Properties -> C/C++ -> Preprocessor->Preprocessor Definitions)裡加入 LUA_BUILD_AS_DLL 和 LUA_CORE 兩個定義。
Preprocessor Definitions

1.6 按下 Build 即可產生一個 DLL 檔。

2 將 DLL 檔複製到 Unity3D 專案的 plugins 目錄下 ("\Assets\Plugins"),測試 Lua DLL。

2.1 測試大約區分成三個部分,第一是 "開啟和關閉 Lua" ,第二是 "註冊 C# 的函數給 Lua",第三是 "執行 Lua Script" 並在 Script 內回呼之前註冊的 C# Function。

2.2 "開啟  Lua" 執行如下:
m_luaState = luaL_newstate();
luaL_openlibs(m_luaState);

其中 m_luaState 的定義是:
protected IntPtr m_luaState = IntPtr.Zero;

luaL_newstate() 和 luaL_openlibs() 這兩個函數在執行前須 Import :
[DllImport("lua514vc")] private static extern IntPtr luaL_newstate();
[DllImport("lua514vc")] private static extern void luaL_openlibs(IntPtr lua_State);
其中 lua514vc 是編譯的 Lua 的 DLL 檔名稱。

2.3 "關閉  Lua" 執行如下:
lua_close(m_luaState);

2.4 "註冊 C# 函數",底下是 Lua 註冊 C# Function 的函數:
protected static void lua_register(IntPtr pLuaState, string strFuncName, LuaFunction pFunc)
{
    lua_pushcclosure(pLuaState, pFunc, 0);
    lua_setfield(pLuaState, (int)LuaIndexes.LUA_GLOBALSINDEX, strFuncName);
}

其中 pLuaState 是開啟 Lua 之後取得的 m_luaState,strFuncName 是欲註冊的函數名稱, pFunc 是 C# 函數, LuaFunction 是函數指標,定義如下:
public delegate int LuaFunction(IntPtr pLuaState);

函數 lua_pushcclosure() 和 lua_setfield() 的 Import 方式如下:
[DllImport("lua514vc")]
private static extern void lua_pushcclosure(
    IntPtr lua_State, 
    [MarshalAs(UnmanagedType.FunctionPtr)] LuaFunction func, 
    int n);

[DllImport("lua514vc")]
private static extern void lua_setfield(
    IntPtr lua_State, 
    int idx, 
    string s);

例如現在有一個函數為:
int TestDisplay(IntPtr pLuaState)
{
    Debug.Log("LuaWrapper.TestDisplay()");
    return 0;
}

則註冊 TestDisplay() 函數的方法就是:
lua_register(m_luaState, "TestDisplay", TestDisplay);

2.5 "執行 Lua Script" 檔案:
執行 Lua Script 檔案的函數是:
protected static int luaL_dofile(IntPtr lua_State, string s)
{
    if (luaL_loadfile(lua_State, s) != 0)
        return 1;
 
    return lua_pcall(lua_State, 0, -1, 0);
}

其中 string s 表示 Lua Script 的檔案路徑。函數 luaL_loadfile和 lua_pcall 的 Import 方式為:
[DllImport("lua514vc")]
private static extern int lua_pcall(IntPtr lua_State, int nargs, int nresults, int errfunc);

[DllImport("lua514vc")]
private static extern int luaL_loadfile(IntPtr lua_State, string s);

最後,編輯一個文字檔,儲存檔名為 test.txt,其內容為:
TestDisplay()

然後在 C# 裡執行:
luaL_dofile( m_luaState , "test.txt" );

就會看到 Untiy3D 裡的 Console 視窗出現 "LuaWrapper.TestDisplay()" 字串了。

update 2012-07-05 :
範例程式碼可以在  https://github.com/phardera/unity3d_lua 下載

8 則留言 :

  1. 請問一下~使用Lua用意~好處是什麼呢?

    回覆刪除
  2. 嗯~謝謝你的發問~~
    關於 Lua 的詳細介紹和好處,可以參考這篇文章,寫得還不錯!
    http://blog.monkeypotion.net/gameprog/beginner/introduction-of-scripting-system-and-lua

    回覆刪除
  3. 太感謝了!!! 猴子靈藥也是我很愛逛的網站說XDD 竟沒發現他以前的文真就有介紹了~^^

    回覆刪除
  4. 您好! 请问您代码中的LuaIndexes在哪里定义的,方便给个联系方式吗?有些问题需要请教。 我的邮箱
    madjonr@gmail.com

    回覆刪除
    回覆
    1. 您好 ! 非常抱歉~ LuaIndexes 的定義確實忘了寫在文章裡, 關於 LUA_GLOBALSINDEX 等數值其實可以在 lua.h 檔案裡找到 !
      此外, 我把文章的範例程式碼放在 https://github.com/phardera/unity3d_lua 上供您參考, 謝謝

      刪除
  5. 您好! 上面的问题通过google解决了,非常感谢!现在有些新问题,方便的话可以帮忙给个思路吗?
    1. lua以U3D插件的形式跑在IOS上,我看了官方的插件教程上面说的很简单,将lua源码放在xcode的classes文件夹下,在u3d项目的Assets/Plugins/ 下面创建一个C#的接口文件调用lua的api,但是我报错找不到lua 的api 函数。
    2. 怎么将lua源码编译成lua.bundle(类似pc上的lua.dll)跑在mac os上。

    如果方便的话!可以留您邮箱我吗?

    回覆刪除
    回覆
    1. 您好!關於 lua+unity3d on ios 的部分我最近會測試看看並把結果發表出來。此外,聯絡我可以用這個信箱:phardera(at)live . ca謝謝~

      刪除
  6. 我翻遍了网络没有找到解决方案,也可能是我英文不太好。

    回覆刪除