溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過(guò)JS_CallFunctionValue函數(shù)完成JS層回調(diào)函數(shù)的調(diào)用。

發(fā)布時(shí)間:2021-12-14 11:29:25 來(lái)源:億速云 閱讀:196 作者:柒染 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)cocos2d-x中回調(diào)函數(shù)的按鍵回調(diào)是怎樣的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

回調(diào)函數(shù)是界面交互和接入各種第三方SDK的關(guān)鍵所在,因?yàn)榛卣{(diào)函數(shù)的C++代碼是不能自動(dòng)生成的,一切的一切,都需要手寫(xiě)完成。

比較不錯(cuò)的是,Cocos2d-x引擎對(duì)于回調(diào)函數(shù)提供了完整的包裝機(jī)制。我們所需要做的就是了解這個(gè)機(jī)制,并使用他。學(xué)習(xí)引擎自己的代碼例子,可以比較快速準(zhǔn)確的上手這一機(jī)制。

首先,我們?cè)贑ocos2d-x 3.0 beta版中,使用他自帶的工程創(chuàng)建工具,新建一個(gè)跨平臺(tái)的JS項(xiàng)目。按照慣例,這是一個(gè)helloworld項(xiàng)目。在XCode運(yùn)行時(shí),我們可以看到右下角的回調(diào)按鈕。我們來(lái)看看他是怎么實(shí)現(xiàn)的。分成兩個(gè)過(guò)程來(lái)做:

一、綁定回調(diào)函數(shù)過(guò)程

首先,我們要去找回調(diào)函數(shù)JS的綁定代碼,在myApp.js中,init函數(shù)里面,可以看到如下代碼:

1 2 3 4 5 6 7 8 9 10 11 12//
add a "close" icon to exit the progress. it's an autorelease object
var closeItem
= cc.MenuItemImage.create(
    "res/CloseNormal.png",    "res/CloseSelected.png",    function ()
{
        cc.log("close
button was clicked."
);    },this);closeItem.setAnchorPoint(cc.p(0.5,
0.5));
var menu
= cc.Menu.create(closeItem);
menu.setPosition(cc.p(0,
0));
this.addChild(menu,
1);
closeItem.setPosition(cc.p(size.width
- 20, 20));

cc.MenuItemImage.create函數(shù)的第三個(gè)參數(shù),綁定了匿名回調(diào)函數(shù)。第四個(gè)參數(shù),傳入的是回調(diào)函數(shù)調(diào)用時(shí)的this(如果不理解JS的this機(jī)制,請(qǐng)先閱讀一些JS的資料)。這些都是意圖和作用很明顯的JS代碼,不用細(xì)說(shuō)。

然后,我們?nèi)タ吹讓訉?duì)應(yīng)執(zhí)行的C++代碼。在cocos2d_specifics.cpp文件中,找到j(luò)s_cocos2dx_CCMenuItemImage_create函數(shù)。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46//
"create" in JS
//
cc.MenuItemImage.create( normalImage, selectedImage, [disabledImage], callback_fn, [this]
JSBool
js_cocos2dx_CCMenuItemImage_create(JSContext *cx, uint32_t argc, jsval *vp)
{    if (argc
>= 2 && argc <= 5) {
        jsval
*argv = JS_ARGV(cx, vp);
        JSStringWrapper
arg0(argv[0]);
        JSStringWrapper
arg1(argv[1]);
        JSStringWrapper
arg2;
        bool thirdArgIsString
true;        jsval
jsCallback = JSVAL_VOID;
        jsval
jsThis = JSVAL_VOID;
        int last
= 2;
        if (argc
>= 3) {
            thirdArgIsString
= argv[2].isString();
            if (thirdArgIsString)
{
                arg2.set(argv[2],
cx);
                last
= 3;
            }        }        cocos2d::MenuItemImage*
ret = cocos2d::MenuItemImage::create(arg0.get(), arg1.get(), std::string(arg2.get()));
        if (argc
>= 3) {
            if (!thirdArgIsString)
{
                //cc.MenuItemImage.create(
normalImage, selectedImage, callback_fn, [this] )
                jsCallback
= argv[last++];
                if (argc
== 4) {
                    jsThis
= argv[last];
                }            }            else {                //cc.MenuItemImage.create(
normalImage, selectedImage, disabledImage, callback_fn, [this] )
                if (argc
>= 4) {
                    jsCallback
= argv[last++];
                    if (argc
== 5) {
                        jsThis
= argv[last];
                    }                }            }        }        JSObject
*obj = bind_menu_item<cocos2d::MenuItemImage>(cx, ret, jsCallback, jsThis);
        JS_SET_RVAL(cx,
vp, OBJECT_TO_JSVAL(obj));
        return JS_TRUE;    }    JS_ReportError(cx, "Invalid
number of arguments. Expecting: 2 <= args <= 5"
);    return JS_FALSE;}

因?yàn)樵贑++層,這是一個(gè)重載過(guò)的函數(shù),所以他的實(shí)現(xiàn)里面有很多參數(shù)個(gè)數(shù)的判斷(關(guān)于重載問(wèn)題請(qǐng)參考之前的章節(jié))。過(guò)濾掉很多代碼,我們直接看關(guān)鍵部分:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18if (argc
>= 3) {
            if (!thirdArgIsString)
{
                //cc.MenuItemImage.create(
normalImage, selectedImage, callback_fn, [this] )
                jsCallback
= argv[last++];
                if (argc
== 4) {
                    jsThis
= argv[last];
                }            }            else {                //cc.MenuItemImage.create(
normalImage, selectedImage, disabledImage, callback_fn, [this] )
                if (argc
>= 4) {
                    jsCallback
= argv[last++];
                    if (argc
== 5) {
                        jsThis
= argv[last];
                    }                }            }        }

在這里我們從參數(shù)中取出回調(diào)函數(shù)和this,分別賦值給jsCallback和jsThis。

1JSObject
*obj = bind_menu_item<cocos2d::MenuItemImage>(cx, ret, jsCallback, jsThis);

由這句模板函數(shù)來(lái)實(shí)現(xiàn)回調(diào)的綁定,四個(gè)參數(shù)依次是,JS上下文,cc.MenuItemImage對(duì)應(yīng)的C++對(duì)象,回調(diào)函數(shù),和回調(diào)函數(shù)調(diào)用時(shí)的this。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17template<class T>JSObject*
bind_menu_item(JSContext *cx, T* nativeObj, jsval callback, jsval thisObj) {  
    js_proxy_t
*p = jsb_get_native_proxy(nativeObj);
    if (p)
{
        addCallBackAndThis(p->obj,
callback, thisObj);
        return p->obj;    else {        js_type_class_t
*classType = js_get_type_from_native<T>(nativeObj);
        assert(classType);        JSObject
*tmp = JS_NewObject(cx, classType->jsclass, classType->proto, classType->parentProto);
        //
bind nativeObj <-> JSObject
        js_proxy_t
*proxy = jsb_new_proxy(nativeObj, tmp);
        JS_AddNamedObjectRoot(cx,
&proxy->obj, 
typeid(*nativeObj).name());              addCallBackAndThis(tmp,
callback, thisObj);
        return tmp;    }}

繼續(xù)看bind_menu_item的實(shí)現(xiàn)。簡(jiǎn)單說(shuō)一下,因?yàn)榻壎ǖ氖且粋€(gè)JS函數(shù),所以實(shí)際上,需要在SpiderMonkey里面做這個(gè)綁定操作。傳進(jìn)來(lái)的是一個(gè)C++對(duì)象(CCMenuItemImage類型),首先找到和這個(gè)C++對(duì)象對(duì)應(yīng)的JS對(duì)象。如果找不到,就新建立一個(gè)。然后通過(guò)函數(shù)addCallBackAndThis執(zhí)行綁定。

1 2 3 4 5 6 7 8 9static
void addCallBackAndThis(JSObject *obj, jsval callback, jsval &thisObj)
{    if(callback
!= JSVAL_VOID) {
        ScriptingCore::getInstance()->setReservedSpot(0,
obj, callback);
    }    if(thisObj
!= JSVAL_VOID) {
        ScriptingCore::getInstance()->setReservedSpot(1,
obj, thisObj);
    }}
1 2 3 4JSBool
ScriptingCore::setReservedSpot(uint32_t i, JSObject *obj, jsval value) {
    JS_SetReservedSlot(obj,
i, value);
    return JS_TRUE;}

最終我們看到,存儲(chǔ)回調(diào)函數(shù)的方法是通過(guò)SpiderMonkey的ReservedSlot機(jī)制。0位存放的是回調(diào)函數(shù),1位存放的是回調(diào)函數(shù)對(duì)應(yīng)的this。

好,到此為止,回調(diào)函數(shù)的綁定全部結(jié)束。

二、調(diào)用回調(diào)函數(shù)過(guò)程

現(xiàn)在我們看從C++層啟動(dòng)JS回調(diào)的過(guò)程。我們省略掉事件派發(fā)機(jī)制,直接看按鍵事件發(fā)生時(shí)的調(diào)用代碼。在按鍵事件發(fā)生時(shí),會(huì)調(diào)用MenuItemImage的父類MenuItem中的activate函數(shù)。該函數(shù)在CCMenuItem.cpp中。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17void MenuItem::activate(){    if (_enabled)    {        if(
_callback )
        {            _callback(this);        }                                                                                                                                    if (kScriptTypeNone
!= _scriptType)
        {            BasicScriptData
data(
this);            ScriptEvent
scriptEvent(kMenuClickedEvent,&data);
            ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);        }    }}

非常簡(jiǎn)單,首先判斷按鍵是否可用。然后如果有C++層回調(diào)就調(diào)用。如果有腳本層(JS或lua)回調(diào),就包裝一個(gè)kMenuClickedEvent事件,然后向?qū)?yīng)的腳本引擎發(fā)送該事件。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45int
ScriptingCore::sendEvent(ScriptEvent* evt)
{    if (NULL
== evt)
        return 0;                                                                     JSAutoCompartment
ac(_cx, _global);
                                                                        switch (evt->type)    {        case kNodeEvent:            {                return handleNodeEvent(evt->data);            }            break;        case kMenuClickedEvent:            {                return handleMenuClickedEvent(evt->data);            }            break;        case kTouchEvent:            {                return handleTouchEvent(evt->data);            }            break;        case kTouchesEvent:            {                return handleTouchesEvent(evt->data);            }            break;        case kKeypadEvent:            {                return handleKeypadEvent(evt->data);            }            break;        case kAccelerometerEvent:            {                return handleAccelerometerEvent(evt->data);            }            break;        default:            break;    }                                                                        return 0;}

JS通過(guò)ScriptingCore::sendEvent進(jìn)行事件分發(fā)。kMenuClickedEvent事件派發(fā)給handleMenuClickedEvent函數(shù)來(lái)處理。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20int
ScriptingCore::handleMenuClickedEvent(void* data)
{    if (NULL
== data)
        return 0;                                                               BasicScriptData*
basicScriptData = static_cast<BasicScriptData*>(data);
    if (NULL
== basicScriptData->nativeObject)
        return 0;                                                               MenuItem*
menuItem = static_cast<MenuItem*>(basicScriptData->nativeObject);
                                                               js_proxy_t
* p = jsb_get_native_proxy(menuItem);
    if (!p) return 0;    jsval
retval;
    jsval
dataVal;
    js_proxy_t
*proxy = jsb_get_native_proxy(menuItem);
    dataVal
= (proxy ? OBJECT_TO_JSVAL(proxy->obj) : JSVAL_NULL);
    executeJSFunctionFromReservedSpot(this->_cx,
p->obj, dataVal, retval);
    return 1;}
1 2 3 4 5 6 7 8 9 10 11 12 13 14static void executeJSFunctionFromReservedSpot(JSContext
*cx, JSObject *obj,
                                              jsval
&dataVal, jsval &retval) {
    jsval
func = JS_GetReservedSlot(obj, 0);
    if (func
== JSVAL_VOID) { 
return;
}
    jsval
thisObj = JS_GetReservedSlot(obj, 1);
    JSAutoCompartment
ac(cx, obj);
                           if (thisObj
== JSVAL_VOID) {
        JS_CallFunctionValue(cx,
obj, func, 1, &dataVal, &retval);
    else {        assert(!JSVAL_IS_PRIMITIVE(thisObj));        JS_CallFunctionValue(cx,
JSVAL_TO_OBJECT(thisObj), func, 1, &dataVal, &retval);
    }}

再次通過(guò)SpiderMonkey的ReservedSlot機(jī)制,取回相應(yīng)的參數(shù),最后通過(guò)JS_CallFunctionValue函數(shù)完成JS層回調(diào)函數(shù)的調(diào)用。

關(guān)于cocos2d-x中回調(diào)函數(shù)的按鍵回調(diào)是怎樣的就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

js
AI