Cocos2d-x下Lua調用自定義C++類和函數的最佳實踐

關於cocos2d-x下Lua調用C++的文檔看了很多,但沒有一篇真正把這事給講明白了,我本身也是個初學者,摸索了半天,總結以下:php

cocos2d-x下Lua調用C++這事之因此看起來這麼複雜、網上全部的文檔都沒講清楚,是由於存在5個層面的知識點:html

一、在純C環境下,把C函數註冊進Lua環境,理解Lua和C之間能夠互相調用的本質
二、在cocos2d-x項目裏,把純C函數註冊進Lua環境,理解cocos2d-x是怎樣建立Lua環境的、以及怎樣獲得這個環境並繼續自定義它
三、瞭解爲何要使用toLua++來註冊C++類
四、在純C++環境下,使用toLua++來把一個C++類註冊進Lua環境,理解toLua++的用法
五、在cocos2d-x項目裏,使用cocos2d-x註冊自身的方式把自定義的C++類註冊進Lua環境,理解cocos2d-x是怎樣經過bindings-generator腳原本封裝toLua++的用法來節省工做量的python

只有理解了前4層,在最後使用bindings-generator腳本的時候內心纔會清清楚楚。而網上的文檔,要麼是隻解釋了第1層,要麼是隻填鴨式地告訴你第5層怎麼用bindings-generator腳本,不只中間重要的知識點一律不提,示例代碼每每也寫的不夠簡潔,這讓我這種看見C++就眼暈的人理解起來大爲頭疼(不是我不會C++,而是我很是不接受C++的設計哲學,能避就避)。因此接下來的講解我會對每一層知識點逐一講解,示例代碼也不求完整嚴謹,而是儘可能用最簡潔的方式把程序的關鍵點說明白。ios


第一層:純C環境下,把C函數註冊進Lua環境

直接看代碼比囉哩囉嗦講一大堆概念要清晰明瞭的多。創建一個a.lua和一個a.c文件,內容以下,一看就明白是怎麼回事了:c++

a.lua程序員

print(foo(99))

a.c編程

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int foo(lua_State *L)
{
  int n = lua_tonumber(L, 1);

  lua_pushnumber(L, n + 1);

  return 1;
}

int main()
{
  lua_State *L = lua_open();

  luaL_openlibs(L);

  lua_register(L, "foo", foo);

  luaL_dofile(L, "a.lua");

  lua_close(L);

  return 0;
}

怎麼樣,這代碼簡單吧?一看就明白,簡單的不能再簡單了。我特別煩示例代碼裏又是判斷錯誤又是加代碼註釋的,原本看本身不會的代碼就夠吃力的了,還加那麼多花花綠綠的干擾項,純粹增長學習負擔。segmentfault

在命令行下用gcc來編譯並執行吧:xcode

gcc a.c -llua && ./a.out

注意-llua選項是必要的,由於要鏈接lua的庫。架構

看完上面那段代碼,再解釋起來就容易多了:

一、要想註冊進Lua環境,函數須要定義爲這個樣:int xxx(lua_State *L)
二、使用lua_tonumberlua_tostring等函數,來取得傳入的參數,好比lua_tonumber(L, 1)就是獲得傳入的第一個參數,且類型爲數字
三、使用lua_pushnumberlua_pushstring等函數,來將返回值壓入Lua的環境中,由於Lua支持函數返回多個值,因此能夠push多個返回值進Lua環境
四、最終函數返回的數字表示有多少個返回值被壓入了Lua環境
五、使用lua_register宏定義來將這個函數註冊進Lua環境,Lua腳本里就能夠用它了,大功告成!就這麼簡單!


第二層:在cocos2d-x環境下,把C函數註冊進Lua環境

也簡單:

一、在frameworks/runtime-src/Classes/目錄下,找到AppDelegate.cpp文件。若是frameworks目錄不存在,則須要參考這篇Blog:用Cocos Code IDE寫Lua,如何與項目中的C++代碼和諧相處

AppDelegate.cpp文件中的關鍵代碼以下:
```c++
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);

LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));

//register custom function
//LuaStack* stack = engine->getLuaStack();
//register_custom_function(stack->getLuaState());

能夠看到cocos2d-x已經爲咱們留出了註冊自定義C函數的位置,在註釋代碼後面這麼寫就能夠了: ```cpp lua_State *L = stack->getLuaState(); lua_register(L, "test_lua_bind", test_lua_bind);

