cocos2dx lua 绑定详解与实战

我们平时在开发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](https://developer.android.com/ndk/downloads/index.html)
* Run
<pre>
export NDK_ROOT=/path/to/android-ndk-10c
./genbindings.py
</pre>-->

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

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

…….

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

工程目录\frameworks\cocos2d-x\cocos\scripting\lua-bindings\auto

下便有了两个生成的文件

lua_cocos2dx_custom_auto.cpp
lua_cocos2dx_custom_auto.hpp

进入到文件夹

C:\Users\user\Documents\Cocos\Link\frameworks\cocos2d-x\cocos\scripting\lua-bindings\manual

打开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,帮助说明文档。
致谢:
坚持原创技术分享,您的支持将鼓励我继续创作!