Cocos2dx 3.x 使用 Python 进行 Lua 绑定,除了为每个类定制配置文件 *.ini 之外,还需要修改可执行脚本 generator.py。
版本之痛,痛中之痛;环境之难,难中之难。
cocos2d-x3.x 与 2.x 很不一样,关于 Lua 绑定也不同。在 2.x 版本,引擎目录下有个 tolua++.exe 文件,通过包描述文件 *.tolua,执行脚本 build.bat 来一键导出。而在 3.x 版本,描述文件换成了 *.ini,然后编写 python 脚本 generator.py,使用 python 来导出。
工程结构
首先来看一下 cocos2d-x3.x 的源码目录结构,
1 | |-cocos2d-x-3.x |
build 为工程目录,docs 帮助文档,tests 官方示例,templates 为创建项目时使用的模板,extensions 为源码的一些扩展,external 为外部库,主要是一些第三方的基础库,比如物理引擎、md5、json 等等。重点要介绍的是 tools 和 cocos 这两个文件夹。
cocos目录是 cocos2d-x 引擎的 C++ 源码,其中cocos/scripting为导出的脚本,包括 js 和 lua,分别放在cocos/scripting/lua-bindings和cocos/scripting/js-bindings目录下;cocos/scripting/lua-bindings/auto为 tolua 生成文件存放的地方。tools目录下是 cocos2d-x 非常实用的一些工具,包括创建项目、编译项目、运行项目的控制台命令等,这里我们要看的是tolua和bindings-generator这两个文件夹;tolua下是包描述文件和可执行脚本genbindings.py,binding-generator目录下则是需要用到的一些库或脚本。
环境配置
在开始之前需要做一个准备工作,就是配置环境,主要配置的是 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 目录下即可。
Lua 绑定
首先,跟写普通 C++ 程序一样,我们先写一个要导出的 C++ 类 MyClass。
1 | //MyClass.h |
接下来就可以准备导出这个类了,使用的是 tools/tolua/genbindings.py 以及每个类对应的 ini 文件,这个 python 脚本可以导出 cocos2d-x 中的所有类,我们可以修改这个脚本,添加我们自定义的类,也可以模仿这个脚本另外写一个脚本,然后执行我们自己写的这个脚本。为了防止破坏源代码,我们使用第二种,把 genbindings.py 复制一份,命名为 genbindings_myclass.py,再把 cocos2dx.ini 复制一份,命名为 myclass.ini。在引擎源码下创建一个 custom 目录,然后把 myclass.ini 和 C++ 源文件统一放在这个目录下,而 genbindings_myclass.py 则仍放在 tools/tolua 目录下,这样改动最小。
1 | |-cocos2d-x-3.x |
修改 genbindings_myclass.py:
tolua_root为包描述文件myclass.ini的路径,即project_root/cocos/custom;output_dir为生成文件的路径,我们放在project_root/cocos/custom/auto目录下;cmd_args指定使用的配置文件以及导出的文件名,这是一个 table,可以指定多个 ini 配置文件,一个配置文件可以配置一个类或多个类,格式为"ini_file" : ("section", "output_file")。这里我们只需要一个myclass.ini,section为myclass,在myclass.ini的第一行定义,导出的文件名为lua_myclass_auto。
1 | # genbindings_myclass.py |
修改 myclass.ini:
1 | # section,与上面 cmd_args 配置的一致 |
总结一下就是创建了 genbindings_myclass.py 和 myclass.ini 这两个文件,接下来在 tools/tolua 文件夹下打开命令行,输入 python genbindings_myclass.py。 如果没错误,将在 tests/custom/auto 目录生成相应的文件。
1 | |-cocos2d-x-3.x |
如果报错,检查一下 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,添加下面内容。
1 |
|
register_all_myclass 在 lua_myclass_auto.hpp 中声明,所以要引入这个头文件,在 lua_myclass_auto.cpp 中实现,我们可以看看这个函数,
1 | TOLUA_API int register_all_myclass(lua_State* tolua_S) |
总的来说就是打开 tolua,把 c++ 层的函数注册到 lua 环境中,之后就可以在 lua 中直接使用这些函数了。具体的可以看我这篇文章tolua 用法。
接下来打开 src/app/views/MainScene.lua,开始测试我们的例子,
1 | local MainScene = class("MainScene", cc.load("mvc").ViewBase) |
这里我们创建 MyClass 的一个实例,然后调用其 excute 方法;注意我们前面导出的时候定义其命名空间为 cc,所以这里要写 cc.MyClass,否则会报错。接下来就是见证成果的时候了,编译运行项目,看到下面结果就表示大功告成了。
常见问题
1. NDK 版本不对
1 | ==== |
如果出现这个错误,是因为 NDK 工具链 llvm 匹配不上,最简单直接的办法就是换一个版本的 NDK,通常是使用了版本太高的 NDK 才会出现,比如我之前电脑上装了 r10 和 r20 两个版本,不知道什么时候环境变量设成了 NDK_ROOT = D:\Android\android-ndk-r20,把它换成 NDK_ROOT = D:\Android\android-ndk-r10e 就行了。注意要删掉 tools/tolua/userconfi.ini,然后重启终端再执行就可以了。
2. 类名、头文件路径写错
TranslationUnitLoadError: Error parsing translation unit.
如果报这个错,检查一下类名有没有写错,头文件路径是否正确。
<!https://www.jianshu.com/p/48a6fb7123ee>
3. 命名空间解析不了
Exception: The namespace (flash::FlashSpriteFrame) conversion wasn't set in 'ns_map' section of the conversions.yaml
其实这个报错信息很明显了,在 conversions.yaml 的 ns_map section 中没找到这个命名空间的映射。也就是说如果定义了新的命名空间,不仅要在 ini 文件中定义 target_namespace 和 cpp_namespace 这两个字段,还要在 conversions.yaml 中定义这两个命名空间的映射关系,格式为 "cpp_namespace::": "target_namespace."。
4. 脚本工作目录错误
No option 'cxxgeneratordir' in section: 'DEFAULT',报这个错主要是没有进入到脚本所在的目录去执行脚本,有些配置路径就不对了。
to be continue…