也能夠經過ScriptEngineManager類從頭取得當前的LuaEngine對象,而後再getLuaStack()方法獲得封裝的LuaStack對象,再調用getLuaState()獲得原始的lua_State結構指針。只要知道了入口位置,其餘一切就不成問題了,仍是挺簡單的。

感興趣的話能夠去看一下ScriptEngineManager類的詳細定義,在frameworks/cocos2d-x/cocos/base/CCScriptSupport.h文件中。

BTW:這裏還有一個小知識點,插入在AppDelegate.cpp中的自定義代碼儘可能寫在COCOS2D_DEBUG宏定義的判斷前面,由於在調試環境下和真機環境下後續執行的代碼是不同的:

#if (COCOS2D_DEBUG>0)
    if (startRuntime())
        return true;
#endif

    // 調試環境下代碼就不會走到這裏了
    engine->executeScriptFile(ConfigParser::getInstance()->getEntryFile().c_str());
    return true;

二、接下來,找個地方把test_lua_bind函數定義寫進去就算大功告成了。若是追求文件組織的優雅,按理說應該新建一個.c文件,但這樣的話搞很差會把本身陷入到編譯階段的泥潭裏,因此先不追求優雅,而就在AppDelegate.cpp文件末尾寫上函數的定義就能夠了,簡單清楚明瞭:

int test_lua_bind(lua_State *L)
{
    int number = lua_tonumber(L, 1);

    number = number + 1;

    lua_pushnumber(L, number);

    return 1;
}

三、大功告成,如今就能夠在main.lua文件裏使用test_lua_bind()函數了:

local i = test_lua_bind(99)
  print("lua bind: " .. tostring(i))

四、若是是新建一個.c文件呢?把AppDelegate.cpp文件裏test_lua_bind函數定義的代碼刪掉,在頭部#include後面加入:

#include "test_lua_bind.h"

frameworks/runtime-src/Classes目錄下建立test_lua_bind.h文件,內容以下:

extern "C" {
#include "lua.h"
#include "lualib.h"
}

int test_lua_bind(lua_State *L);

再建立test_lua_bind.c文件,內容不變:

#include "test_lua_bind.h"

int test_lua_bind(lua_State *L)
{
    int number = lua_tonumber(L, 1);

    number = number + 1;

    lua_pushnumber(L, number);

    return 1;
}

此時用cocos compile -p mac命令編譯,會發現test_lua_bind.c文件並無被編譯。這是固然的,普通的C/C++項目都是用Makefile來指定編譯哪些.c/cpp文件的,當前的cocos2d-x項目雖然沒有Makefile文件,但也是遵循這個原則的,也即確定是有一個地方來指定全部要編譯的文件的,須要在這個地方把test_lua_bind.c加進去,使得整個項目編譯時把它也做爲項目的一部分。

答案是,cocos2d-x項目沒有使用Makefile,而是很是聰明地使用了與具體環境相關的工程文件來做爲命令行編譯的環境,好比在編譯iOS或Mac時就使用Xcode工程文件,在編譯Android時就使用Android.mk文件。

因此,添加好了test_lua_bind.htest_lua_bind.c文件後,用Xcode打開項目,將這倆文件添加進工程中就好了。

Xcode中添加.h和.cpp文件進工程

注意,千萬不要勾選「Copy items into destination group's folder(if needed)」,由於cocos2d-x的Xcode工程目錄組織不是常規的結構,一旦勾選這個,會致使這兩個文件被拷貝至frameworks/runtime-src/proj.ios_mac目錄下,原來frameworks/runtime-src/Classes目錄下的文件就廢掉了,這樣的組織方式會亂,並且會影響Android那邊對這倆文件的引用。

test_lua_bind.htest_lua_bind.cpp這倆文件添加進Xcode工程後,再去命令行執行cocos compile -p mac,編譯就能成功了。

網上有其餘文章說還要修改Xcode工程的「User Headers Path」,這個通過試驗是不須要的,哪怕把這倆文件放進新建的文件夾裏也不須要,只要加入了Xcode工程便可,由於Xcode內部根本就不是按照文件夾的形式來組織文件的,它本身有一套叫作「Group」的東西。搞了好幾年iOS開發,對Xcode的這個特性仍是熟悉的。

