开始之前
首先来看一下 cocos2d-x3.x 的源码目录结构(这篇文章以 3.10 为例,只列出重要的几个目录和文件)
|-cocos2d-x3.10
|-tools
|-tolua
|-genbindings.py
|-cocos2dx.ini
|-cocos2dx_ui.ini
|-...
|-README.mdown
|-bindings-generator
|-libclang
|-libclang.dll
|-libclang.so
|-libclang.dylib
|-clang
|-cindex.py
|-generator.py
|-cocos
|-2d
|-...
|scripting
|-js-bindings
|-lua-bindings
|-auto
|-api
|-Action.lua\
|-...
|-lua_cocos2dx_auto.hpp
|-lua_cocos2dx_auto.cpp
|-lua_cocos2dx_ui_auto.hpp
|-lua_cocos2dx_ui_auto.cpp
|-...
主要列出了 tools 和 cocos 这两个文件夹下的主要内容,cocos 目录是 cocos2d-x 引擎的源码(C++),其中 lua-bindings 目录下是导出的脚本语言,包括 js 和 lua;在 lua-bindings 下有个 auto 文件夹,这就是 tolua 生成文件存放的地方。tools 目录下是 cocos2d-x 非常实用的一些工具,包括创建项目、编译项目、运行项目的控制台命令等,这里我们要看的是 tolua 和 bindings-generator 这两个文件夹;tolua 目录毫无疑问就是这篇文章的主角,接下来我们要做的工作就是在这个目录下完成的;binding-generator 目录下是一些 tolua 会用到的文件,其实我们不用理会,这里列出来只是为了告诉大家 tolua 会用到这几个文件。
准备工作
在开始之前我们需要做一个准备工作,就是配置环境。cocos 环境和 android 环境就不用讲了,还需要配置的是 python 环境,因为 cocos2d-x 的 tolua 是使用 python 脚本来执行的。这个工作看 tools/tolua/README.mdown 这个文件就行了,下面翻译一下
- 首先,安装 ndk 并配置环境变量 NDK_ROOT
- 然后,下载安装 python2.7,注意必须安装 32 位版本,下载地址
python2.7.3 - 配置 python 环境变量 PYTHON_BIN
- 下载安装 python 库 pyyaml,下载地址
pyyaml,pyyaml 安装的时候会读取 python 安装路径并将结果安装在 %PYTHON_BIN%\Lib\site-packages 目录下 - 下载 python 库 pyCheetah,下载地址
pyCheetah,下载之后解压到 %PYTHON_BIN%\Lib\site-packages 目录下即可
使用 tolua
首先,跟写普通 c++ 程序一样,我们先写一个要导出的 c++ 类 MyClass
头文件
//MyClass.h
class MyClass
{
public:
MyClass(int a, int b);
~MyClass();
int excute();
private:
int a;
int b;
};
源文件
//MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(int a, int b)
{
this->a = a;
this->b = b;
}
MyClass::~MyClass()
{
}
int MyClass::excute()
{
return a + b;
}
接下来就可以准备导出这个类了,使用的是 tools/tolua/genbindings.py,这个 python 脚本可以导出 cocos2d-x 中的所有类,我们可以修改这个脚本,添加我们自定义的类,也可以模仿这个脚本另外写一个脚本,然后执行我们自己写的这个脚本。为了防止破坏源代码,我们使用第二种,把 genbindings.py 复制一份,命名为 genbindings_myclass.py,然后修改下面两处地方
# genbindings_myclass.py
tolua_root = '%s/tools/tolua' % project_root
output_dir = '%s/tests/custom/auto' % project_root
cmd_args = {'myclass.ini' : ('myclass', 'lua_myclass_auto')}
tolua_root 是 tolua 的根目录,不用修改;output_dir 是 tolua 生成的文件存放路径,原来的路径是 cocos/scripting/lua-bindings/auto,为了不和 cocos2d-x 的源代码混在一起,我们另外 cocos2d-x3.10/tests 目录新创建文件夹 custom/auto,把我们前面创建的 MyClass 类也放进去(这个路径随便放都行,只要配置文件中的路径不要写错就行)
|-cocos2d-x3.10
|-tests
|-custom
|-MyClass.h
|-MyClass.cpp
|-auto
cmd_args 是一个 table,指定我们要导出的 c++ 类,key 是一个 ini 配置文件,value 指定源文件名和导出文件名。cmd_args 可以指定多个 ini 配置文件,一个配置文件配置一个类或多个类;这里我们需要一个 myclass.ini,这个文件从哪来呢,直接在 tools/tolua 目录复制一个即可,复制之后修改下面内容
# 源文件名,与上面 cmd_args 配置的一致
[myclass]
prefix = myclass
# 导出的类所在命名空间,在 lua 层使用时需要用到,不写也行,表示在全局空间
target_namespace = cc
# 要导出的 c++ 类头文件路径
headers = %(cocosdir)s/tests/custom/MyClass.h
# 要导出的类名
classes = MyClass
skip =
abstract_classes =
总结一下就是创建了 genbindings_myclass.py 和 myclass.ini 这两个文件,接下来在 tools/tolua 文件夹下打开命令行,输入
python genbindings_myclass.py
如果没错误,将在 tests/custom/auto 目录生成相应的文件
|-cocos2d-x3.10
|-tests
|-custom
|-MyClass.h
|-MyClass.cpp
|-auto
|-lua_myclass_auto.hpp
|-lua_myclass_auto.cpp
|-api
|-MyClass.lua
|-lua_myclass_auto.lua
如果报错,检查一下 genbindings_myclass.py 和 myclass.ini 有没有写错,特别是源文件路径 headers 和 目标文件 output_dir。还有就是 python,pyyaml 和 pyCheetah 是不是全装的 32 位,还有 python 环境变量是否配置对;当然 MyClass.h 和 MyClass.cpp 肯定也不能有错误。
测试
新建一个 lua 项目,把 MyClass.h,MyClass.cpp,lua_myclass_auto.hpp 和 lua_myclass_auto.cpp 这四个文件拷到项目中。然后打开 AppDelegate.cpp,添加下面内容
#include "lua_myclass_auto.hpp"
//...
bool AppDelegate::applicationDidFinishLaunching()
{
//...
//register custom function
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
register_all_myclass(stack->getLuaState());
#endif
//...
}
register_all_myclass
在 lua_myclass_auto.hpp 中声明,所以要引入这个头文件,在 lua_myclass_auto.cpp 中实现,我们可以看看这个函数
TOLUA_API int register_all_myclass(lua_State* tolua_S)
{
tolua_open(tolua_S);
tolua_module(tolua_S,"cc",0);
tolua_beginmodule(tolua_S,"cc");
lua_register_myclass_MyClass(tolua_S);
tolua_endmodule(tolua_S);
return 1;
}
总的来说就是打开 tolua,把 c++ 层的函数注册到 lua 环境中,之后就可以在 lua 中直接使用这些函数了。具体的可以看我这篇文章
tolua 用法
接下来打开 src/app/views/MainScene.lua,开始测试我们的例子
local MainScene = class("MainScene", cc.load("mvc").ViewBase)
function MainScene:onCreate()
local test = cc.MyClass:new(10, 20)
local num = test:excute()
local str = "Hello World" .. num
cc.Label:createWithSystemFont(str, "Arial", 40)
:move(display.cx, display.cy)
:addTo(self)
end
return MainScene
这里我们创建 MyClass 的一个实例,然后调用其 excute 方法;注意我们前面导出的时候定义其命名空间为 cc,所以这里要写 cc.MyClass,否则会报错。这种 tolua 导出方式是针对 android 和 ios 的,所以在 pc 下是不行的,看一下 lua_myclass_auto.hpp 的源码就知道了
#include "base/ccConfig.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#ifndef __myclass_h__
#define __myclass_h__
#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif
int register_all_myclass(lua_State* tolua_S);
#endif // __myclass_h__
#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
接下来就是见证成果的时候了,打包安卓 apk,然后安装运行,看到下面结果就表示大功告成了