cocos2dx lua 绑定详解与实战

13k 词

我们平时在开发cocos2dx lua游戏的时候的,会遇到这样的情况:

  • 在c++层定义了一些类,我们需要将这些类导出给Lua来使用,从而完成在c++层实现起来容易的需求,这个时候就需要将整个类作为模块导出。

而Cocos2d-x正是采用的这种思想,将Cocos中的类导出供用户使用,而不是再写一套Lua代码,用户使用Cocos导出的这套接口,在Lua脚本层写游戏代码。

为了更好的理解这部分的内容,可以先了解c++中调用Lua的机制。

推荐

前面文章中,我们说到了,Lua的本质是C,不是C++,Lua提供给C用的API也都是基于面向过程的C函数来用的,要把C++类注册进Lua形成一个一个的table环境是不太容易一下子办到的事。

为了实现我们的需求,同样也是官方的需求,在Cocos 2.x版本的时候,使用的是tolua++这个工具,但是这个工具用起来相当的麻烦,耗费体力,所以现在使用的是bindings-generator工具(官方用Python写的一个工具),这个东西底层使用的也应该是tolua++。

这里只针对iOS平台,Android和其他平台在tolua中README.mdown中有具体介绍,其他步骤基本上一样!

在项目跟目录framework/cocos2d-x/cocos中创建mybinding文件夹,里面创建一个MyBinding.h文件(mybinding, MyBinding自定义),输入如下测试代码

#include "cocos2d.h"

namespace cocos2d {
class MyBinding: public Ref {
public:

virtual bool init() {
return true;
}

void sayBinding() {
log("Hello Binding Lua");
}

MyBinding();
~MyBinding();
}
}

在framework/cocos2d-x/tools/tolua,新建一个配置文件,这里命名cocos2dx_binding.ini

输入下面代码:(处理标记修改处其他所有都一样)

[cocos2dx_binding] # 标记修改
# the prefix to be added to the generated functions. You might or might not use this in your own
# templates
prefix = cocos2dx_binding # 标记修改

create a target namespace (in javascript, this would create some code like the equiv. to ns = ns || {})

all classes will be embedded in that namespace

target_namespace = cc # 标记修改

--------- ?

#android_headers = -I%(androidndkdir)s/platforms/android-19/arch-arm/usr/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/include
android_headers = -I%(androidndkdir)s/platforms/android-19/arch-arm/usr/lib -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/include

--------- ?

android_flags = -D_SIZE_T_DEFINED_

#clang_headers = -I%(clangllvmdir)s/lib/clang/%(clang_version)s/include
clang_headers = -I%(clangllvmdir)s/lib64/clang/5.0/include
clang_flags = -nostdinc -x c++ -std=c++11 -U SSE

--------- ?

-I%(cocosdir)s/jsext -I%(cocosdir)s/jsext/system -I%(cocosdir)s/jsext/alipay -I%(cocosdir)s/jsext/video -I%(cocosdir)s/jsext/webview -I%(cocosdir)s/jsext/umeng

cocos_headers = -I%(cocosdir)s -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/base -I%(cocosdir)s/cocos/platform/android -I%(cocosdir)s/extensions -I%(cocosdir)s/external -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s/cocos/network -I%(cocosdir)s/cocos/ui/UIEditBox -I%(cocosdir)s/cocos/ui
#cocos_headers = -I%(cocosdir)s -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/platform/android

cocos_flags = -DANDROID

cxxgenerator_headers =

extra arguments for clang

extra_arguments = %(android_headers)s %(clang_headers)s %(cxxgenerator_headers)s %(cocos_headers)s %(android_flags)s %(clang_flags)s %(cocos_flags)s %(extra_flags)s

what headers to parse 头文件路径

headers = %(cocosdir)s/cocos/mybinding/MyBinding.h # 标记修改

what classes to produce code for. You can use regular expressions here. When testing the regular

expression, it will be enclosed in "^", like this: "^Menu*".

#包含的类,新添加文件需要修改
classes = MyBinding.* # 标记修改

#需要在js里面派生的类
#classes_need_extend = MyBinding # 标记修改

what should we skip? in the format ClassName::[function function]

ClassName is a regular expression, but will be used like this: "^ClassName$" functions are also

regular expressions, they will not be surrounded by "^$". If you want to skip a whole class, just

add a single "" as functions. See bellow for several examples. A special class name is "", which

