在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ JS 與 C++ 的交互 1——JS 代碼調用 C++ 代碼
cocos2d-x for js 中的繼承
JS 與 C++ 的交互 1——JS 代碼調用 C++ 代碼
迎接腳本時代的到來
解決在 vs 中修改 js 源文件無效
JS 腳本語言的優(yōu)勢與一些問題
注冊函數
回調函數 2
cxx-generator JS 綁定工具
使用 cocos2d-console 工具轉換腳本為字節(jié)碼
hybrid 開發(fā)模式
JS 與 C++ 的交互 2——JS 與 C++ 的“函數重載”問題
回調函數1——按鍵回調
Google 的繼承寫法解析
John Resiq 的繼承寫法解析
JS 與 C++ 的交互 3——C++ 和 JS 類型轉換
傀儡構造函數

JS 與 C++ 的交互 1——JS 代碼調用 C++ 代碼

之前我們講過,在游戲啟動時,我們要通過 SpiderMonkey 引擎的注冊接口,向 SpiderMonkey 注冊相應的從 C++ 到 JS 的綁定函數,這些函數用于把 JS 函數調用代碼轉換成對應 C++ 函數調用來執(zhí)行。

//在AppDelegate::applicationDidFinishLaunching函數中
    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_opengl);
    sc->addRegisterCallback(jsb_register_system);
    sc->addRegisterCallback(MinXmlHttpRequest::_js_register);
    sc->addRegisterCallback(register_jsb_websocket);
    sc->addRegisterCallback(register_all_cocos2dx_builder);
    sc->addRegisterCallback(register_CCBuilderReader);
    sc->addRegisterCallback(register_all_cocos2dx_gui);
    sc->addRegisterCallback(register_all_cocos2dx_gui_manual);
    sc->addRegisterCallback(register_all_cocos2dx_studio);
    sc->addRegisterCallback(register_all_cocos2dx_studio_manual);

    sc->addRegisterCallback(register_all_cocos2dx_spine);

可以看到上面導入了 Cocos2d-x 的各種庫,核心庫,擴展,opengl,物理引擎,websocket,CCB 等等等等。

下面我們說 JS 代碼如何調用 C++ 代碼。 首先,在創(chuàng)建 JS 對象的時候,也會創(chuàng)建一個對應的 C++ 對象。換句話說,JS 對象是和 C++ 對象一一對應的(當然必須是引擎支持的,而且綁定了接口的)。然后,在 JS 對象執(zhí)行函數時,發(fā)生了什么呢? SpiderMonkey 引擎會通過注冊的接口,找到對應的 C++ 對象,調用該對象上對應的 C++ 函數。

換句話說,如果有下面的 JS 代碼:

var node = cc.Node.create();
node.setVisible(false);

那么經過 SpiderMonkey 執(zhí)行后,會調用下面的代碼:

auto node = CCNode::create();
node->setVisible(false);

當然,SpiderMonkey 遠遠還不止干了這些,還做了很多事,比如綁定和查找 JS 和 C++ 對象的對應關系,包裝參數為對應類型,類型安全檢查,返回值包裝等等。要知道他干了些什么,直接看引擎代碼是更好的選擇。

在 Cocos2d-x 3.0 版的引擎中,引擎目錄結構進行了大規(guī)模重構。

http://wiki.jikexueyuan.com/project/cocos2d-x-from-cplusplus-js/images/9.jpg" alt="" />

兩個腳本語言被放到一個類似的目錄中。其中 auto-generated/js-bindings 文件夾是 gxx-generator 工具自動生成的所有 C++ 綁定 JS 代碼。而 javascript/bingdings 文件夾是手寫的綁定代碼,因為工具無法做到完全自動綁定,所以必須有一部分手寫的(腳本語言都是這樣,習慣就好了,謝謝)。

好,我們繼續(xù)找剛才說的源代碼。打開 jsb_cocos2dx_auto.cpp

JSBool js_cocos2dx_Node_create(JSContext *cx, uint32_t argc, jsval *vp)
{
    if (argc == 0) {
        cocos2d::Node* ret = cocos2d::Node::create();
        jsval jsret = JSVAL_NULL;
        do {
        if (ret) {
            js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, (cocos2d::Node*)ret);
            jsret = OBJECT_TO_JSVAL(proxy->obj);
        } else {
            jsret = JSVAL_NULL;
        }
    } while (0);
        JS_SET_RVAL(cx, vp, jsret);
        return JS_TRUE;
    }
    JS_ReportError(cx, "js_cocos2dx_Node_create : wrong number of arguments");
    return JS_FALSE;
}

