Cocos2dx-JS 绑定C++流程

Cococs2dx-JS 版本3.15,环境:Mac。
这里有两种情况,为修改引擎底层方法和自定义新的C++类。
(以下有参考官方文档+别人方案和自己修改添加的地方整理)


环境配置

自动绑定,说简单点,其实就只要执行一个 python 脚本即可自动生成对应的.cpp、.h、.js文件。所以首先要保证电脑有 python 运行环境,这里以 Mac 上安装为例来讲解。

1.安装 python,强烈建议先安装HomeBrew,然后直接命令行运行:

brew install python

2.通过 pip 安装 python 的一些依赖库:

sudo easy_install pip
sudo pip install PyYAML
sudo pip install Cheetah

3.安装 NDK,涉及到 c++ 肯定这个是必不可少的,我安装的是ndk-r10c版本,然后在~/.bash_profile中设置PYTHON_ROOT和NDK_ROOT这两个环境变量,因为在后面执行的 python 文件里面就会直接用到这两个环境变量:

export NDK_ROOT="/xxxxxx/ndk-r14b"
export PYTHON_BIN="/usr/bin/python"

修改引擎底层方法

这个比较简单,这里做的是给UIImageView添加了一个public方法。然后在js中用一个UIImageView对象去调用这个方法。

首先在UIImageView.cpp中添加如下方法:

void ImageView::getImageDes()
{
    CCLOG("this is getImageDes!!");
}

然后到工程/frameworks/cocos2d-x/tools/tojs目录下执行python脚本genbindings.py。(可以对这个脚本修改,然后只是重新绑定UIImageView相关的文件)。然后就可以在/frameworks/cocos2d-x/cocos/scripting/js-bindings/auto目录下的jsb_cocos2dx_ui_auto.cpp文件中看到相应的绑定内容。如下:

bool js_cocos2dx_ui_ImageView_getImageDes(JSContext *cx, uint32_t argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
    js_proxy_t *proxy = jsb_get_js_proxy(obj);
    cocos2d::ui::ImageView* cobj = (cocos2d::ui::ImageView *)(proxy ? proxy->ptr : NULL);
    JSB_PRECONDITION2( cobj, cx, false, "js_cocos2dx_ui_ImageView_getImageDes : Invalid Native Object");
    if (argc == 0) {
        cobj->getImageDes();
        args.rval().setUndefined();
        return true;
    }

    JS_ReportError(cx, "js_cocos2dx_ui_ImageView_getImageDes : wrong number of arguments: %d, was expecting %d", argc, 0);
    return false;
}

然后在js脚本中就可以调用getImageDes方法了,如下:

this._image.getImageDes();

添加自定义的C++类

TestClass.h

#ifndef TestClass_hpp
#define TestClass_hpp

#include <stdio.h>
#include "cocos2d.h"
namespace cocos2d {
  class TestClass : public cocos2d::Ref
  {
    public:
      TestClass();
      ~TestClass();
      bool init();
      std::string helloMsg();
      CREATE_FUNC(TestClass);
  };
} //namespace cocos2d
#endif /* TestClass_hpp */

TestClass.cpp

#include "TestClass.h"
USING_NS_CC;
TestClass::TestClass(){
}
TestClass::~TestClass(){
}
bool TestClass::init(){
  return true;
}
std::string TestClass::helloMsg(){
  return "this is TestClass Msg!";
}

然后把这个类放在游戏工程目录/frameworks/cocos2d-x/cocos/my/下。(自己新建的目录my)


1.png

然后把my文件夹导入到cocos2d_libs工程中,


2.png

并为cocos2d_libs工程的 User Header Searcher Paths,添加目录,指定到my目录下,$(SRCROOT)/../../../../cocos/my。
3.png

同时,我也为游戏工程添加了这个目录.
然后在/cocos2d-x/tools/tojs文件夹下添加cocos2dx_test.ini文件。这个可以复制一份,然后修改相应的位置,主要是:
headers = %(cocosdir)s/cocos/my/TestClass.h 这个是头文件的路径,然后是
classes = TestClass 这个是要包含进来的类
classes_need_extend = TestClass 需要在js里面派生的类

这里的配置文件 cocos2dx_test.ini 又是用来干嘛的呢?其实就跟 /cocos2d-x/tools/tojs/ 下的其他 .ini 文件类似,主要让自动绑定工具知道哪些 API 要被绑定和以什么样的方式绑定,写法上直接参考 Cocos 已有的 ini 文件,这里展示下 cocos2dx_test.ini 的内容:

[cocos2dx_test]
# the prefix to be added to the generated functions. You might or might not use this in your own
# templates
prefix = cocos2dx_test

# 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 = ccext

android_headers = -I%(androidndkdir)s/platforms/android-14/arch-arm/usr/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 -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/%(clang_include)s 
clang_flags = -nostdinc -x c++ -std=c++11 -U __SSE__

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 

# what headers to parse 头文件路径
headers = %(cocosdir)s/cocos/test/TestClass.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 = TestClass

# 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 = TestClass::[init]

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 = TestClass

# base classes which will be skipped when their sub-classes found them.
base_classes_to_skip = Clonable

# classes that create no constructor
# Set is special and we will use a hand-written constructor
abstract_classes = TestClass

# 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

JSB 配置脚本编写

为了保持跟官方的一致,我们在 /cocos2d-x/tools/tojs 目录下创建 genbindings_test.py,里面的内容基本跟 genbindings.py 差不多,复制修改就行,主要区别有如下几点:

  1. 去掉了 cmd_args 那段,里面主要是记录了 cocos 自带的一些需要生成 jsb 的文件,因为考虑到项目可能会对 Cocos 源码进行修改,如果这时候把这部分保留的话,当运行脚本后会把我们自带的修改就给覆盖掉了。

  2. 取消了定制的 output_dir 也就是最终生成的 js,c++ 等绑定文件的路径,而是保持跟 Cocos 一样,也即在 cocos/scripting/js-bindings/auto,主要为了方便下一步配置 mk 文件。

        custom_cmd_args = {
            'cocos2dx_test.ini': ('cocos2dx_test', 'jsb_cocos2dx_test_auto'),
        }
        if len(custom_cmd_args) > 0:
            #注释掉下面一行
            # output_dir = '%s/frameworks/custom/auto' % project_root
            for key in custom_cmd_args.keys():
                args = custom_cmd_args[key]
                cfg = '%s/%s' % (tojs_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)

这里先说下 genbindings_test.py 里面配置的一些参数:

NDK_ROOT 环境变量:指示 NDK 的根目录
PYTHON_BIN 环境变量:指示 Python 命令的路径
cocosdir:Cocos 引擎根目录,在用户工程下一般是 frameworks/cocos2d-x/
jsbdir:JSB 目录,在用户工程下一般是 frameworks/cocos2d-x/cocos/scripting/js-bindings
cxx_generator_root:自动绑定工具路径,在用户工程下一般是 frameworks/cocos2d-x/tools/bindings-generator//如果没有去引擎包里拷一份放到工程里
output_dir:生成的绑定文件存储路径
cmd_args 和 custom_cmd_args:所有配置文件,及其对应的模块名称和输出文件名称

这里自动绑定工具使用 libclang 的 python API 对 C++ 头文件进行语法分析。绑定的过程大致如下:

创建绑定代码输出文件。
递归扫描需要绑定的头文件。
通过 libclang 的 clang.cindex python 模块找到所有需要绑定的类,公共 API 等。
按照模版生成类绑定函数,API 绑定函数,绑定注册函数并输出到文件中。

关于 custom_cmd_args 参数的配置这里说明下:
'cocos2dx_test.ini': ('cocos2dx_test', 'jsb_cocos2dx_test_auto'),
配置文件:(模块名称,输出的绑定文件名)

以上的配置完成后,运行./genbindings_test.py命令自动生成绑定文件:


4.png

绑定成功之后可以在项目/frameworks/cocos2d-x/cocos/scripting/js-bindings/auto目录下看到对应的jsb_cocos2dx_test_auto.hpp和jsb_cocos2dx_test_auto.cpp,同时在这个目录下有一个api文件夹,jsb_cocos2dx_test_api.js也相应的生成了。
然后把这两个文件导入到cocos2d_js_bindings工程的auto文件夹下。

5.png

然后在AppDelegate.cpp文件中注册,如下:

sc->addRegisterCallback(register_all_cocos2dx_test);

然后我们就可以在js代码中使用我们的C++类了

var testClass = ccext.TestClass.create();
var msg = testClass.helloMsg()
cc.log("testClass's msg is========>>>" + msg)

然后就是 Android 下面的处理。主要是把我们新添加的类,包含到Android.mk中。
首先到项目/frameworks/cocos2d-x/cocos/scripting/js-bindings/proj.android/目录下,找到Android.mk ,然后在 LOCAL_C_INCLUDES下,包含我们的新添加的类的路径。
在 LOCAL_SRC_FILES下包含,我们的cpp文件,如下: ../auto/jsb_cocos2dx_test_auto.cpp \ 。同时,要保证我们的原始的C++类也包含到mk文件中,保证变异的时候能找到我们的原始C++文件。然后这样Android下面编译也没什么问题了。

参考文章:https://www.jianshu.com/p/347feb55cee9
https://docs.cocos.com/creator/manual/zh/advanced-topics/jsb-auto-binding.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容