will apply to all class names. This is a convenience wildcard to be able to skip similar named

functions from all classes.

skip =

rename_functions =

rename_classes =

for all class names, should we remove something when registering in the target VM?

remove_prefix =

classes for which there will be no "parent" lookup

classes_have_no_parents =

base classes which will be skipped when their sub-classes found them.

base_classes_to_skip = Ref

classes that create no constructor

Set is special and we will use a hand-written constructor

abstract_classes =

Determining whether to use script object(js object) to control the lifecycle of native(cpp) object or the other way around. Supported values are 'yes' or 'no'.

script_control_cpp = no

注意检查一下三个头文件对应的路径及文件问题

  • android_headers
  • clang_headers
  • cocos_headers
说明内容:
+ •[title]:要配置将被使用的工具/ tolua的/ gengindings.py脚本的称号。一般来说,标题可以是文件名。
+ •prefix:要配置一个函数名的前缀,通常,我们还可以使用文件名作为前缀。
+ •target_namespace:要配置在脚本层模块的名字。在这里,我们使用cc作为模块名,当你想在脚本层REF的名称,您必须将一个名为前缀,CC在名称的前面。例如,CustomClass可以参考作为cc.CustomClass。
+ •headers:要配置所有需要解析的头文件和%(cocosdir)s是的Cocos2d-x的引擎的根路径。
+ •classes:要配置所有绑定所需的类。在这里,它支持正则表达式。因此,我们可以设置MyCustomClass。*在这里,用于查找多个特定的用法,你可以对照到tools/tolua/cocos2dx.ini。
+ •skip:要配置需要被忽略的功能。现在绑定发电机无法解析的void *类型,并委托类型,所以这些类型的需要进行手动绑定。而在这种情况下,你应该忽略所有这些类型,然后再手动将它们绑定。你可以对照到配置文件路径下的cocos/scripting/lua-bindings/auto 。
+ •rename_functions:要配置的功能需要在脚本层进行重命名。由于某些原因,开发者希望更多的脚本友好的API,所以配置选项就是为了这个目的。
+ •rename_classes:不在使用。
+ •remove_prefix:不在使用。
+ •classes_have_no_parents:要配置是过滤器所需要的父类。这个选项是很少修改。
+ •abstract_classes:要配置的公共构造并不需要导出的类。
+ •script_control_cpp:是的。要配置脚本层是否管理对象的生命周期。如果没有,那么C++层关心他们的生命周期。现在,它是不完善的,以控制原生对象的续航时间在脚本层。所以,你可以简单地把它设置为no

修改framework/cocos2d-x/tools/tolua里面的genbindings.py。有个cmd_args键值对的配置,增加下面代码,作为自定义绑定配置

'cocos2dx_binding.ini' : ('cocos2dx_binding', 'lua_cocos2dx_binding_auto'), 

注:python注释为#,这里将cmd_args其他的元素注释掉是因为这些文件都是生成过得,没必要再生成浪费时间
这行代码表示在cocos2dx_custom中找到cocos2dx_custom的模块,然后生成lua_cocos2dx_custom_auto文件

这里要确保NDK_ROOT,和PYTHON_BIN安装切配置好了,然后在framework/cocos2d-x/tools/tolua执行

./genbindings.py

之前./genbindings.py之前,请先查看官方tolua中README文件,按照对应的流程,安装好需要的依赖,和相应的库,下面是我这边3.17最新的README

* The OSX 10.1<!--0 has a built-in python2.7 and if your os don't have python2.7 then use [Homebrew](http://brew.sh/) to install the python and use pip install the python dependencies.
<pre>
brew install python
</pre>
  • Install python dependices by pip.
    <pre>
    sudo easy_install pip
    sudo pip install PyYAML
    sudo pip install Cheetah
    </pre>

  • Download NDK 64bit r10c or later from Android Ndk

  • Run
    <pre>
    export NDK_ROOT=/path/to/android-ndk-10c
    ./genbindings.py
    </pre>–>

大概意思就是下载r10c之后版的NDK本,然后安装python(这一步一般Mac都有,但是最好安装到2.x)。

大概意思就是安装,pip, PyYAML, Cheetah

…….

执行./genbindings.py之后生成在

工程目录frameworkscocos2d-xcocosscriptinglua-bindingsauto

下便有了两个生成的文件

lua_cocos2dx_custom_auto.cpp
lua_cocos2dx_custom_auto.hpp

进入到文件夹

