溫馨提示×

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

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

【cocos2d-x從c++到j(luò)s】13:回調(diào)函數(shù)2——JSCallbackWrapper

發(fā)布時(shí)間:2020-06-07 06:40:57 來(lái)源:網(wǎng)絡(luò) 閱讀:4803 作者:老G 欄目:游戲開(kāi)發(fā)

上一篇我們講了按鍵回調(diào),這一次我們來(lái)說(shuō)說(shuō)各種邏輯上的回調(diào)函數(shù)。


Cocos2d-x里面一共有三大類回調(diào)函數(shù),第一是按鍵回調(diào)CCMenu相關(guān)的,第二類是定時(shí)器相關(guān)的回調(diào)

Schedule,第三類是Action相關(guān)的回調(diào)CallFunc。這些回調(diào)從最初的引擎版本中就存在著,一直到現(xiàn)在。


一、綁定代碼


在JSB的解決方案中,對(duì)于后兩類函數(shù),引擎統(tǒng)一封裝成JSCallbackWrapper及其子類。

class JSCallbackWrapper: public cocos2d::Object {
public:
    JSCallbackWrapper();
    virtual ~JSCallbackWrapper();
    void setJSCallbackFunc(jsval obj);
    void setJSCallbackThis(jsval thisObj);
    void setJSExtraData(jsval data);
                                                                                                                                                                                                                                                                                                                                                                                                                 
    const jsval& getJSCallbackFunc() const;
    const jsval& getJSCallbackThis() const;
    const jsval& getJSExtraData() const;
protected:
    jsval _jsCallback;
    jsval _jsThisObj;
    jsval _extraData;
};


JSCallbackWrapper從名字就可以知道,是JS回調(diào)函數(shù)的包裝器。三個(gè)接口也一目了然,回調(diào)函數(shù),this,外部數(shù)據(jù)。


// cc.CallFunc.create( func, this, [data])
// cc.CallFunc.create( func )
static JSBool js_callFunc(JSContext *cx, uint32_t argc, jsval *vp)
{
    if (argc >= 1 && argc <= 3) {
        jsval *argv = JS_ARGV(cx, vp);
        std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper());
                                                                                                                                                                                                                                                                                                                                                                            
        tmpCobj->setJSCallbackFunc(argv[0]);
        if(argc >= 2) {
            tmpCobj->setJSCallbackThis(argv[1]);
        } if(argc == 3) {
            tmpCobj->setJSExtraData(argv[2]);
        }
                                                                                                                                                                                                                                                                                                                                                                            
        CallFuncN *ret = CallFuncN::create([=](Node* sender){
            const jsval& jsvalThis = tmpCobj->getJSCallbackThis();
            const jsval& jsvalCallback = tmpCobj->getJSCallbackFunc();
            const jsval& jsvalExtraData = tmpCobj->getJSExtraData();
                                                                                                                                                                                                                                                                                                                                                                                
            bool hasExtraData = !JSVAL_IS_VOID(jsvalExtraData);
            JSObject* thisObj = JSVAL_IS_VOID(jsvalThis) ? nullptr : JSVAL_TO_OBJECT(jsvalThis);
                                                                                                                                                                                                                                                                                                                                                                                
            JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
                                                                                                                                                                                                                                                                                                                                                                                
            js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender);
                                                                                                                                                                                                                                                                                                                                                                                
            jsval retval;
            if(jsvalCallback != JSVAL_VOID)
            {
                if (hasExtraData)
                {
                    jsval valArr[2];
                    valArr[0] = OBJECT_TO_JSVAL(proxy->obj);
                    valArr[1] = jsvalExtraData;
                                                                                                                                                                                                                                                                                                                                                                                        
                    JS_AddValueRoot(cx, valArr);
                    JS_CallFunctionValue(cx, thisObj, jsvalCallback, 2, valArr, &retval);
                    JS_RemoveValueRoot(cx, valArr);
                }
                else
                {
                    jsval senderVal = OBJECT_TO_JSVAL(proxy->obj);
                    JS_AddValueRoot(cx, &senderVal);
                    JS_CallFunctionValue(cx, thisObj, jsvalCallback, 1, &senderVal, &retval);
                    JS_RemoveValueRoot(cx, &senderVal);
                }
            }
                                                                                                                                                                                                                                                                                                                                                                                
            // I think the JSCallFuncWrapper isn't needed.
            // Since an action will be run by a cc.Node, it will be released at the Node::cleanup.
            // By James Chen
            // JSCallFuncWrapper::setTargetForNativeNode(node, (JSCallFuncWrapper *)this);
        });
                                                                                                                                                                                                                                                                                                                                                                            
        js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::CallFunc>(cx, ret);
        JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(proxy->obj));
                                                                                                                                                                                                                                                                                                                                                                            
        JS_SetReservedSlot(proxy->obj, 0, argv[0]);
        if(argc > 1) {
            JS_SetReservedSlot(proxy->obj, 1, argv[1]);
        }
//        if(argc == 3) {
//            JS_SetReservedSlot(proxy->obj, 2, argv[2]);
//        }
                                                                                                                                                                                                                                                                                                                                                                            
      //  test->execute();
        return JS_TRUE;
    }
    JS_ReportError(cx, "Invalid number of arguments");
    return JS_FALSE;
}

這是JS層調(diào)用cc.CallFunc.create時(shí),底層執(zhí)行的C++函數(shù),這里面用了一些C++11的特性,包括std::shared_ptr智能指針和lambda表達(dá)式(也很簡(jiǎn)單,不熟悉的童鞋可以自己找資料熟悉下)。


這里面回調(diào)函數(shù)被封裝到了lambda表達(dá)式里面,通過(guò)=方式引用外部的tmpCobj變量,這種方式跟JS的閉包非常類似。依然使用JS_CallFunctionValue進(jìn)行函數(shù)調(diào)用。注意,這種調(diào)用方式跟JS里面的apply方式是很類似的。


這里面有一對(duì)函數(shù)非常有趣,JS_AddValueRoot和JS_RemoveValueRoot,這兩個(gè)函數(shù)JS_CallFunctionValue調(diào)用包起來(lái)了。因?yàn)檫@個(gè)valArr或senderVal是在棧上臨時(shí)生成的,沒(méi)有指定對(duì)應(yīng)的root。但是中間又進(jìn)行了JS函數(shù)的調(diào)用,所以這兩個(gè)值可能在JS函數(shù)調(diào)用的時(shí)候被SpiderMonkey虛擬機(jī)給垃圾回收掉(可以去看看JS的垃圾回收機(jī)制原理)。于是我們需要給他們掛一個(gè)root,保護(hù)一下,不被回收掉。


二、調(diào)用代碼


先看一下構(gòu)造函數(shù)

CallFuncN * CallFuncN::create(const std::function<void(Node*)> &func)
{
    auto ret = new CallFuncN();
    if (ret && ret->initWithFunction(func) ) {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}

bool CallFuncN::initWithFunction(const std::function<void (Node *)> &func)
{
    _functionN = func;
    return true;
}


傳進(jìn)來(lái)的lambda表達(dá)式被存為一個(gè)std::function<void(Node*)>類型。


調(diào)用代碼異常簡(jiǎn)單,使用_functionN進(jìn)行調(diào)用即可。

void CallFuncN::execute() {
    if (_callFuncN) {
        (_selectorTarget->*_callFuncN)(_target);
    }
    else if (_functionN) {
        _functionN(_target);
    }
}



對(duì)比上一篇中的方式,我認(rèn)為這種調(diào)用方式更加合理,因?yàn)檫@種調(diào)用方式,對(duì)C++層Core代碼,隱藏了腳本機(jī)制。而之前的調(diào)用方式是顯示通過(guò)腳本引擎來(lái)調(diào)用的。

看完此篇和前篇,我們仔細(xì)分析了Cocos2d-x JSB里面的回調(diào)函數(shù)的寫(xiě)法,詳細(xì)對(duì)于讀者而言自己實(shí)現(xiàn)一個(gè)回調(diào)函數(shù)已經(jīng)不是什么特別困難的事情。


在剛完成此篇的時(shí)候,突然發(fā)現(xiàn)有這么一個(gè)帖子,講的也是JSB回調(diào)函數(shù),寫(xiě)得很不錯(cuò),還是IAP的,可以作為額外閱讀參考:

Cocos2d-x使用iOS游戲內(nèi)付費(fèi)IAP(JSB篇)


還有一篇可以學(xué)習(xí)的:

JS的回調(diào)函數(shù)的參數(shù)構(gòu)造注記——Web學(xué)習(xí)筆記(十八)


關(guān)于回調(diào)函數(shù)的問(wèn)題,先說(shuō)這些吧。


下篇繼續(xù),我們來(lái)討論一下注冊(cè)函數(shù)的事


向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)容。

AI