Cocos Creator JSB [Lv.3]
Posted VermillionTear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cocos Creator JSB [Lv.3]相关的知识,希望对你有一定的参考价值。
目录
摘要
本文内容大部分源于官方文档,对其中一些比较重要的内容,以及有自己想法的内容做了适度的展开,意在帮助读者更加全面的了解JSB
。
资源
正式开始
为什么要使用JSB
为了让脚本层与C++
层面进行交互,从而让脚本层可以控制一些高效的或是定制性较强的操作。
- 虽然游戏的大部分逻辑都放到了脚本层,但是对于一些性能要求较高的操作(例如文件的读写,文件的加解密等)还是需要在
C++
层面完成。 - 对于某些功能,各原生平台会有各自的实现方式,需要
C++
层面定义统一的接口,各原生平台自行实现功能。 - 实际上
cocos2dx
引擎主要都是使用C++
进行编写,js
使用引擎的功能都是通过JSB
来调用C++
提供的接口。
基于以上几点,脚本层面想要使用这些功能,就需要通过JSB
调用C++
提供的接口。
以cc.Sprite
为例,在JSB
中如果使用new
操作符来调用cc.Sprite
的构造函数,实际上在C++
层会调用js_cocos2dx_Sprite_constructor
函数。在这个C++
函数中,会为这个精灵对象分配内存,并把它添加到自动回收池,然后调用JS
层的_ctor
函数来完成初始化。在_ctor
函数中会根据参数类型和数量调用不同的init
函数,这些init
函数也是C++
函数的绑定。
三层的方法对应关系如下:
javascript | JSB | Cocos2d-x |
---|---|---|
cc.Sprite.initWithSpriteFrameName | js_cocos2dx_Sprite_initWithSpriteFrameName | cocos2d::Sprite::initWithSpriteFrameName |
cc.Sprite.initWithSpriteFrame | js_cocos2dx_Sprite_initWithSpriteFrame | cocos2d::Sprite::initWithSpriteFrame |
cc.Sprite.initWithFile | js_cocos2dx_Sprite_initWithFile | cocos2d::Sprite::initWithFile |
cc.Sprite.initWithTexture | js_cocos2dx_Sprite_initWithTexture | cocos2d::Sprite::initWithTexture |
JSB
能做什么
他能让js脚本与C++互相传递参数,互相调用函数。他搭建了js脚本与C++之间通讯的桥梁。
进行JSB
绑定需要做什么
JSB
绑定简单来讲就是在C++
层实现一些类库,然后经过一些特定处理可以在JS
端进行对应方法调用的过程。
构建工程
- 指定
发布路径
。 模板
选择default
。- 构建工程。
模板一定要选default
,因为这样才会将cocos2dx
的源码拷贝到发布路径下。
我们需要修改cocos2dx
的部分源码,从而实现JSB
绑定。
cocos2dx
源码目录为发布路径/jsb-default/frameworks/cocos2d-x/
。之后为了叙述方便,提及cocos2dx
源码中的文件,如不做特殊说明,均以此目录作为根目录。
创建类
在cocos/模块
目录下编写类.h
,类.cpp
。
绑定(搭建桥梁)
在cocos/scripting/js-bindings/auto/模块
目录下编写jsb_模块_auto.hpp
,jsb_模块_auto.cpp
。
jsb_模块_auto.hpp
#pragma once
#include "base/ccConfig.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_android || CC_TARGET_PLATFORM == CC_PLATFORM_ios || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
extern se::Object* __jsb_cocos2d_模块_proto;
extern se::Class* __jsb_cocos2d_模块_class;
bool js_register_模块_类(se::Object* obj); // 注册类。
bool register_all_模块_类(se::Object* obj); // 注册模块下所有的类。
SE_DECLARE_FINALIZE_FUNC(js_cocos2d_模块_类_finalize); // 声明一个 JS 对象被 GC 回收后的回调函数。
SE_DECLARE_FUNC(js_cocos2d_模块_类_成员函数); // 声明一个 JS 函数,一般在 .h 头文件中使用。
...
#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
jsb_模块_auto.cpp
#include "scripting/js-bindings/auto/jsb_模块_auto.hpp"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include "scripting/js-bindings/manual/jsb_conversions.hpp"
#include "scripting/js-bindings/manual/jsb_global.h"
#include "模块/类.h"
se::Object* __jsb_cocos2d_模块_proto = nullptr;
se::Class* __jsb_cocos2d_模块_class = nullptr;
static bool js_cocos2d_模块_类_成员函数(se::State &s) {
...
}
SE_BIND_FUNC(js_cocos2d_模块_类_成员函数); // 包装一个 JS 函数,可用于全局函数、类成员函数、类静态函数。
static bool js_cocos2d_模块_类_get_成员变量(se::State& s) {
...
}
SE_BIND_PROP_GET(js_cocos2d_模块_类_get_成员变量); // 包装一个 JS 对象属性读取的回调函数
static bool js_cocos2d_模块_类_set_成员变量(se::State& s) {
...
}
SE_BIND_PROP_SET(js_cocos2d_模块_类_set_成员变量); // 包装一个 JS 对象属性写入的回调函数
static bool js_cocos2d_模块_类_constructor(se::State& s) {
...
}
// 包装一个 JS 构造函数
SE_BIND_CTOR(js_cocos2d_模块_类_constructor, __jsb_cocos2d_模块_类_class, js_cocos2d_模块_类_finalize);
static bool js_cocos2d_模块_类_finalize(se::State& s) {
...
}
SE_BIND_FINALIZE_FUNC(js_cocos2d_模块_类_finalize); // 包装一个 JS 对象被 GC 回收后的回调函数
bool js_register_模块_类(se::Object* obj) {
auto cls = se::Class::create("类", obj, nullptr, nullptr);
// 绑定 jsb.对象.函数 到 C++类的静态成员函数。
// 一般用于 let foo = jsb.对象.getInstance()
cls->defineStaticFunction("静态成员函数", _SE(js_cocos2d_模块_类_静态成员函数));
// 绑定 jsb.对象.属性 到 C++类的SET和GET成员函数。
// 一般用于 let foo = jsb.对象.属性 以及 jsb.对象.属性 = foo
cls->defineProperty("成员变量", _SE(js_cocos2d_模块_类_get_成员变量), _SE(s_cocos2d_模块_类_set_成员变量));
// 绑定 jsb.对象.函数 到 C++类的成员函数。
// 一般用于 jsb.对象.fun()
cls->defineFunction("成员函数", _SE(js_cocos2d_模块_类_成员函数));
...
cls->install();
// 将 se::Class::create 的 Goddess 与 C++ 实际的 Goddess 类对应起来。
JSBClassType::registerClass<cocos2d::模块::类>(cls);
__jsb_cocos2d_模块_proto = cls->getProto();
__jsb_cocos2d_模块_class = cls;
se::ScriptEngine::getInstance()->clearException();
return true;
}
bool register_all_模块(se::Object* obj)
{
// Get the ns
se::Value nsVal;
if (!obj->getProperty("jsb", &nsVal))
{
se::HandleObject jsobj(se::Object::createPlainObject());
nsVal.setObject(jsobj);
obj->setProperty("jsb", nsVal);
}
se::Object* ns = nsVal.toObject();
js_register_模块_类(ns); // 调用上边的函数,注册类。
...
return true;
}
桥梁做了下面这些工作:
接收js 传递 | C++ 类型参数 | |
----------------------------------> | 转换 | ----------------------------------> |
se::Value 类型参数 | seval_to_C++类型 | 调用C++ 类的成员函数 |
se::Value 类型返回值 | 接收C++ 类的成员函数传递 | |
<---------------------------------- | 转换 | <---------------------------------- |
通过s.rval 传递 | C++类型_to_seval | C++ 类型返回值 |
实现自定义的类型转换(非必须)
如果在桥梁函数中,需要将自定义类型转换为se::Value
,或是将se::Value
转换为自定义类型,就需要在
cocos/scripting/js-bindings/manual/jsb_conversions.hpp
和cocos/scripting/js-bindings/manual/jsb_conversions.cpp
中编写转换的代码。
jsb_conversions.hpp
#include "模块/类.h"
bool seval_to_类(const se::Value& v, cocos2d::模块::类* pt);
jsb_conversions.cpp
bool seval_to_类(const se::Value& v, cocos2d::模块::类* pt)
{
...
return true;
}
注册
如果是新建的模块,就需要在发布路径/jsb-default/frameworks/runtime-src/Classes/jsb_module_register.cpp
中注册模块。
#include "cocos/scripting/js-bindings/auto/jsb_模块_auto.hpp"
se->addRegisterCallback(register_all_模块);
将新增的文件加入libcocos2d
类.h
,类.cpp
,jsb_模块_auto.hpp
,jsb_模块_auto.cpp
都有可能是自己新建的文件,需要将他们编译进libcocos2d
库。
windows
添加 -> 现有项
Android
需要修改发布路径/jsb-default/frameworks/cocos2d-x/cocos/Android.mk
LOCAL_SRC_FILES := \\
...
模块/类.cpp \\
scripting/js-bindings/auto/jsb_cocos2dx_模块_auto.cpp \\
...
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) \\
...
$(LOCAL_PATH)/模块 \\
...
划重点
- 为什么要使用
JSB
。 - 进行
JSB
绑定需要做什么。 - 参考已有的绑定写法。
以上是关于Cocos Creator JSB [Lv.3]的主要内容,如果未能解决你的问题,请参考以下文章