C:UsersuserDocumentsCocosLinkframeworkscocos2d-xcocosscriptinglua-bindingsmanual

打开CCLuaStack.cpp,添加

#include "lua_cocos2dx_custom_auto.hpp"
register_all_cocos2dx_custom(_state);

期间遇到的错误

错误一
Traceback (most recent call last):
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1799, in <module>
main()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1772, in main
'clang_args': (config.get(s, 'extra_arguments', 0, dict(userconfig.items('DEFAULT'))) or "").split(" "),
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ConfigParser.py", line 623, in get
return self._interpolate(section, option, value, d)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ConfigParser.py", line 691, in _interpolate
self._interpolate_some(option, L, rawval, section, vars, 1)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ConfigParser.py", line 726, in _interpolate_some
section, map, depth + 1)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ConfigParser.py", line 723, in _interpolate_some
option, section, rest, var)
InterpolationMissingOptionError: Bad value substitution:
section: [cocos2dx_custom]
option : extra_arguments
key : clang_version
rawval : /include

路径错误

#clang_headers = -I%(clangllvmdir)s/lib/clang/%(clang_version)s/include
clang_headers = -I%(clangllvmdir)s/lib64/clang/5.0/include  # --------- ?
错误二
Traceback (most recent call last):

File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1799, in <module>
main()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1795, in main
generator.generate_code()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1444, in generate_code
self._parse_headers()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1487, in _parse_headers
raise Exception("Fatal error in parsing headers")
Exception: Fatal error in parsing headers

Generating lua bindings fails.

C++语法错误

错误三
Generating bindings for cocos2dx_custom...
Using userconfig
[('androidndkdir', '/Users/iCocos/tools/android-ndk-r16b'), ('clangllvmdir', '/Users/iCocos/tools/android-ndk-r16b/toolchains/llvm/prebuilt/darwin-x86_64'), ('gcc_toolchain_dir', '/Users/iCocos/tools/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64'), ('cocosdir', '/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x'), ('cxxgeneratordir', '/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator'), ('extra_flags', '')]

… Generating bindings for target lua

… … Processing section cocos2dx_custom

Traceback (most recent call last):
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1799, in <module>
main()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1795, in main
generator.generate_code()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1444, in generate_code
self._parse_headers()
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/generator.py", line 1478, in _parse_headers
tu = self.index.parse(header, self.clang_args)
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/clang/cindex.py", line 2602, in parse
self)
File "/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/tools/bindings-generator/clang/cindex.py", line 2714, in from_source
raise TranslationUnitLoadError("Error parsing translation unit.")
TranslationUnitLoadError: Error parsing translation unit.

Generating lua bindings fails.

路径错误

#android_headers = -I%(androidndkdir)s/platforms/android-19/arch-arm/usr/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/include
android_headers = -I%(androidndkdir)s/platforms/android-19/arch-arm/usr/lib -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/include
错误四
====

Errors in parsing headers:

  1. <severity = Fatal,
    location = <SourceLocation file '/Users/iCocos/Desktop/cocos2d/Products/cocos2dx-demo/SingleLua/frameworks/cocos2d-x/cocos/platform/android/CCPlatformDefine-android.h', line 33, column 10>,
    details = "'android/log.h' file not found">
    ====

……

提示成功

---------------------------------
Generating lua bindings succeeds.
---------------------------------

拓展推荐

  • lua: http://www.lua.org/

    • lua是个脚本语言,脚本语言!!就是脚本文件加解释器。之后你就可以看效果了。可是呢,lua如果正是靠自己独立完成点事情,那就是大材小用,需要和其他东西结合起来,比如C/C++.貌似主要也就是C/C++。
  • tolua++: http://www.codenix.com/~tolua/#news

    • tolua++:首先看名字“到、lua、++”,就是把其他语言(C/C++函数对象转化为lua能调用形式,++这里理解为增强版),有了这个工具,我们就可以快速的将我们现成的C/C++代码封装成Lua接口形式。
  • luajit: http://luajit.org/luajit.html

    • LuaJIT:LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language. 。。。。。。说了半天就一个lua的高效率版本。
  • lua for windows: http://luaforge.net/projects/luaforwindows/

    • lua for windows:lua在windows下的打包版本,除了最基本的lua解释器,还包括了可用于和C/C++集成开发的【动态链接库、静态链接库、头文件】、文本编辑器、常用的lua module,帮助说明文档。
致谢: