Cocos Creator JSB [Lv.1]
Posted VermillionTear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cocos Creator JSB [Lv.1] 相关的知识,希望对你有一定的参考价值。
摘要
承接上文 Cocos Creator JSB [Lv.1] (2)
在上文中,我们已经跟小姐姐进行了基本的互动。可是光说不练怎么行,本文中我们将要给小姐姐送礼物~
系列文章
正式开始
准备礼物
我们在C++
层面创建一个礼物的类。
在myjsb
目录下创建Gift.h
和Gift.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] 的主要内容,如果未能解决你的问题,请参考以下文章