现在我们的需求是:在c++层定义了一些类,我们需要将这些类导出给Lua来使用,从而完成在c++层实现起来容易的需要,这个时候就需要将整个类作为模块导出。而Cocos2d-x正是采用的这种思想,将Cocos中的类导出供用户使用,而不是再写一套Lua代码,用户使用Cocos导出的这套接口,在Lua脚本层写游戏代码。为了更好的理解这部分的内容,可以先看一下在Lua中调用c++函数这篇博客,了解c++中调用Lua的机制。该博客中我们将c++中需要导出的函数放到了一个模块中,其中我们手动的做了一些工作。然而Lua的本质是C,不是C++,Lua提供给C用的API也都是基于面向过程的C函数来用的,要把C++类注册进Lua形成一个一个的table环境是不太容易一下子办到的事。为了实现我们的需求,同样也是官方的需求,在Cocos 2.x版本的时候,使用的是tolua++这个工具,但是这个工具用起来相当的麻烦,耗费体力,所以现在使用的是bindings-generator工具(官方用Python写的一个工具),这个东西底层使用的也应该是tolua++。

我没有使用过tolua++这个工具,不过从网上了解了一下这个工具的使用方法,大致是这样的:1、写自己的c++类,该怎么写就怎么写。2、根据需要导出的c++类的头文件写对应的.pkg文件,具体怎么写得参照tolua++格式。3、写一个桥接的类,只写它的头文件,cpp文件是使用tolua++工具来生成的,头文件具体怎么写,也是需要按照tolua++的格式。4、给这个桥接的类写一个.pkg文件。5、使用命令生成桥接类的.cpp文件。6、程序中使用桥接的类,执行一些先关的函数。所以如果使用的是tolua++工具的话,至少需要写三个文件,导出类的.pkg文件,桥接类的.h文件,桥接类的.pkg文件,可想而知还是比较麻烦的。现在好了,我们有了bindings-generator工具,下面就来具体的使用一下吧。

1、使用Cocos Code IDE建立一个Lua工程,创建工程的时候记住选中add native code这个选项,这样会在工程目录下生成frameworks这个文件夹,里边是c++层的代码。

绑定自定义类到Runtime

2、进入到frameworks的工程目录下,打开对应的工程,我是在Mac下使用,所以打开的就是Xcode工程。

绑定自定义类到Runtime

3、在工程中我们写自己的类,这个类就是你要导出给Lua层用的接口,实际的需求可能是你一直在c++层写代码,某一部分的功能需要在Lua层来实现,这个时候有一些类需要导出给Lua使用,这里我们只是模拟一下,写一个简单的类好了。

绑定自定义类到Runtime

TestLua头文件和cpp文件的内容如下。

#ifndef __Test11__TestLua__
#define __Test11__TestLua__

#include "cocos2d.h"

USING_NS_CC;

class TestLua : public Ref
{
public:
    bool init(){return true;};
    CREATE_FUNC(TestLua);
    int show(int);
};

#endif /* defined(__Test11__TestLua__) */
#include "TestLua.h"

int TestLua::show(int arg)
{
    log("xiaota show %d",arg);
    return arg+100;
}

文件写好了,这个时候去工程中看一下TestLua的文件位置,发现不是在Classes目录下,为了方便操作,我把这俩个文件复制到了Classes目录下一份。

绑定自定义类到Runtime

4、现在需要做的就是导出这个类的接口给Lua使用了。现在进入工程的frameworks/cocos2d-x/tools/bindings-generator目录下,仔细阅读README.md文档,将必须配置的工具和环境变量配置好。

绑定自定义类到Runtime

然后进入tolua目录,原目录下只有genbindings.py,我把这个文件复制了一份命名为genbindings_xiaota.py,然后复制了一个cocos2dx.ini文件,重新命名为xiaota.ini。genbindings.py文件是一个Python脚本,这个脚本在执行的过程中会读取.ini的配置文件,然后根据这些个配置文件生成我们需要的桥接类。

绑定自定义类到Runtime

现在我们就来修改一下这俩个重要的文件,网上有不少的童鞋没有成功的关键大多数是因为这俩个文件修改的不当所导致的,所以这一步至关重要!如下图所示,output_dir是将生成的桥接类放到哪个文件夹下,我这里放置的地方和引擎将桥接类放置的地方是相同的,cmd_args是在脚本执行的过程中读取的配置文件,我的配置文件是xiaota.ini,所以第一个参数就是xiaota.ini,其中你看到的lua_xiaota_auto这个东西就是最后生成的桥接类的名字。

绑定自定义类到Runtime

genbindings.py文件还是比较简单的,我们再来修改xiaota.ini文件,打开文件,我们有以下的五处需要修改。中括号中的内容和你文件名相同,prefix同样和文件名相同,主要的是target_namespace,它的值代表的就是模块名,比如Cocos中的模块名cc,这里我使用的模块名是tt(塔塔,呵呵)。headers代表的是头文件的路径,我引用的是Classes下的文件,classes代表的就是类名。如果你有多个文件,可以参照cocos2d.ini中的写法去写,我们写这个文件的时候也是复制的官方的文件,然后在这个基础上进行的修改,所以,以后不论官方如何变化,复制它的文件,然后修改这几个关键的地方就好了。

绑定自定义类到Runtime 绑定自定义类到Runtime

5、接下来就需要使用Python脚本来生成我们的桥接类了,网上不少的同学最后的结果都是失败,为了排除错误,你可以先运行genbindings.py,看看是否成功,如果不成功那么就是你环境配置的有问题,如果成功再运行自己的Python文件,不成功的话80%的错误都是ini配置文件没有配置正确。

绑定自定义类到Runtime 绑定自定义类到Runtime

编译成功,我们可以到刚才生成桥接类的文件夹下看一下这个文件。

绑定自定义类到Runtime

在auto文件夹下,lua_xiaota_auto.hpp和lua_xiaota_auto.cpp就是我生成的桥接类,在api文件夹下的TestLua.lua就是导出的LuaAPI。

6、下面我们就使用这个桥接类来将我们的类注册到Lua环境中。打开AppDelegate.cpp文件,包含一下桥接类lua_xiaota_auto.hpp,然后在applicationDidFinishLaunching函数中写如下的代码。

绑定自定义类到Runtime 绑定自定义类到Runtime

其中的register_all_xiaota就是用来注册类的,它是桥接类中的一个比较重要的函数。

绑定自定义类到Runtime

7、为了编译这个桥接类,我们需要将这个新生成的类加入到工程中,右键工程添加文件,添加进来桥接类。这个时候你会发现桥接类的.cpp文件处说找不到我们自己的类TestLua。所以还需要设置一下search的路径。

绑定自定义类到Runtime绑定自定义类到Runtime绑定自定义类到Runtime

8、经过这些步骤以后基本就算成功了,现在需要打开Cocos Code IDE的工程,重新build一下runtime,跑程序的时候我们使用这个最新的runtime,大家需要明确的一点是当我们改变c++层的代码的时候就需要重新build一下runtime,说白了就是重新编译一下c++的文件,让Lua脚本运行在一个新的环境下。

9、打开工程的main.lua,在工程中使用导出来的接口,最终来验证一下是否成功。

local function main()
    collectgarbage("collect")
    -- avoid memory leak
    collectgarbage("setpause", 100)
    collectgarbage("setstepmul", 5000)

    local val = tt.TestLua:create():show(10)
    print(val)

end

绑定自定义类到Runtime

大功告成!以后的binding只要参照这个思路来做一切OK!