您好,登錄后才能下訂單哦!
之前我們講過,在游戲啟動時,我們要通過SpiderMonkey引擎的注冊接口,向SpiderMonkey注冊相應的從C++到JS的綁定函數,這些函數用于把JS函數調用代碼轉換成對應C++函數調用來執行。
//在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++代碼。
首先,在創建JS對象的時候,也會創建一個對應的C++對象。換句話說,JS對象是和C++對象一一對應的(當然必須是引擎支持的,而且綁定了接口的)。然后,在JS對象執行函數時,發生了什么呢?SpiderMonkey引擎會通過注冊的接口,找到對應的C++對象,調用該對象上對應的C++函數。
換句話說,如果有下面的JS代碼:
var node = cc.Node.create(); node.setVisible(false);
那么經過SpiderMonkey執行后,會調用下面的代碼:
auto node = CCNode::create(); node->setVisible(false);
當然,SpiderMonkey遠遠還不止干了這些,還做了很多事,比如綁定和查找JS和C++對象的對應關系,包裝參數為對應類型,類型安全檢查,返回值包裝等等。要知道他干了些什么,直接看引擎代碼是更好的選擇。
在Cocos2d-x 3.0版的引擎中,引擎目錄結構進行了大規模重構。
兩個腳本語言被放到一個類似的目錄中。其中auto-generated/js-bindings文件夾是gxx-generator工具自動生成的所有C++綁定JS代碼。而javascript/bingdings文件夾是手寫的綁定代碼,因為工具無法做到完全自動綁定,所以必須有一部分手寫的(腳本語言都是這樣,習慣就好了,謝謝)。
好,我們繼續找剛才說的源代碼。打開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()執行時,底層C++跑的代碼。所有的通過JS調用C++的代碼都與這個形式非常一致,首先看函數接口:
第一個參數JSContext *cx是JS的上下文
第二個參數uint32_t argc是JS代碼中的參數個數,在這個里argc==0
第三個參數jsval *vp是JS代碼中的具體參數
繼續分析
cocos2d::Node* ret = cocos2d::Node::create();
這個代碼再熟悉不過了,標準的Cocos2d-x靜態工場生成對象的代碼
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語言的動態性進行更方便的編程。綁完之后,下面那個函數是用于獲得返回值。
最后,函數都要返回一個JSBool,表面這個函數執行是否成功。如果返回JS_FALSE,還會通過JS_ReportError打印一條報錯信息。注意!腳本語言有一個特點,如果函數運行失敗了,則該函數后面的函數(在同一作用域中的)都會跳過執行。
繼續看下一個函數
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; }
這個函數和前一個函數的區別是,這個函數有參數,并且他是一個類成員函數(上一個是類靜態函數),所以,這里要有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,以及設置返回值。
很繁瑣不是嗎?如果這種代碼全部手寫是不是會死人呢。肯定的吧。所以這些代碼都是用腳本生成器做出來的(絕大部分)。
后面我們會繼續講解各種JS的綁定代碼。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。