Cocos Creator JSB [Lv.1]

Posted VermillionTear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cocos Creator JSB [Lv.1] 相关的知识,希望对你有一定的参考价值。

摘要

承接上文 Cocos Creator JSB [Lv.1] (2)
在上文中,我们已经跟小姐姐进行了基本的互动。可是光说不练怎么行,本文中我们将要给小姐姐送礼物~

系列文章

正式开始

准备礼物

我们在C++层面创建一个礼物的类。
myjsb目录下创建Gift.hGift.cpp
Gift.h

#ifndef __CC_GIFT_H__
#define __CC_GIFT_H__

#include <string>
#include "base/ccMacros.h"

namespace cocos2d { namespace myjsb {		// 命名空间,cocos2d::myjsb

    class CC_DLL Gift		// 普通的带有构造函数的类。
    {
    public: 
        Gift();		// 无参构造函数。
        Gift(const std::string& name);	// 带1个参数的构造函数。
        virtual ~Gift();	// 析构函数。

		// 对应成员变量的 set 和 get 方法。
        inline const std::string& getName() const { return _name; };
        inline void setName(std::string& name) { _name = name; };
    protected:       
        std::string _name;	// 礼物的名称,会在构造函数中赋初始值。
    };

}}  // namespace cocos2d::myjsb

#endif    // __CC_GIFT_H__

Gift.cpp

#include "myjsb/Gift.h"

namespace cocos2d { namespace myjsb {		// 命名空间,cocos2d::myjsb

	// 无参构造函数。
    Gift::Gift(): 
    _name("")
    {
        // nothing.
    }

	// 带1个参数的构造函数。
    Gift::Gift(const std::string& name): 
    _name("")
    {
        _name = name;	// 初始化礼物名称。
    }

	// 析构函数。
    Gift::~Gift()
    {
        // nothing.
    }

}}  //  namespace cocos2d::myjsb

将这两个文件加入libcocos2d中,从而让它们编译进libcocos2d库中,
在这里插入图片描述在这里插入图片描述
jsb_cocos2dx_myjsb_auto.hpp

...
extern se::Object* __jsb_cocos2d_myjsb_Gift_proto;
extern se::Class* __jsb_cocos2d_myjsb_Gift_class;

bool js_register_cocos2d_myjsb_Gift(se::Object* obj);
bool register_all_myjsb(se::Object* obj);
// 析构函数的桥梁,构造函数的桥梁不用在这里声明。
SE_DECLARE_FINALIZE_FUNC(js_cocos2d_myjsb_Gift_finalize);
// 声明作为桥梁的函数。
// 桥梁函数的作用:
// 1、将js层面传递过来的参数从se::Value类型转换为C++的类型。
// 2、调用对应的(在js_register_xxx中绑定的)C++类的成员函数,并传递转换后的的参数。
// 3、得到返回值,并将其转换为se::Value类型。
// 4、存入s.rval中,s.rval中的值就是返回给js层面的返回值。
SE_DECLARE_FUNC(js_cocos2d_myjsb_Gift_getName);
SE_DECLARE_FUNC(js_cocos2d_myjsb_Gift_setName);
...

jsb_cocos2dx_myjsb_auto.cpp

...
#include "myjsb/Gift.h"
...
se::Object* __jsb_cocos2d_myjsb_Gift_proto = nullptr;
se::Class* __jsb_cocos2d_myjsb_Gift_class = nullptr;

// 桥梁函数,new jsb.Gift 实际会调用到这里。
static bool js_cocos2d_myjsb_Gift_constructor(se::State& s) {
    const auto& args = s.args();	// 传递的参数。
    size_t argc = args.size();		// 参数的数量。
    CC_UNUSED bool ok = true;		// 参数转换是否成功的标志。

    do {
        if (argc == 0) {
        	// 调用不带参数的构造函数。
            cocos2d::myjsb::Gift* cobj = new (std::nothrow) cocos2d::myjsb::Gift();
            // 将 C++ 类的实例保存在 se::State 中,之后可以通过 s.nativeThisObject() 获取。
            s.thisObject()->setPrivateData(cobj);
            se::NonRefNativePtrCreatedByCtorMap::emplace(cobj);
            return true;
        } else if (argc == 1) {
            std::string name;
            // 传递过来的昵称是 se::Value 的形式,将其转换为 std::string 。
            ok &= seval_to_std_string(args[0], &name);
            SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_constructor : Error processing new value");

			// 调用带1个参数的构造函数。
            cocos2d::myjsb::Gift* cobj = new (std::nothrow) cocos2d::myjsb::Gift(name);
            // 将 C++ 类的实例保存在 se::State 中,之后可以通过 s.nativeThisObject() 获取。
            s.thisObject()->setPrivateData(cobj);
            se::NonRefNativePtrCreatedByCtorMap::emplace(cobj);
            return true;
        }
    } while(false);

	// 传递的参数不正确时,打印的提示信息。
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d or %d", (int)argc, 0, 1);
    return false;
}
SE_BIND_CTOR(js_cocos2d_myjsb_Gift_constructor, __jsb_cocos2d_myjsb_Gift_class, js_cocos2d_myjsb_Gift_finalize)

// 桥梁函数,js对象被回收时,实际会调用到这里。
static bool js_cocos2d_myjsb_Gift_finalize(se::State& s) {
    CCLOGINFO("jsbindings: finalizing JS object %p (cocos2d::myjsb::Gift)", s.nativeThisObject());

    auto iter = se::NonRefNativePtrCreatedByCtorMap::find(s.nativeThisObject());
    if (iter != se::NonRefNativePtrCreatedByCtorMap::end())
    {
        se::NonRefNativePtrCreatedByCtorMap::erase(iter);
        // s.nativeThisObject() 可以获取到绑定的 C++ 类的实例。
        cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
        // 删除 C++ 类的实例,从而触发类的析构函数。
        delete cobj;
    }

    return true;
}
SE_BIND_FINALIZE_FUNC(js_cocos2d_myjsb_Gift_finalize)

// 桥梁函数,let foo = gift.name 时,实际会调用到这里。
static bool js_cocos2d_myjsb_Gift_getName(se::State& s) {
	// s.nativeThisObject() 可以获取到绑定的 C++ 类的实例。
    cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Gift_getName : Invalid Native Object");

    const auto& args = s.args();	// 传递的参数。
    size_t argc = args.size();		// 参数的数量。
    CC_UNUSED bool ok = true;		// 参数转换是否成功的标志。

    if (argc == 0) {	// 需要0个参数。
	    // 不需要参数,也就没有了参数的转换部分。
        std::string result = cobj->getName();	// 调用类的成员函数,并得到返回值。
        // 将返回值转换为 se::Value 类型,并赋值到s.rval,s.rval中的值就是返回给js层面的返回值。
        ok &= std_string_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_getName : Error processing arguments");
        return true;
    }
    
    // 传递的参数不正确时,打印的提示信息。
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_PROP_GET(js_cocos2d_myjsb_Gift_getName)

// 桥梁函数,gift.name = "foo" 时,实际会调用到这里。
static bool js_cocos2d_myjsb_Gift_setName(se::State& s) {
	// s.nativeThisObject() 可以获取到绑定的 C++ 类的实例。
    cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Gift_setName : Invalid Native Object");

    const auto& args = s.args();	// 传递的参数。
    size_t argc = args.size();		// 参数的数量。
    CC_UNUSED bool ok = true;		// 参数转换是否成功的标志。

    if (argc == 1) {	// 需要1个参数。
        std::string name;
        // 传递过来的昵称是 se::Value 的形式,将其转换为 std::string 。
        ok &= seval_to_std_string(args[0], &name);
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_setName : Error processing new value");
        cobj->setName(name);	// 调用类的成员函数,并传递转换后的参数。
        // 因为此成员函数没有返回值,所以不用对 s.rval 赋值。

        return true;
    }

	// 传递的参数不正确时,打印的提示信息。
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
    return true;
}
SE_BIND_PROP_SET(js_cocos2d_myjsb_Gift_setName)

bool js_register_cocos2d_myjsb_Gift(se::Object* obj)
{
    auto cls = se::Class::create("Gift", obj, nullptr, _SE(js_cocos2d_myjsb_Gift_constructor));

	// 这里就是定义,jsb.Gift要暴露出一个name属性,
	// let foo = gift.name时,在JSB中要调用 js_cocos2d_myjsb_Gift_getName
	// gift.name= "foo" 时,在JSB中要调用 js_cocos2d_myjsb_Gift_setName
    cls->defineProperty("name", _SE(js_cocos2d_myjsb_Gift_getName), _SE(js_cocos2d_myjsb_Gift_setName));
    // 这里定义js对象被垃圾回收时,需要调用js_cocos2d_myjsb_Gift_finalize
    // js_cocos2d_myjsb_Gift_finalize中做一些收尾工作,并delete掉C++类的实例,从而触发类的析构函数。
    // 构造函数的桥梁js_cocos2d_myjsb_Gift_constructor不需要在这里定义。
    cls->defineFinalizeFunction(_SE(js_cocos2d_myjsb_Gift_finalize));

    cls->install();
    // 将 se::Class::create 的 Gift 与 C++ 实际的 Gift 类对应起来。
    JSBClassType::registerClass<cocos2d::myjsb::Gift>(cls);

    __jsb_cocos2d_myjsb_Gift_proto = cls->getProto();
    __jsb_cocos2d_myjsb_Gift_class = cls;

    se::ScriptEngine::getInstance()->clearException();
    return true;
}

bool register_all_myjsb(se::Object* obj)
{
    ...
    // 以下函数被调用之后,jsb.Gift就可以使用了。
    js_register_cocos2d_myjsb_Gift(ns);
	...
}
...

这样我们的礼物就准备好了,可以在js脚本中按照如下方式使用,

let gift = new jsb.Gift()
gift.name = "爱马仕"

let another_gift = new jsb.Gift("兰博基尼")

怎么送

送给小姐姐的礼物要精心包装,还要知道小姐姐喜不喜欢。
所以我们在送礼物的接口,传递一个礼物的object,以及一个回调函数,让小姐姐在回调函数中,告诉我们她喜不喜欢这个礼物。

送礼物

创建一个输入框,用于输入礼物的名字。
创建一个按钮,用于送礼物。
创建一个心形图片以及一个文本框,用于显示小姐姐对我们的心动值~
在这里插入图片描述在这里插入图片描述在这里插入图片描述
JSBController.js中实现功能。

...
let _heartValue = 0		// 心动值。

cc.Class({
    ...
    properties: {
        ...
        giftEditBox: cc.EditBox, 	// 输入框,用于输入礼物名称。
        heartValue: cc.Label, 		// 文本框,用于显示心动值。
    }, 

    onLoad () {
        ...
        // 初始化心动值并显示。
        _heartValue = 0
        this.heartValue.string = _heartValue
    }, 
	...
    sendGift () {
        let gift_name = this.giftEditBox.string		// 获取输入的礼物名称。
        let gift = new jsb.Gift(gift_name)		// 创建一个礼物。

		// 小姐姐收礼物,传递一个礼物object,并且传递一个回调函数。
		// msg: 小姐姐收到礼物后对我们说的话。
		// val: 小姐姐收到礼物后,心动值的变化。
        _goddess.receiveGift(gift, (msg, val) => {
        	// 把小姐姐说的话以及心动值的变化,格式化后,显示出来。
            this.txt.string = msg + '(' + val + ')'
            
            // 更新心动值。
            _heartValue += val
            this.heartValue.string = _heartValue
        })
    }, 
});

将节点挂载到脚本对应变量上。
在这里插入图片描述
指定按钮点击后的回调函数。
在这里插入图片描述
接下来,要在C++层面实现相应的函数。
Goddess.h

...
#include <functional>
...
#include "myjsb/Gift.h"

namespace cocos2d { namespace myjsb {		// 命名空间,cocos2d::myjsb

    class CC_DLL Goddess 
    {
    public: 
	    // 定义收礼物接口的回调函数。
	    // msg: 小姐姐收到礼物后对我们说的话。
		// val: 小姐姐收到礼物后,心动值的变化。
        typedef std::function<void(const std::string& msg, const int val)> ResultCallback;
		...
        void receiveGift(Gift& gift, const ResultCallback& callback);	// 收礼物。
    protected: 
        ...
    };

}}  // namespace cocos2d::myjsb

Goddess.cpp

...
#include <cstdlib>
#include <ctime>

namespace cocos2d { namespace myjsb {		// 命名空间,cocos2d::myjsb
	...
	// 简单增加了随机性,增加了一点点趣味性。
    void Goddess::receiveGift(Gift& gift, const ResultCallback& callback) {
	    // 小姐姐有可能对我们说的话。
        std::string txts[] = {"What is this?", "Just so so.", "That's great!"};
        std::string s = gift.getName();		// 获取礼物名称。
        int k = 0;		// 用于 txts 的索引。
        int v = 0;		// 心动值变化。

        if (s.empty()) {	// 如果没写礼物名字。
            s += txts[0];
            v = -10;
        } else {	// 如果写了礼物名字。
            srand((int)time(0));  // 产生随机种子  把0换成NULL也行
            // k是整数且,1 <= k <= 2
            k = (rand() % ((sizeof(txts) / sizeof(txts[0])) - 1)) + 1;
            // v是整数且,1 <= v <= 9
            v = ((rand() % 10) + 1);

            if (1 == k) {	// 如果是 "Just so so." (txts[1])
                v *= -1;	// 心动值变化就是负数。
            }

            s = s + ", " + txts[k];		// 简单组织要说的话。
        }

		// 调用回调函数。
        if (callback) {
            callback(s, v);
        }
    }
    
}}  //  namespace cocos2d::myjsb

jsb_cocos2dx_myjsb_auto.hpp

...
// 声明作为桥梁的函数。
// 桥梁函数的作用:
// 1、将js层面传递过来的参数从se::Value类型转换为C++的类型。
// 2、调用对应的(在js_register_xxx中绑定的)C++类的成员函数,并传递转换后的的参数。
// 3、得到返回值,并将其转换为se::Value类型。
// 4、存入s.rval中,s.rval中的值就是返回给js层面的返回值。
SE_DECLARE_FUNC(js_cocos2d_myjsb_Goddess_receiveGift);
...

jsb_cocos2dx_myjsb_auto.cpp

...
// 桥梁函数,jsb.Goddess.receiveGift() 时,实际会调用到这里。
static bool js_cocos2d_myjsb_Goddess_receiveGift(se::State &s) {
	// s.nativeThisObject() 可以获取到绑定的 C++ 类的实例。
    cocos2d::myjsb::Goddess *cobj = (cocos2d::myjsb::Goddess *) s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_receiveGift : Invalid Native Object");

    const auto& args = s.args();	// 传递的参数。
    size_t argc = args.size();		// 参数的数量。
    CC_UNUSED bool ok = true;		// 参数转换是否成功的标志。

    do {
        if (argc == 2) {	// 需要2个参数。
            cocos2d::myjsb::Gift gift;
            // 传递过来的礼物是 se::Value 的形式,将其转换为 Gift 类的实例 。
            // 由于 Gift 类是我们自创建的类,
            // cocos2dx 引擎中没有将 se::Value 转换为 Gift 类的实例的代码,需要我们在之后自己编写。
            ok &= seval_to_Gift(args[0], &gift);
            // C++ 无法直接调用 js 传递过来的回调函数,所以这里定义 C++ 层面的回调函数,
            // 参数的类型以及数量与 js 的回调函数保持一致。
            // Goddess::receiveGift中调用的回调函数,实际上就是这个callbac

以上是关于Cocos Creator JSB [Lv.1] 的主要内容,如果未能解决你的问题,请参考以下文章

Cocos Creator JSB [Lv.1]

Cocos Creator JSB [Lv.1]

Cocos Creator JSB [Lv.1]

Cocos Creator JSB [Lv.1]

Cocos Creator JSB [Lv.1]

Cocos Creator JSB [Lv.1]