Cocos2d-html5 v3.0 RC0之JS与C++间通讯
(PS:本人第一次写博客,如果有错,欢迎指正!不喜莫喷~)
随着 Cocos2d-x 的发展,Cocos2d-html5 也日益完善,JS接第三方SDK的需求也随之增加!为了方便同时管理Android和IOS,那么我们就要实现JS与C++间通讯,然后再加上已有的C++与Android和IOS间的通讯就可以完美实现JS接第三方SDK了!
下面就为大家介绍一下Cocos2d-html5中JS与C++间通讯!
1.定义一个待绑定(被JS调用)的类
JSBinding.h
#include <iostream>
#include "cocos2d.h"
#include "ScriptingCore.h"
using namespace cocos2d;
namespace JSB {
class JSBinding: public Ref
{
public:
static Scene* scene();
virtual bool init();
CREATE_FUNC(JSBinding);
void functionTest();//被JS调用的代码
};
}
JSBinding.cpp
#include "JSBinding.h"
bool JSB::JSBinding::init(){
bool bRef = false;
do{
cocos2d::log("JSB init...");
bRef = true;
} while (0);
return bRef;
}
void JSB::JSBinding::functionTest(){
log("JSToC++ & C++ToJS");
//一下代码为C++调用JS的callback方法
js_proxy_t* p = jsb_get_native_proxy(this);
jsval retval;
// 定义参数,由两个参数
jsval v[] = {
v[0] = UINT_TO_JSVAL(32),
v[1] = UINT_TO_JSVAL(88)
};
// 通过 ScriptingCore 封装好的方法实现回调,可以帮助我们节省很多细节上的研究
ScriptingCore::getInstance()->executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj),
"callback", 2, v, &retval);
}
2.自定义JSB手动绑定函数入口JSBinding类
JSB_AUTO.h
#include <iostream>
#include "jsapi.h"
#include "jsfriendapi.h"
#include "ScriptingCore.h"
#include "JSBinding.h"
using namespace cocos2d;
void register_all(JSContext* cx, JSObject* obj);
JSB_AUTO.cpp
#include "JSB_AUTO.h"
#include "cocos2d.h"
#include "cocos2d_specifics.hpp"
// 定义 js 端的类型
JSClass* jsb_class;
JSObject* jsb_prototype;
// js 端 functionTest 所绑定的方法调用
bool js_functionTest(JSContext* cx, uint32_t argc, jsval* vp){
bool ok = true;
JSObject* obj = NULL;// 定义以获取真实类型
JSB::JSBinding* cobj = NULL;
obj = JS_THIS_OBJECT(cx, vp);
js_proxy_t* proxy = jsb_get_js_proxy(obj);
// 获取 js 绑定的实际对象 通过 proxy->ptr
cobj = (JSB::JSBinding* )(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2(cobj, cx, false, "Invalid Native Object");
if (argc == 0) {
// 调用实际的方法
cobj->functionTest();
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return ok;
}
JS_ReportError(cx, "Wrong number of arguments");
return false;
}
// js 构造函数实现
bool js_constructor(JSContext* cx, uint32_t argc, jsval* vp){
cocos2d::log("JS Constructor...");
if (argc == 0) {
// 调用 C++ 构造函数
JSB::JSBinding* cobj = new JSB::JSBinding();
cocos2d::Ref* ccobj = dynamic_cast<Ref*>(cobj);
// 默认使用原有的内存管理方式
if (ccobj) {
ccobj->autorelease();
}
TypeTest<JSB::JSBinding> t;
js_type_class_t* typeClass = nullptr;
std::string typeName = t.s_name();
auto typeMapIter = _js_global_type_map.find(typeName);
CCASSERT(typeMapIter != _js_global_type_map.end(), "Can't find the class type!");
typeClass = typeMapIter->second;
CCASSERT(typeClass, "The value is null.");
JSObject* obj = JS_NewObject(cx, typeClass->jsclass, typeClass->proto, typeClass->parentProto);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
// 构造 js 端对象,将 cobj 实际对象存入
js_proxy_t* p = jsb_new_proxy(cobj, obj);
JS_AddNamedObjectRoot(cx, &p->obj, "JSB::JSBinding");
return true;
}
JS_ReportError(cx, "Wrong number of arguments: %d, was expecting: %d", argc, 0);
return false;
}
// 静态函数 create 的具体实现
bool js_create(JSContext* cx, uint32_t argc, jsval* vp){
cocos2d::log("js is creating...");
if (argc == 0) {
// 创建 JSBinding 对象
JSB::JSBinding* ret = JSB::JSBinding::create();
jsval jsret;
do{
if (ret) {
js_proxy_t* proxy = js_get_or_create_proxy<JSB::JSBinding>(cx, ret);
jsret = OBJECT_TO_JSVAL(proxy->obj);
}
else{
jsret = JSVAL_NULL;
}
} while(0);
JS_SET_RVAL(cx, vp, jsret);
return false;
}
JS_ReportError(cx, "Wrong number of arguments");
return false;
}
void js_finalize(JSFreeOp* fop, JSObject* obj){
// 析构函数实现,如果在构造函数做了什么,如开辟内存空间,那么需要在这里做些收尾工作
CCLOGINFO("JSBindings: finallizing JS object %p JSB", obj);
}
void js_register(JSContext* cx, JSObject* global){
jsb_class = (JSClass *)calloc(1, sizeof(JSClass));
// 类型名称为 **JSBinding** 正式绑定到 js 由 js 调用的名称
jsb_class->name = "JSBinding";
jsb_class->addProperty = JS_PropertyStub;
jsb_class->delProperty = JS_DeletePropertyStub;
jsb_class->getProperty = JS_PropertyStub;
jsb_class->setProperty = JS_StrictPropertyStub;
jsb_class->enumerate = JS_EnumerateStub;
jsb_class->resolve = JS_ResolveStub;
jsb_class->convert = JS_ConvertStub;
// JSBinding 类型的析构函数绑定
jsb_class->finalize = js_finalize;
jsb_class->flags = JSCLASS_HAS_RESERVED_SLOTS(2);
static JSPropertySpec properties[] = {
{0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER}
};
// 为 JSBinding 设定绑定函数,函数名 "functionTest",绑定函数 "js_functionTest"
// 后面可以添加其它函数绑定,如果需要,之后以 "JS_FS_END" 结尾
static JSFunctionSpec funcs[] = {
JS_FN("functionTest", js_functionTest, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE),
JS_FS_END
};
//这里定义并且绑定了静态函数(static),包括方法名 "create" 和对应的绑定实现 "js_create"
static JSFunctionSpec st_funcs[] = {
JS_FN("create", js_create, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
JS_FS_END
};
//初始化类型属性
jsb_prototype = JS_InitClass(
cx, global,
NULL,
jsb_class,
js_constructor, 0,// 这里绑定的是构造函数的实现,也就是用 js new 操作符创建的对象
properties,
funcs,// 函数绑定
NULL,// no static properties
st_funcs);// 静态函数绑定
TypeTest<JSB::JSBinding> t;
js_type_class_t* p;
std::string typeName = t.s_name();
if (_js_global_type_map.find(typeName) == _js_global_type_map.end())
{
p = (js_type_class_t *)malloc(sizeof(js_type_class_t));
p->jsclass = jsb_class;
p->proto = jsb_prototype;
p->parentProto = NULL;
_js_global_type_map.insert(std::make_pair(typeName, p));
}
}
//示例方法,不必要
bool JSB_cocos2dx_retain(JSContext* cx, uint32_t argc, jsval *vp){
JSObject* thisObj = JS_THIS_OBJECT(cx, vp);
if (thisObj) {
js_proxy_t* proxy = jsb_get_js_proxy(thisObj);
if (proxy) {
((Ref* )proxy->ptr)->retain();
log("Retain succeed!");
return true;
}
}
JS_ReportError(cx, "Invaild native object");
return false;
}
//示例方法,不必要
bool JSB_cocos2dx_release(JSContext* cx, uint32_t argc, jsval *vp){
JSObject* thisObj = JS_THIS_OBJECT(cx, vp);
if (thisObj) {
js_proxy_t* proxy = jsb_get_js_proxy(thisObj);
if (proxy) {
((Ref* )proxy->ptr)->release();
log("Release succeed!");
return true;
}
}
JS_ReportError(cx, "Invaild native object");
return false;
}
// 实现命名空间下的类绑定
void register_all(JSContext* cx, JSObject* obj){
JS::RootedValue nsval(cx);
JS::RootedObject ns(cx);
JS_GetProperty(cx, obj, "JS", &nsval);
if (nsval == JSVAL_VOID) {
ns = JS_NewObject(cx, NULL, NULL, NULL);
nsval = OBJECT_TO_JSVAL(ns);
JS_SetProperty(cx, obj, "JSB", nsval);
}
else{
JS_ValueToObject(cx, nsval, &ns);
}
obj = ns;
// 实现绑定JSBinding类
js_register(cx, obj);
//示例方法绑定,不必要
JS_DefineFunction(cx, jsb_prototype, "retain", JSB_cocos2dx_retain, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(cx, jsb_prototype, "release", JSB_cocos2dx_release, 0, JSPROP_READONLY | JSPROP_PERMANENT);
}
functionTest()方法中又包含了C++调用JS的方法!
3.在AppDelegate中注册register_all方法
<p class="p1"><span class="s1">#include </span>"JSB_AUTO.h"//记得加上头文件</p>
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
glview = GLView::createWithRect("helloWorld", Rect(0,0,900,640));
director->setOpenGLView(glview);
}
// turn on display FPS
director->setDisplayStats(true);
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
ScriptingCore* sc = ScriptingCore::getInstance();
sc->addRegisterCallback(register_all_cocos2dx);
sc->addRegisterCallback(register_all_cocos2dx_extension);
sc->addRegisterCallback(register_cocos2dx_js_extensions);
sc->addRegisterCallback(register_all_cocos2dx_extension_manual);
sc->addRegisterCallback(jsb_register_chipmunk);
sc->addRegisterCallback(jsb_register_system);
sc->addRegisterCallback(JSB_register_opengl);
sc->addRegisterCallback(register_all_cocos2dx_builder);
sc->addRegisterCallback(register_CCBuilderReader);
<span style="white-space:pre"> </span>sc->addRegisterCallback(register_all_cocos2dx_ui);
sc->addRegisterCallback(register_all_cocos2dx_ui_manual);
sc->addRegisterCallback(register_all_cocos2dx_studio);
sc->addRegisterCallback(register_all_cocos2dx_studio_manual);
sc->addRegisterCallback(register_all_cocos2dx_spine);
sc->addRegisterCallback(register_all_cocos2dx_spine_manual);
sc->addRegisterCallback(MinXmlHttpRequest::_js_register);
sc->addRegisterCallback(register_jsb_websocket);
sc->addRegisterCallback(register_jsb_socketio);
<span style="white-space:pre"> </span>sc->addRegisterCallback(register_all);//与JS绑定的代码
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
sc->addRegisterCallback(JavascriptJavaBridge::_js_register);
#endif
sc->start();
ScriptEngineProtocol *engine = ScriptingCore::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);
ScriptingCore::getInstance()->runScript("main.js");
return true;
}
通过以上的步骤,我们实现了 C++ 类 JSBinding 到 JS 端的绑定。在 JS 中我们可以通过以下调试测试:
//JS调用C++
var testJSB = new JSB.JSBinding(); // 创建一个对象
testJSB.callback = function(i, j){
log("JSB Callback" + i + j);
};
testJSB.retain();
testJSB.functionTest();
testJSB.release();
4.大功告成
按下运行键,是不是有点小激动呢?终于实现了JS与C++的交互了!
接下来就是C++与Android和IOS的工作了!
最后附上DEMO下载地址:点击打开链接