Xcode工程中添加了test_lua_bind.h/cpp文件後的樣子

說到這就不由要插一句對網上全部cocos2d-x文檔的吐槽了,學習cocos2d-x的人水平實在是參差不齊,大部分人彷佛都是對遊戲熱衷的編程初學者,他們大多底子薄基礎差,甚至一大部分人以前都沒作過移動APP的開發,他們學習cocos2d-x只想知其然而不想知其因此然,給他們講他們也看不明白(由於編程基礎差),因此網上很多cocos2d-x文章都是隻講123步驟,而不告訴你爲何這麼作,包括cocos2d-x官方的大量文檔也是基於這個思路寫的,中文和英文都同樣。我看這些文章就特別痛苦,一邊看一邊內心就老是在想,「憑什麼要這麼作啊」、「這一步是爲了什麼啊」、「怎麼這麼麻煩啊」、「這個步驟明顯不是最佳實踐啊」、「解決這事爲啥要這麼麻煩」、「有更好的方法嗎」,因此我這種初學者來看cocos2d-x文檔就變成了不是單純的學習,而是學習、質疑、求證、反思、優化的過程,對別人來講cocos2d-x的入門比較容易,到我這裏反倒成了入門比較難、入門以後比較容易了,由於文檔中的垃圾信息和無效信息實在是太多了,別人能夠照單全收、之後懂了以後再慢慢剔除,我是必須從一開始就本身甄別垃圾、只保留最佳實踐,這也是這篇Blog寫的比較長的緣由。

扯遠了。反正通過以上步驟,就完成了在cocos2d-x項目中把C函數註冊進Lua環境這件事。至此,算是完全搞懂了Lua和C函數之間的互相調用關係,也能在cocos2d-x的Lua環境中使用自定義的C函數了。但這還不夠,由於一個正規的項目是須要狠好的組織結構的,全局C函數滿天飛確定是不行的,好一點的狀況是把全部的C函數都在Lua中組織爲模塊註冊進去,更好一點的狀況是把C++類註冊進Lua、而且C++類也是以Lua模塊爲組織方式註冊進Lua環境的。這其實就是cocos2d-x本身把本身註冊進Lua環境的方式。


第三層:瞭解爲何要使用toLua++來註冊C++類

由於Lua的本質是C,不是C++,Lua提供給C用的API也都是基於面向過程的C函數來用的,要把C++類註冊進Lua造成一個一個的table環境是不太容易一會兒辦到的事,由於這須要繞着彎地把C++類變成各類其餘類型註冊進Lua,至關於用面向過程的思惟來維護一個面向對象的環境。這其中的細節就不去深究了,總之正是由於如此,因此單純地手寫lua_register()等代碼來註冊C++類是行不通的、代價高昂的,因此須要藉助toLua++這個工具。

這一層的知識點看似簡單,但實際上是很是重要的,只有理解了手工用lua_register()去註冊C++類的難度,才能理解使用toLua++這類工具的必要性。只有理解了使用toLua++工具的必要性,纔會潛下心來冷靜地接受toLua++自己的優勢和缺點。只有看到了toLua++自己的缺點和使用上的麻煩,纔會真心理解cocos2d-x使用bindings-generator腳本帶來的好處。只有理解了bindings-generator腳本帶來的好處,才能諒解這個腳本自己在使用上的一些不便之處。


第四層:在純C++環境下,使用toLua++來把一個C++類註冊進Lua環境

雖然終極方法是用bindings-generator腳原本註冊C++類進cocos2d-x的Lua環境,但理解toLua++自己的用法仍是狠有必要的,只有知道了toLua++本來的用法,才能更好地理解cocos2d-x是怎麼把本身的C++類都註冊進Lua環境的,這不只能讓編程時的思路更加清晰,也能爲往後在源碼中尋找各類接口文檔的過程當中不至於看不懂那一大堆tolua_beginmoduletolua_function是什麼意思。影響程序員學習提升的一大障礙就是忽略那些只知其一;不知其二的代碼,不去刨根究底地搞明白。

使用toLua++的標準作法是:

一、準備好本身的C++類,該怎麼寫就怎麼寫
二、仿造這個類的.h文件,改一個.pkg文件出來,具體格式要按照toLua++的規定,好比移除全部的private成員等
三、建一個專門用來橋接C++和Lua之間的C++類,使用特殊的函數簽名來寫它的.h文件,.cpp文件不寫,等着toLua++來生成
四、給這個橋接的C++類寫一個.pkg文件,按照toLua++的特殊格式來寫,目的是把真正作事的C++類給定義進去
五、在命令行下用toLua++生成橋接類的.cpp文件
六、程序入口引用這個橋接類,執行生成的橋接函數,Lua環境中就可使用真正作事的C++類了

toLua++這種本身手寫.pkg文件的方式古老又難受,因此我沒有仔細地去學習,這套流程放在10年前的那個年代是沒有太大問題的,做者怎麼規定就怎麼用好了,可是放在2014年的今天,任何程序的架構設計都講究學習成本低、輕量化、符合以往的習慣,所以toLua++用起來我以爲實際上是難受的。

下面我以儘可能最少的代碼來走一遍toLua++的流程,注意這是在純C++環境下,跟任何框架都不要緊,也不考慮內存釋放等細節:

MyClass.h

class MyClass {
public:
  MyClass() {};

  int foo(int i);
};

MyClass.cpp

#include "MyClass.h"

int MyClass::foo(int i)
{
  return i + 100;
}

MyClass.pkg

class MyClass
{
  MyClass();
  int foo(int i);
};

MyLuaModule.h

extern "C" {
#include "tolua++.h"
}

#include "MyClass.h"

TOLUA_API int tolua_MyLuaModule_open(lua_State* tolua_S);

MyLuaModule.pkg

$#include "MyLuaModule.h"

$pfile "MyClass.pkg"

main.cpp

