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
-
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:
-
<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,帮助说明文档。
致谢:
- https://blog.csdn.net/never_QH/article/details/45148835
- http://geekgaoyang.herokuapp.com/blog/2015/02/11/cocos2d-x-tutorial-of-binding-c-plus-plus-class-to-lua-usage/
- https://www.cnblogs.com/ZhYQ-Note/p/5939783.html
- http://www.bubuko.com/infodetail-1210197.html
- https://www.jianshu.com/p/9bd4d5518d53