Cocos2d-html5 v3.0 RC0之JS与C++间通讯

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下载地址:点击打开链接


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值