extern "C" { 
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#include "MyLuaModule.h"

int main()
{
  lua_State *L = lua_open();

  luaL_openlibs(L);

  tolua_MyLuaModule_open(L);

  luaL_dofile(L, "main.lua");

  lua_close(L);

  return 0;
}

main.lua

local test = MyClass:new()
print(test:foo(99))

先在命令行下執行:

tolua++ -o MyLuaModule.cpp MyLuaModule.pkg

此命令用來生成橋接文件MyLuaModule.cpp。注意命令行中-o參數的順序不能隨意擺放,從這個小事也能看出tolua++的古老和難用

生成好MyLuaModule.cpp文件後,就能看到它裏面的那一大堆橋接代碼了,好比tolua_beginmoduletolua_function等。之後看到這些東西就不陌生了,就明白這些函數只是toLua++用來作橋接的必備代碼了,簡單看一下代碼,就理解toLua++是怎樣把MyClass這個C++類註冊進Lua中的了:

toLua++生成的橋接代碼

接下來,用g++來編譯:

g++ MyClass.cpp MyLuaModule.cpp main.cpp -llua -ltolua++

默認就生成了a.out文件,執行,就能看到main.lua的執行結果了:

toLua++的執行結果

至此,對toLua++的運做原理內心就透亮了,無非就是:

一、把本身該寫的類寫好
二、寫個.pkg文件,告訴toLua++這個類暴露出哪些接口給Lua環境
三、再寫個橋接的.h和.pkg文件,讓toLua++去生成橋接代碼
四、在程序裏使用這個橋接代碼,類就註冊進Lua環境裏了


第五層:使用cocos2d-x的方式來將C++類註冊進Lua環境

cocos2d-x在2.x版本里就是用toLua++和.pkg文件這麼把本身註冊進Lua環境裏的。不過這種方法明顯笨拙,既要寫真正作事的.pkg文件,也要寫橋接的.pkg文件和.h文件,工做量又大又枯燥。因此從cocos2d-x 3.x開始,用bindings-generator腳本代替了toLua++。

bindings-generator腳本的工做機制是:

一、不用挨個類地寫橋接.pkg和.h文件了,直接定義一個ini文件,告訴腳本哪些類的哪些方法要暴露出來,註冊到Lua環境裏的模塊名是什麼,就好了,等於將原來的每一個類乘以3個文件的工做量變成了全部類只須要1個.ini文件
二、摸清了toLua++工具的生成方法,改由Python腳本動態分析C++類,自動生成橋接的.h和.cpp代碼,不調用tolua++命令了
三、雖然再也不調用tolua++命令了,可是底層仍然使用toLua++的庫函數,好比tolua_function,bindings-generator腳本生成的代碼就跟使用toLua++工具生成的幾乎同樣

bindings-generator腳本掌握了生成toLua++橋接代碼的主動權,不只能夠省下大量的.pkg和.h文件,並且能夠更好地插入自定義代碼,達到cocos2d-x環境下的一些特殊目的,好比內存回收之類的。因此cocos2d-x從3.x開始放棄了toLua++和.pkg而改用了本身寫的bindings-generator腳本是很是值得讚揚的聰明作法。

接下來講怎麼用bindings-generator腳本:

一、寫本身的C++類,按照cocos2d-x的規矩,繼承cocos2d::Ref類,以便使用cocos2d-x的內存回收機制。固然不這麼幹也行,可是不推薦,否則在Lua環境下對象的釋放狠麻煩。
二、編寫一個.ini文件,讓bindings-generator能夠根據這個配置文件知道C++類該怎麼暴露出來
三、修改bindings-generator腳本,讓它去讀取這個.ini文件
四、執行bindings-generator腳本,生成橋接C++類方法
五、用Xcode將自定義的C++類和生成的橋接文件加入工程,否則編譯不到
六、修改AppDelegate.cpp,執行橋接方法,自定義的C++類就註冊進Lua環境裏了

看着步驟挺多,其實都狠簡單。下面一步一步來。

首先是自定義的C++類。我習慣將文件保存在frameworks/runtime-src/Classes/目錄下:

frameworks/runtime-src/Classes/MyClass.h

#include "cocos2d.h"

using namespace cocos2d;

class MyClass : public Ref
{
public:
  MyClass()   {};
  ~MyClass()  {};
  bool init() { return true; };
  CREATE_FUNC(MyClass);

  int foo(int i);
};

frameworks/runtime-src/Classes/MyClass.cpp

#include "MyClass.h"

int MyClass::foo(int i)
{
  return i + 100;
}

而後編寫.ini文件。在frameworks/cocos2d-x/tools/tolua/目錄下能看到genbindings.py腳本和一大堆.ini文件,這些就是bindings-generator的實際執行環境了。隨便找一個內容比較少的.ini文件,複製一份,從新命名爲MyClass.ini。大部份內容均可以湊合不須要改,這裏僅列出必需要改的重要部分:

frameworks/cocos2d-x/tools/tolua/MyClass.ini

[MyClass]
prefix           = MyClass
target_namespace = my
headers          = %(cocosdir)s/../runtime-src/Classes/MyClass.h
classes          = MyClass

也即在MyClass.ini中指定MyClass.h文件的位置,指定要暴露出來的類,指定註冊進Lua環境的模塊名。

注意,這個地方我踩了個坑。若是.ini配置文件中存在macro_judgement = ...宏定義,要特別當心,我第一次是從cocos2dx_controller.ini文件複製來的,結果沒注意macro_judgement,致使生成的橋接類文件加入了不應加入的宏,只在iOS和Android平臺上才起做用,對Mac平臺無效,這個要特別注意。

而後修改genbindings.py文件129行附近,將MyClass.ini文件加進去:

frameworks/cocos2d-x/tools/tolua/genbindings.py

cmd_args = {'cocos2dx.ini' : ('cocos2d-x', 'lua_cocos2dx_auto'), \
            'MyClass.ini' : ('MyClass', 'lua_MyClass_auto'), \
            ...

(其實這一步原本是能夠省略的,只要讓genbindings.py腳本自動搜尋當前目錄下的全部ini文件就好了,不知道未來cocos2d-x團隊會不會這樣優化)

至此,生成橋接文件的準備工做就作好了,執行genbindings.py腳本:

python genbindings.py

(在Mac系統上可能會遇到缺乏yaml、Cheetah包的問題,安裝這些Python包狠簡單,先sudo easy_install pip,把pip裝好,而後用pip各類pip searchsudo pip install就能夠了)

成功執行genbindings.py腳本後,會在frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目錄下看到新生成的文件:

成功執行genbindings.py後生成的橋接C++文件

每次執行genbindings.py腳本時間都挺長的,由於它要從新處理一遍全部的.ini文件,建議大膽修改腳本文件,靈活處理,讓它每次只處理須要的.ini文件就能夠了,好比像這個樣子:

修改genbindings.py使其只生成自定義的橋接類

frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目錄下觀察一下生成的C++橋接文件lua_MyClass_auto.cpp,裏面的註冊函數名字爲register_all_MyClass(),這就是將MyClass類註冊進Lua環境的關鍵函數:

生成的橋接文件內容

編輯frameworks/runtime-src/Classes/AppDelegate.cpp文件,首先在文件頭加入對lua_MyClass_auto.hpp文件的引用:

AppDelegate.cpp文件的頭加入對lua_MyClass_auto.hpp文件的引用

而後在正確的代碼位置加入對register_all_MyClass函數的調用:

修改AppDelegate.cpp文件,將MyClass類註冊進Lua環境

最後在執行編譯前,將新加入的這幾個C++文件都加入到Xcode工程中,使得編譯環境知道它們的存在:

在Xcode中加入新冒出來的C++文件

這其中還有一個小坑,因爲lua_MyClass_auto.cpp文件要引用MyClass.h文件,而這倆文件分屬於不一樣的子項目,互相不認識頭文件的搜尋路徑,所以須要手工修改一下cocos2d_lua_bindings.xcodeproj子項目的User Header Search Paths配置。特別注意一共有幾個../

須要修改cocos2d_lua_bindings子項目的User Header Search Paths配置

最後,就能夠用cocos compile -p mac命令從新編譯整個項目了,不出意外的話編譯必定是成功的。

修改main.lua文件中,嘗試調用一下MyClass類:

local test = my.MyClass:create()
print("lua bind: " .. test:foo(99))

而後執行程序(用cocos rum -p mac或在Cocos Code IDE中都可),見證奇蹟的時刻~~~~咦我擦?!程序崩潰!爲毛?

第一次運行綁定了C++類的程序竟然崩潰

這是我做爲cocos2d-x初學者遇到的最大的坑,坑了我整整一天半,具體的研究細節就不詳細說了,總之罪魁禍首是cocos2d-x框架中的CCLuaEngine.cpp文件的這段代碼:

CCLuaEngine.cpp執行的這段代碼要了命了

緣由是executeScriptFile函數執行時,對當前Lua環境中的棧進行了清理,當register_all_MyClass函數被調用時,Lua棧是全空的狀態,函數內部執行到tolua_module函數調用時就崩潰了:

CCLuaEngine.cpp中對executeScriptFile方法的定義清理了Lua棧

解決辦法是修改AppDelegate.cpp爲這個樣子:

AppDelegate.cpp調用register_all_MyClass函數時要從新恢復一下棧的內容才行

文本形式的代碼以下:

AppDelegate.cpp

lua_State *L = stack->getLuaState();
lua_getglobal(L, "_G");
register_all_MyClass(L);
lua_settop(L, 0);

從新編譯並執行,程序就正確執行了:

程序終於正確執行了

至此,就完全搞清楚應該怎樣在cocos2d-x項目裏綁定一個C函數或者C++類到Lua環境中了,感興趣的話能夠再進一步深刻研究Lua內部metatable的運做原理、類對象的生成與釋放、以及垃圾回收。我本身也是剛接觸cocos2d-x不到一個星期,理解不深,以上不免會有用詞不當或理解錯誤的地方,若有錯誤請多包涵。

後記補充:若是C++類定義了namespace,則須要修改frameworks/cocos2d-x/tools/bindings-generator/targets/lua/conversions.yaml文件,定義namespace與Lua之間的映射關係,不然會報conversion wasn't set錯誤:

C++的類若是定義了namespace,則須要在導出Lua時修改conversions.yaml文件

--
參考資料:

Calling C++ Functions From Lua
子龍山人:Lua教程(4)Lua調用C/C++函數
wtyqm:tolua++實現分析
wtyqm:cocos2dx的lua綁定
cocos2d-x-lua如何導出自定義類到lua腳本環境
How to bind a custom class to lua runtime
如何使用 bindings-generator 自動生成 lua綁定
cocos2d-x 3.0 + lua tolua_module崩潰問題與解決吐槽
cocos2d-x 3.0rc0 - bindings-generator 問題與解決

相關文章
相關標籤/搜索