最近开始使用cocos2dx进行游戏开发,过程中发现有些东西需要使用c++进行实现,暴露给lua层使用,所以研究了下cocos2dx自带的tolua插件功能,发现网上相关的资料都写得比较粗略,故自己总结一篇,以加深印象。
一、环境配置(win)
lua绑定所需要的环境,可以通过查看frameworks/cocos2d-x/tools/tolua目录下的README文档得知,按照文档要求配置相关环境即可:
- python:版本2.7.5 ,按照完成后需要在系统变量中的path后加上python的安装路径,一般都是c/python27
- ndk:具体版本查看README,在系统变量中新添变量名NDK_ROOT,变量为ndk解压的目录
- 下载PyYAML-3.10.win32-py2.7.exe,安装到python目录下,一般会自动安装到python目录下,无需手动选择。
- 下载Cheetah,将其放在C:\Python27\Lib\site-packages目录下
二、编写C++类
我们可以先编写个简单的c++类进行测试,编写好代码后,把代码加入到项目中,我在项目中是把代码放在frameworks/runtime-src/Classes/core/lua目录下,c++代码如下:
// MyClass.h
#pragma once
#include "cocos2d.h"
using namespace cocos2d;
class MyClass : public Ref
{
public:
MyClass();
~MyClass();
bool init()
{
return true;
}
CREATE_FUNC(MyClass);
int foo(int i);
};
// MyClass.cpp
#include "MyClass.h"
MyClass::MyClass()
{
}
MyClass::~MyClass()
{
}
int MyClass::foo(int i)
{
return i + 100;
}
三、编写ini文件
代码已经写好了,后面就要开始使用tolua进行lua绑定了。cocos2dx本质上是使用tolua++工具进行lua绑定的,而且引擎维护团队还编写了一个转换工具,使得我们可以只通过修改配置参数后就可以导出相关代码。相关转换工具在目录:frameworks/cocos2d-x/tools/tolua下,在目录里我们可以看到很多的ini文件和一个genbindings.py文件。打开cocos2dx.ini文件,我们可以看到这个文件有很多配置项,注释也比较全面,下面我们主要了解下重要的几个配置项:
// cocos2dx.ini
[title] //第一行,文件标题,这个是当前ini文件的文件名,我们直接填写文件名
//如下这些配置保持原样即可
android_headers
android_flags
clang_headers
clang_flags
cocos_headers
cocos_flags
cxxgenerator_headers
extra_arguments
//前缀字段,这里自定义一个前缀,后续这个前缀会自动添加到绑定的所有函数名里面去。
prefix =
//这里定义了你所绑定到lua里面的类是否需要一个命名空间,比如cocos中很多lua方法都使用'cc.'进行调用,cc就是这个命名空间,如果这里定义了命名空间,那就必须在:framework/cocos2d-x/tools/blindings-generator/targets/lua目录下找到conversions.yaml文件,修改其中ns_map字段,把自定义的命名空间添加上去“ccmy::”: "ccmy",当然,你也可以为空。
target_namespace =
//配置c++代码头文件路径,多个文件使用空格隔开,建议使用一个头文件引用其他头文件。路径可以使用%(cocosdir)s/表示framework/cocos2d-x/路径,后面在跟子路径即可。
headers =
// 配置需要绑定的类,多个类的话,使用空格隔开。
classes =
// 这个配置哪些public函数不需要提供给lua
skip =
// 还剩下一些配置,直接留空即可,具体功能,注释和从名称中可以很直观的知道
rename_functions =
rename_classes =
remove_prefix =
classes_have_no_parents =
base_classes_to_skip =
abstract_classes =
script_control_cpp = no
一般般来说,我们拷贝一份cocos2dx.ini,重命名,比如:myClass.ini,然后我们只要配置上面详细注释的这些字段即可,配置好了后,我们还需要拷贝一份genbindings.py文件,重命名,比如:myClass.py,这个文件是一个python文件,如果懂python的同学可以仔细阅读下代码,但是我们使用的话,只要修改几个地方即可,如下:
// NDK目录,如果我们在环境配置那步配置了NDK环境变量的话,这个字段可以不用修改,否则我们需要修改下NDK目录。
ndk_root = "F:android-ndk-r16b"
// 文件导出目录,这个字段是配置我们使用tolua生成的文件最终导出的目录,建议专门新建一个目录进行管理,这样便于后续维护。
output_dir =
// 这个字段表示:在myClass.ini中找到title为myClass的模块,然后生成JSCCMyClass类文件,他会把所有myClass.ini中定义到的类都生成到JSCCMyClass类文件中。
cmd_args = {'myClass.ini' : ('myClass', 'JSCCMyClass')}
OK,如果你配置好了上述相关信息,下一步就可以使用python执行myClass.py文件了,进入到myClass.py文件所在的目录,执行该文件,如果不出现意外,则会出现成功的信息,然后就会在output_dir配置的文件夹下看到相关的生成代码了,当然,这个过程中可能会出现一些错误,下面是常见的错误:
- 1、环境配置错误,可以根据相关错误提示,得到环境配置错误信息。
- 2、如果在ini文件中配置了target_namespace命名空间,但是没有在conversions.yaml上进行声明,也会报错
- 3、还有一个就是NDK路径配置错误,这样也会导致生成失败。
四、Lua中进行调用
如果前面步骤都顺利完成了,那恭喜你,离成功马上就到了。在前面步骤都完成后,我们会得到2个文件,JSCCMyClass.cpp和JSCCMyClass.hpp,我们把这2个文件也加入到项目中,在JSCCMyClass.cpp中可能会出现文件引用错误,我们修改好就可以了。
下一步,我们要把文件注册给lua使用,在哪里注册呢?我们找到AppDelegate.cpp文件,在applicationDidFinishLanching函数里进行注册,最好我们创建一个函数专门用于lua注册,这样比较利于项目管理,注册代码如下:
// 获取lua堆栈,所有的lua和c的交互都是通过lua_State来进行的,如果不熟悉的同学,可以看下lua教程中关于C api相关章节。
lua_State* L = engine->getLuaStack()->getLuaState();
// 调用JSCCMyClass里面的注册方法
register_all_JSCC(L)
对的,就是这样,然后运行项目,就可以在lua中调用相关c的方法了。
local myClass = MyClass:create()
myClass:foo(100)
还有一个特例,如果你使用了target_namespace字段设置命名空间的话,那就需要修改tolua生成的cpp文件中修改下lua注册方法,比如上面的例子中,需要找到JSCCMyClass.cpp的register_all_JSCC函数,修改为:
TOLUA_API int register_all_JSCC(lua_State* tolua_S)
{
lua_getglobal(tolua_S, "_G"); // 新增修改代码
if (lua_istable(tolua_S, -1)) //新增修改代码 {
tolua_open(tolua_S);
tolua_module(tolua_S, "ccmy", 0);
tolua_beginmodule(tolua_S, "ccmy");
lua_register_JSCC_MyClass(tolua_S);
tolua_endmodule(tolua_S);
}
lua_pop(tolua_S, 1); //新增修改代码
return 1;
}
如此,我们就可以在lua中通过下面这种方式进行调用了:
local myClass = ccmy.MyClass:create()
myClass:foo()
the end!