- 一、相关文件
- 二、自动绑定
- #
- #
- clang 标签,其中可以添加预编译宏。
- 下面headers文件的搜索路径
- extra arguments for clang
- 要生成绑定代码的类的头文件,空格隔开。
- 绑定代码需要包含但是不需要被绑定工具扫描的头文件列表。
- 需要生成绑定代码的类,可以使用正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
- 需要在脚本层被继承的类列表,以空格分隔。
- 指出不绑定的函数
- 格式:ClassName::[func1 func2]
- 类名可以是正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
- 函数名可以是正则表达式,没有上面的^$情况
- ClassName::[*],跳过这个类的所有函数
- *::[update],跳过所有类的update函数。
- 需要被重命名的函数,会将 C++ 中的函数绑定为指定名字的脚本函数,
- 格式为 ClassName::[cppFunctionName=scriptFunctionName …],不同的类以逗号分隔。
- 需要被重命名的类,会将 C++ 中的类名绑定为指定的脚本类名,
- 格式为 CppClassName::ScriptClassName,以逗号分割。
- for all class names, should we remove something when registering in the target VM?
- classes for which there will be no “parent” lookup
- 没有父类的类列表,以空格分隔。
- base classes which will be skipped when their sub-classes found them.
- classes that create no constructor
- Set is special and we will use a hand-written constructor
- 没有构造函数的类列表,以空格分隔。
- Determining whether to use script object(js object) to control the lifecycle
- of native(cpp) object or the other way around.
- #
- 4、注册绑定
- 三、手动绑定
感谢
- https://zhuanlan.zhihu.com/p/20525109
- https://www.cnblogs.com/chevin/p/5954948.html
- https://blog.csdn.net/qq_25563175/article/details/112943639
- https://zilongshanren.com/post/embed-lua-tutorials-one/
- https://zilongshanren.com/post/pass-lua-to-cpp-and-vise-verse/
- https://zilongshanren.com/post/call-lua-table-from-cpp/
- https://zilongshanren.com/post/lua-call-cpp-functions/
- https://zilongshanren.com/post/cpp-operate-lua-string/
- https://zilongshanren.com/post/bind-a-simple-cpp-class-in-lua/
Lua绑定,指的是C++类暴露给Lua,在Lua中直接可使用。这需要借助脚本工具生成绑定代码,并注册。
一、相关文件
下面是Lua binding时需要用到的相关文件。
# 当前目录是cocos2d-x引擎目录cocos/scripting/ // 引擎的脚本部分:lua/js代码、lua/js的binding代码(C++)js-bindings/ // js的C++绑定代码lua-bindings/ // lua的C++绑定代码、预置的lua代码auto/ // 这个是根据下面tolua/*.ini的预置配置,生成的cocos引擎部分的绑定代码lua_cocos2dx_auto.hpplua_cocos2dx_auto.cppmanual/ // 这个是手写的绑定代码,不是通过ini来配置了,而是从下面的C++代码中修改了。lua_module_register.h // 手动配置,需要加载的绑定代码lua_module_register.cpp...proj.***/ // 这些绑定代码在不同平台的编译工程script/ // 这个是引擎预置的lua代码tools/ // 预生成工具bindings-generator/ // js/lua绑定的自动化工具generator.py // 执行这个脚本,将生成js、lua的C++绑定代码。README.md // 说明文件,描述该工具的Running Requirments、Usage。test/ // 这个是简单示范例子,生成的是js绑定代码tolua/ // 用于生成cocos2dx项目的lua绑定代码的工具,实质是调用上面bindings-generator***.ini // 这些是预置的配置文件,用于生成cocos引擎部分的的lua绑定代码。genbindings.py // 读取上面的配置文件,自动生成lua绑定代码。README.md // bindings-generator的Running Requirments。
通过脚本bindings-generator自动生成绑定代码的大致过程如下:
- 1、配置好bindings-generator的Running Requirements。
- 2、编写ini配置文件
- 告诉脚本,需要生成哪些类的绑定代码。可以参考预置的ini来写。
- 3、配置并执行genbindings.py
- 设置ini配置的读取路径、绑定代码的输出路径。
- 实际上就是调用bindings-generators脚本,传入的参数就是ini中设置的。
- 4、注册绑定
- 在绑定代码的hpp文件中都有一个registerall*方法,在合适的地方调用,即可正式生效。
5、平台编译
Windows
cocos2d-x 3.17.2
1、安装Android NDK
- android-ndk-r16 or later
- 2、安装python
- python2.7.3 (32bit):http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi
- 将Python根目录添加到环境变量PATH
- 3、安装pyyaml
- 4、安装Cheetah
- 5、设置环境变量NDK_ROOT、PYTHON_BIN
2、编写ini配置
```
#
cocos2dx_spine : 就是下面genbindings.py里的args[0]
#
[cocos2dx_spine]
#
prefix : 生成的绑定代码的文件名为:lua_prefix_auto.cpp
#
prefix = cocos2dx_spine
#
比如绑定了类Fuck,Bitch,Shit,在lua中就这么访问:sp.Fuck、sp.Bitch、sp.Shit
也可以为空,那就不需要前缀了。
#
1、C++中必须对应也定义在一个namespace中,如果没有,则报错conversion wasn’t set
2、需要在文件conversions.yaml的ns_map中配置好C++的namespace到Lua的namspace的映射。
文件路径:bindings-generator\targets\lua\
#
#
target_namespace = sp
androidheaders = androidflags = -target armv7-none-linux-androideabi -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -DANDROID -D__ANDROID_API=14 -gcc-toolchain %(gcc_toolchain_dir)s —sysroot=%(androidndkdir)s/platforms/android-14/arch-arm -idirafter %(androidndkdir)s/sources/android/support/include -idirafter %(androidndkdir)s/sysroot/usr/include -idirafter %(androidndkdir)s/sysroot/usr/include/arm-linux-androideabi -idirafter %(clangllvmdir)s/lib64/clang/5.0/include -I%(androidndkdir)s/sources/cxx-stl/llvm-libc++/include clang_headers =
#
clang 标签,其中可以添加预编译宏。
#
clangflags = -nostdinc -x c++ -std=c++11 -fsigned-char -U_SSE
#
下面headers文件的搜索路径
#
cocos_headers = -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s/cocos/platform/android -I%(cocosdir)s/external 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
#
要生成绑定代码的类的头文件,空格隔开。
#
headers = %(cocosdir)s/cocos/editor-support/spine/spine-cocos2dx.h
#
绑定代码需要包含但是不需要被绑定工具扫描的头文件列表。
#
cpp_headers =
#
需要生成绑定代码的类,可以使用正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
#
classes = SkeletonRenderer SkeletonAnimation
#
需要在脚本层被继承的类列表,以空格分隔。
#
classes_need_extend =
#
指出不绑定的函数
格式:ClassName::[func1 func2]
类名可以是正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
函数名可以是正则表达式,没有上面的^$情况
ClassName::[*],跳过这个类的所有函数
*::[update],跳过所有类的update函数。
#
skip = SkeletonRenderer::[findBone findSlot getAttachment setAttachment update draw createWithData], *::[update draw drawSkeleton], SkeletonAnimation::[setAnimationStateData createWithData addAnimation getCurrent setAnimation onAnimationStateEvent onTrackEntryEvent getState createWithFile]
#
需要被重命名的函数,会将 C++ 中的函数绑定为指定名字的脚本函数,
格式为 ClassName::[cppFunctionName=scriptFunctionName …],不同的类以逗号分隔。
#
rename_functions =
#
需要被重命名的类,会将 C++ 中的类名绑定为指定的脚本类名,
格式为 CppClassName::ScriptClassName,以逗号分割。
#
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 =
#
classes that create no constructor
Set is special and we will use a hand-written constructor
没有构造函数的类列表,以空格分隔。
#
abstract_classes = Skeleton SkeletonAnimation
#
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
<a name="ZrSgV"></a>## 3、配置genbindings.pygenbindings.py是对bindings-generator的再包装,用于生成cocos2d-x项目的lua绑定代码。```python#!/usr/bin/python# This script is used to generate luabinding glue codes.# Android ndk version must be ndk-r9b.......;def main():......############################################################################################################### 在以下目录寻找llvm toolchain########## llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform + "-x86_64"##### llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform##### llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform + "-x86"########## platform = windows/linux/darwin##########################################################################################################x86_llvm_path = ""x64_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s-%s' % (cur_platform, 'x86_64')))if not os.path.exists(x64_llvm_path):x86_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s' % (cur_platform)))if not os.path.exists(x86_llvm_path):x86_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s-%s' % (cur_platform, 'x86')))if os.path.isdir(x64_llvm_path):llvm_path = x64_llvm_pathelif os.path.isdir(x86_llvm_path):llvm_path = x86_llvm_pathelse:print 'llvm toolchain not found!'print 'path: %s or path: %s are not valid! ' % (x86_llvm_path, x64_llvm_path)sys.exit(1)############################################################################################################### 在以下目录寻找gcc toolchain########## gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform + "-x86_64"##### gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform##### gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform + "-x86"##########################################################################################################x86_gcc_toolchain_path = ""x64_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s-%s' % (cur_platform, 'x86_64')))if not os.path.exists(x64_gcc_toolchain_path):x86_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s' % (cur_platform)))if not os.path.exists(x86_gcc_toolchain_path):x86_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s-%s' % (cur_platform, 'x86')))if os.path.isdir(x64_gcc_toolchain_path):gcc_toolchain_path = x64_gcc_toolchain_pathelif os.path.isdir(x86_gcc_toolchain_path):gcc_toolchain_path = x86_gcc_toolchain_pathelse:print 'gcc toolchain not found!'print 'path: %s or path: %s are not valid! ' % (x64_gcc_toolchain_path, x86_gcc_toolchain_path)sys.exit(1)############################################################################################################### 设置项目目录##### project_root = cocos_root = cocos引擎目录########## 设置bindings-generator的目录##### cxx_generator_root = project_root + '/tools/bindings-generator'##########################################################################################################project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))cocos_root = os.path.abspath(os.path.join(project_root, ''))cxx_generator_root = os.path.abspath(os.path.join(project_root, 'tools/bindings-generator'))############################################################################################################### 这些目录会保存在和当前文件相同目录的userconf.ini中。对应关系如下:##### androidndkdir : ndk_root##### clangllvmdir : llvm_toolchain_path##### gcc_toolchain_dir : gcc_toolchain_path##### cocosdir : cocos_root##### cxxgeneratordir : bindings-generator##########################################################################################################config = ConfigParser.ConfigParser()config.set('DEFAULT', 'androidndkdir', ndk_root)config.set('DEFAULT', 'clangllvmdir', llvm_path)config.set('DEFAULT', 'gcc_toolchain_dir', gcc_toolchain_path)config.set('DEFAULT', 'cocosdir', cocos_root)config.set('DEFAULT', 'cxxgeneratordir', cxx_generator_root)config.set('DEFAULT', 'extra_flags', '')conf_ini_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'userconf.ini'))print 'generating userconf.ini...'with open(conf_ini_file, 'w') as configfile:config.write(configfile)############################################################################################################### 将bindings-generator目录下的\libclang、\tools\win32添加到 PATH 环境变量中。##########################################################################################################if 'linux' in platform or platform == 'darwin':os.putenv('LD_LIBRARY_PATH', '%s/libclang' % cxx_generator_root)if platform == 'win32':path_env = os.environ['PATH']os.putenv('PATH', r'%s;%s\libclang;%s\tools\win32;' % (path_env, cxx_generator_root, cxx_generator_root))try:############################################################################################################### 下面这些ini配置文件所在的目录:tolua_root = project_root + "/tools/tolua/"##### 生成的C++绑定代码的输出目录,output_dir = project_root + "/cocos/scripting/lua-bindings/auto/"##########################################################################################################tolua_root = '%s/tools/tolua' % project_rootoutput_dir = '%s/cocos/scripting/lua-bindings/auto' % project_root############################################################################################################### key : 'cocos2dx.ini' : tolua_root目录下的配置文件名##### args[0] : 'cocos2d-x' : 指定需要生成绑定代码的section,见ini配置文件中的解释##### args[1] : 'lua_cocos2dx_auto' : 输出的绑定代码的文件名########## 成功,输出日志:Generating lua bindings succeeds.##### 失败,输出日志:Generating lua bindings fails.##########################################################################################################cmd_args = {'cocos2dx.ini' : ('cocos2d-x', 'lua_cocos2dx_auto'), \'cocos2dx_extension.ini' : ('cocos2dx_extension', 'lua_cocos2dx_extension_auto'), \'cocos2dx_ui.ini' : ('cocos2dx_ui', 'lua_cocos2dx_ui_auto'), \'cocos2dx_studio.ini' : ('cocos2dx_studio', 'lua_cocos2dx_studio_auto'), \'cocos2dx_spine.ini' : ('cocos2dx_spine', 'lua_cocos2dx_spine_auto'), \'cocos2dx_physics.ini' : ('cocos2dx_physics', 'lua_cocos2dx_physics_auto'), \'cocos2dx_experimental_video.ini' : ('cocos2dx_experimental_video', 'lua_cocos2dx_experimental_video_auto'), \'cocos2dx_experimental.ini' : ('cocos2dx_experimental', 'lua_cocos2dx_experimental_auto'), \'cocos2dx_controller.ini' : ('cocos2dx_controller', 'lua_cocos2dx_controller_auto'), \'cocos2dx_cocosbuilder.ini': ('cocos2dx_cocosbuilder', 'lua_cocos2dx_cocosbuilder_auto'), \'cocos2dx_cocosdenshion.ini': ('cocos2dx_cocosdenshion', 'lua_cocos2dx_cocosdenshion_auto'), \'cocos2dx_3d.ini': ('cocos2dx_3d', 'lua_cocos2dx_3d_auto'), \'cocos2dx_audioengine.ini': ('cocos2dx_audioengine', 'lua_cocos2dx_audioengine_auto'), \'cocos2dx_csloader.ini' : ('cocos2dx_csloader', 'lua_cocos2dx_csloader_auto'), \'cocos2dx_experimental_webview.ini' : ('cocos2dx_experimental_webview', 'lua_cocos2dx_experimental_webview_auto'), \'cocos2dx_physics3d.ini' : ('cocos2dx_physics3d', 'lua_cocos2dx_physics3d_auto'), \'cocos2dx_navmesh.ini' : ('cocos2dx_navmesh', 'lua_cocos2dx_navmesh_auto'), \}target = 'lua'generator_py = '%s/generator.py' % cxx_generator_rootfor key in cmd_args.keys():args = cmd_args[key]cfg = '%s/%s' % (tolua_root, key)print 'Generating bindings for %s...' % (key[:-4])command = '%s %s %s -s %s -t %s -o %s -n %s' % (python_bin, generator_py, cfg, args[0], target, output_dir, args[1])_run_cmd(command)print '---------------------------------'print 'Generating lua bindings succeeds.'print '---------------------------------'except Exception as e:if e.__class__.__name__ == 'CmdError':print '---------------------------------'print 'Generating lua bindings fails.'print '---------------------------------'sys.exit(1)else:raise# -------------- main --------------if __name__ == '__main__':main()
4、注册绑定
// 比如生成的绑定代码文件如下:// lua_MotherFucker_auto.hpp// lua_MotherFucker_auto.cpp# include "../lua_MotherFucker_auto.hpp"register_all_MotherFucker(_state);auto engine = LuaEngine::getInstance();ScriptEngineManager::getInstance()->setScriptEngine( engine );lua_State* L = engine->getLuaStack()->getLuaState();register_all_MotherFucker( L ); // 加载绑定代码。跟引擎部分的注册放一块也可以,见CCLuaStack// LuaStack* stack = engine->getLuaStack();// stack->setXXTEAKeyAndSign( "2dxLua", strlen( "2dxLua" ), "XXTEA", strlen( "XXTEA" ) );
三、手动绑定
虽然 Bindings Generator 已经非常强大,但是它依然有其局限性。目前的 Bindings Generator 的局限性主要是以下的几点:
- 只能够针对类生成绑定,不可以绑定结构体,独立函数等
- 不能够生成 Delegate 类型的 API,因为脚本中的对象是无法继承 C++ 中的 Delegate 类并重写其中的 Delegate 函数的。
- 子类中重写了父类的 API 的同时,又重载了这个 API。
- 部分 API 实现内容并没有完全体现在其 API 定义中。
- 在运行时由 C++ 主动调用的 API。
前三种情况,可以手动编写绑定代码完成。
手写绑定教程见Lua知识部分。
Lua_C API(C与Lua的交互)
