Cocos2d-x手動綁定C++類到Lua

   Cocos2d-x 3.0開始, Lua Binding使用tolua++方式自動綁定底層C++類到Lua層,使用戶可以用Lua方式調用引擎各類接口。可是用戶仍是但願手動綁定某些自定義類,因此接下來的內容將一步一步講解如何手動將自定義C++類綁定到Lua。android

建立自定義類

首先,定義一個類Foo,這個類就是接下來要綁定到Lua的類。ios

注意:全部C++類文件必須放在 Classes 文件夾裏,全部Lua文件必須放在 Resources 文件夾裏。git

在 fun.h 頭文件中添加以下代碼:github

#include <iostream>#include <sstream>class Foo{public:
    Foo(const std::string & name) : name(name)
    {
        std::cout << "Foo is born" << std::endl;
    }
    
    std::string Add(int a, int b)
    {
        std::stringstream ss;
        ss << name << ": " << a << " + " << b << " = " << (a+b);
        return ss.str();
    }
    
    ~Foo()
    {
        std::cout << "Foo is gone" << std::endl;
    }
    private:
    std::string name;};

這個類有三個函數,構造函數、Add函數和析構函數。做用是輸出不一樣字符串,以判斷各函數是否被調用。web

綁定自定義類到Lua

開始前,先了解下綁定C++類的一些基本原理。app

首先建立一個userdata來存放C++類對象指針,而後給userdata添加元表,用index元方法映射C++類中的對象方法。函數

userdata測試

Lua中userdata爲自定義類型,即用戶自定義C++類類型,非Lua基本類型。綁定過程當中用來存放C++類對象指針,從而將C++類映射到Lua中。ui

元表(metatable)lua

帶有索引的集合的表,綁定過程當中用來存放和映射C++類中的對象和對象方法。

__index

元表索引,指向用來存放userdata的元表。用來索引已建立的元表棧中的C++類名以及類方法名。

接下來完成實現部分,在 fun.cpp 中添加以下代碼:

#include "fun.h"extern "C"{#include <lua.h>#include <lauxlib.h>#include <lualib.h>}// 1.int l_Foo_constructor(lua_State * l){
    const char * name = luaL_checkstring(l, 1);
 
    Foo ** udata = (Foo **)lua_newuserdata(l, sizeof(Foo *));
    
    *udata = new Foo(name);
    
    return 1;}// 2.Foo * l_CheckFoo(lua_State * l, int n){
    return *(Foo **)luaL_checkudata(l, n, "luaL_Foo");}// 3.int l_Foo_add(lua_State * l){
    Foo * foo = l_CheckFoo(l, 1);
    
    int a = luaL_checknumber(l, 2);
    int b = luaL_checknumber(l, 3);
    
    std::string s = foo->Add(a, b);
    lua_pushstring(l, s.c_str());
    
    return 1;}// 4.int l_Foo_destructor(lua_State * l){
    Foo * foo = l_CheckFoo(l, 1);
    
    delete foo;
    
    return 0;}// 5.void RegisterFoo(lua_State * l){
    luaL_Reg sFooRegs[] =
    {
        { "new", l_Foo_constructor },
        { "add", l_Foo_add },
        { "__gc", l_Foo_destructor },
        { NULL, NULL }
    };
    
    luaL_newmetatable(l, "luaL_Foo");
    
    luaL_register(l, NULL, sFooRegs);
   
    lua_setfield(l, -1, "__index");
    
    lua_setglobal(l, "Foo");}

代碼詳解:

1. C++綁定到Lua的構造函數,luaL_checkstring用來檢查構造函數的形參是否爲string類型並返回這個string。利用lua_newuserdata建立一個userdata來存放Foo類對象指針。luaL_getmetatable將與名爲luaL_Foo相關聯的元表推入棧中。此時,Lua棧中的內容以下:

3| metatable "luaL_foo"   |-12| userdata               |-21| string parameter       |-3

lua_setmetatable 將位於Lua棧中-2位置的 userdata 添加到元表 luaL_foo 中。最後,返回值1使得Lua能夠獲得userdata,以後棧將會被清空。

2. luaL_checkudata 用來檢測形參是否爲 luaL_Foo 元表中的userdata,並返回這個userdata。

3. 此函數是將C++類中的Add()函數映射到Lua中, lua_pushstring 將字符串壓入棧中,返回值1使得字符串返回給Lua調用函數。

4. 此函數是將C++類中的析構函數映射到Lua中。

5. 此函數是註冊C++類到Lua和註冊全部已綁定的C++函數到Lua。 sFooRegs 給每一個已綁定的C++函數一個能被Lua訪問的名字。 luaL_newmetatable 建立一個名爲luaL_Foo的元表並壓入棧定, luaL_register 將 sFooRegs 添加到luaL_Foo中。 lua_pushvalue 將luaL_Foo元表中元素的拷貝壓入棧中。 lua_setfield 將luaL_Foo元表的index域設爲 __index 。 lua_setglobal 將元表luaL_Foo重命名爲Foo並將它設爲Lua的全局變量,這樣Lua能夠經過識別Foo來訪問元表luaL_Foo,並使Lua腳本可以覆蓋元表Foo,即覆蓋C++函數。如此一來,用戶能夠用Lua代碼自定功能,覆蓋掉C++類中函數的功能,極大地提升了代碼靈活性。

如今添加綁定函數的函數聲明至 fun.h 中:

int l_Foo_constructor(lua_State * l);Foo * l_CheckFoo(lua_State * l, int n);int l_Foo_add(lua_State * l);int l_Foo_destructor(lua_State * l);void RegisterFoo(lua_State * l);

Lua測試代碼

添加以下代碼到 fun.lua :

function Foo:speak()
    print("Hello, I am a Foo")end
 local foo = Foo.new("fred")local m = foo:add(3, 4)
 print(m)
 foo:speak()
 Foo.add_ = Foo.add// 1.function Foo:add(a, b)
    return "here comes the magic: " .. self:add_(a, b)end
 m = foo:add(9, 8)
 print(m)

1. 同名函數覆蓋綁定的C++函數,提升擴展性。

綁定檢測

將 #include 「fun.h」 添加至 AppDelegate.cpp 中,並在 AppDelegate.cpp 中的 applicationDidFinishLaunching 函數中添加以下代碼:

// register lua engine
    auto engine = LuaEngine::getInstance();
    ScriptEngineManager::getInstance()->setScriptEngine(engine);
    
    // Adding code here...
    // register lua binding
    
    RegisterFoo(engine->getLuaStack()->getLuaState());
    
    std::string path = FileUtils::getInstance()->fullPathForFilename("fun.lua");
    engine->executeScriptFile(path.c_str());

注意: 由於Cocos2d-x Lua Binding目前不支持多個狀態,因此在註冊已綁定的C++類時,只能使用當前運行的狀態。

運行程序,若是獲得下面的輸出結果證實已經綁定成功:

Foo is born
cocos2d: [LUA-print] fred: 3 + 4 = 7cocos2d: [LUA-print] Hello, I am a Foococos2d: [LUA-print] here comes the magic: fred: 9 + 8 = 17

Android環境測試:

若是但願在Android環境下調試,在執行 proj.android/build_native 腳本前,須要在 proj.android/jni/Android.mk 文件中添加 fun.cpp 文件包含:

LOCAL_SRC_FILES := hellolua/main.cpp \                   ../../Classes/AppDelegate.cpp \                   ../../Classes/fun.cpp

項目文件在此下載

相關文章
相關標籤/搜索