這就是 cc.Node.create() 執(zhí)行時,底層 C++ 跑的代碼。所有的通過 JS 調用 C++ 的代碼都與這個形式非常一致,首先看函數接口:
第一個參數 JSContext cx 是 JS 的上下文
第二個參數 uint32_t argc 是 JS 代碼中的參數個數,在這個里argc==0
第三個參數 jsval
v p是 JS 代碼中的具體參數

繼續(xù)分析

cocos2d::Node* ret = cocos2d::Node::create();

這個代碼再熟悉不過了,標準的 Cocos2d-x 靜態(tài)工場生成對象的代碼

jsval jsret = JSVAL_NULL;

jsval jsret 是這個函數的返回值,這是表示的是一個 JS 對象

js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, (cocos2d::Node*)ret);
jsret = OBJECT_TO_JSVAL(proxy->obj);

注意這個模板函數,get_or_create,這就是把 JS 對象和 C++ 對象綁到一起的函數。他非常重要,注意 JS 和 C++ 對象是一一對應關系,理解這個特效,有助于我們利用 JS 語言的動態(tài)性進行更方便的編程。綁完之后,下面那個函數是用于獲得返回值。

最后,函數都要返回一個 JSBool,表面這個函數執(zhí)行是否成功。如果返回 JS_FALSE,還會通過 JS_ReportError 打印一條報錯信息。注意!腳本語言有一個特點,如果函數運行失敗了,則該函數后面的函數(在同一作用域中的)都會跳過執(zhí)行。

繼續(xù)看下一個函數

JSBool js_cocos2dx_Node_setVisible(JSContext *cx, uint32_t argc, jsval *vp)
{
    jsval *argv = JS_ARGV(cx, vp);
    JSBool ok = JS_TRUE;
    JSObject *obj = JS_THIS_OBJECT(cx, vp);
    js_proxy_t *proxy = jsb_get_js_proxy(obj);
    cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
    JSB_PRECONDITION2( cobj, cx, JS_FALSE, "js_cocos2dx_Node_setVisible : Invalid Native Object");
    if (argc == 1) {
        JSBool arg0;
        ok &= JS_ValueToBoolean(cx, argv[0], &arg0);
        JSB_PRECONDITION2(ok, cx, JS_FALSE, "js_cocos2dx_Node_setVisible : Error processing arguments");
        cobj->setVisible(arg0);
        JS_SET_RVAL(cx, vp, JSVAL_VOID);
        return JS_TRUE;
    }
    JS_ReportError(cx, "js_cocos2dx_Node_setVisible : wrong number of arguments: %d, was expecting %d", argc, 1);
    return JS_FALSE;
}

這個函數和前一個函數的區(qū)別是,這個函數有參數,并且他是一個類成員函數(上一個是類靜態(tài)函數),所以,這里要有 this 指針。

jsval *argv = JS_ARGV(cx, vp);
JSBool ok = JS_TRUE;
JSObject *obj = JS_THIS_OBJECT(cx, vp);
js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, JS_FALSE, "js_cocos2dx_Node_setVisible : Invalid Native Object");

這一大段函數都在找那個 this 指針。注意,這里面有一個 Cocos2d-x 引擎經常出現的錯誤提示 Invalid Native Object。底層 C++ 對象被回收了,所以找不到了。

if (argc == 1) {
    JSBool arg0;
    ok &= JS_ValueToBoolean(cx, argv[0], &arg0);
    JSB_PRECONDITION2(ok, cx, JS_FALSE, "js_cocos2dx_Node_setVisible : Error processing arguments");
    cobj->setVisible(arg0);
    JS_SET_RVAL(cx, vp, JSVAL_VOID);
    return JS_TRUE;
}

CCNode::setVisible(xx) 只有一個參數,所以先判斷 JS 的參數個數為1。JS_ValueToBoolean 完成 JS 對象到 C++ 對象的轉換,注意!這是基本類型的轉換,和查找對應的對象指針不同。你在 gxx-generator 生成的代碼中會看到大量的這種轉換。每次轉換都要進行結果判斷,如果失敗,就打印錯誤信息。后面是直接調用對應 C++ 對象的 setVisible,以及設置返回值。

很繁瑣不是嗎?如果這種代碼全部手寫是不是會死人呢。肯定的吧。所以這些代碼都是用腳本生成器做出來的(絕大部分)。

后面我們會繼續(xù)講解各種 JS 的綁定代碼。