溫馨提示×

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

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

Cocos2d-x不要隨便在onEnter里面addChild

發(fā)布時(shí)間:2020-06-03 01:35:47 來(lái)源:網(wǎng)絡(luò) 閱讀:353 作者:wyb10a10 欄目:游戲開發(fā)

使用任何版本的Cocos2d-x(1.x,2.x,3.0),在onEnter中調(diào)用addChild,都要小心謹(jǐn)慎,因?yàn)樗锌赡軐?dǎo)致兩種莫名其妙的BUG,莫名其妙的BUG當(dāng)然難以定位了!更何況這個(gè)BUG隱藏在引擎的底層。

接下來(lái)是場(chǎng)景還原:

在某個(gè)節(jié)點(diǎn)下,需要執(zhí)行這樣一段邏輯,在游戲場(chǎng)景中,添加幾個(gè)節(jié)點(diǎn),由于游戲場(chǎng)景就是該節(jié)點(diǎn)的父節(jié)點(diǎn),于是就直接getParent然后調(diào)用父節(jié)點(diǎn)的addChild,在onEnter函數(shù)中添加看上去比較合適,因?yàn)檫@時(shí)候該節(jié)點(diǎn)的父節(jié)點(diǎn)可以訪問(wèn),而在init函數(shù)中,還沒(méi)有被添加到游戲場(chǎng)景中

神奇的事情發(fā)生了,在這之后添加的節(jié)點(diǎn),都無(wú)法播放動(dòng)畫了,而把節(jié)點(diǎn)添加的位置,移到該節(jié)點(diǎn)之前進(jìn)行添加,動(dòng)畫就可以正常播放,檢查了一下代碼,無(wú)果,先記下該問(wèn)題

接下來(lái)又有一件神奇的事情發(fā)生了,我們的程序崩潰了!用排除法發(fā)現(xiàn),是在onEnter下添加節(jié)點(diǎn)導(dǎo)致的崩潰,但是有趣的是,onEnter下的一個(gè)for循環(huán)添加5個(gè)節(jié)點(diǎn),當(dāng)我把節(jié)點(diǎn)數(shù)量該為4的時(shí)候,程序又可以正常執(zhí)行了!而添加到5或者更多的時(shí)候,程序又崩潰了!

看到這里我仿佛明白了什么,打開2dx的CCNode::addChild的代碼,在每次addChild的時(shí)候,會(huì)根據(jù)當(dāng)前數(shù)組的容量,進(jìn)行擴(kuò)容

void ccArrayDoubleCapacity(ccArray *arr)
{
    arr->max *= 2;
    CCObject** newArr = (CCObject**)realloc( arr->arr, arr->max * sizeof(CCObject*) );
    // will fail when there's not enough memory
    CCAssert(newArr != 0, "ccArrayDoubleCapacity failed. Not enough memory");
    arr->arr = newArr;
}

上面的代碼用realloc重新分配了內(nèi)存,但是,在CCNode的onEnter中,是在遍歷這個(gè)數(shù)組,執(zhí)行所有子節(jié)點(diǎn)的onEnter

void CCNode::onEnter()
{
    arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*);
    
    this->resumeSchedulerAndActions();

    m_bIsRunning = true;

    if (m_eScriptType != kScriptTypeNone)
    {
        CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnEnter);
    }
}

在arrayMakeObjectsPerformSelector中,調(diào)用到了2dx底層的一個(gè)宏,CCARRAY_FOREACH

#define CCARRAY_FOREACH(__array__, __object__)                                                                \
    if ((__array__) && (__array__)->data->num > 0)                                                            \
    for(CCObject** arr = (__array__)->data->arr, **end = (__array__)->data->arr + (__array__)->data->num-1;    \
    arr <= end && (((__object__) = *arr) != NULL/* || true*/);                                                \
    arr++)

這個(gè)宏用于遍歷CCArray,它是用指針偏移的方式進(jìn)行遍歷,所以,當(dāng)我們的數(shù)組擴(kuò)容之后,指針的地址就變了,CCARRAY_FOREACH還在對(duì)原先的指針進(jìn)行訪問(wèn),當(dāng)然崩潰了
其實(shí)這個(gè)BUG很好解決,只需要修改一下CCARRAY_FOREACH的遍歷方式,改為下標(biāo)訪問(wèn)即可,在CCNode::onEnter函數(shù)下,將代碼調(diào)整為如下所示,BUG解決。

void CCNode::onEnter()
{
    //arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*);
    if (NULL != m_pChildren)
    {
        for (int i = 0; i < m_pChildren->count(); ++i)
        {
            ((CCNode*)(m_pChildren->data->arr[i]))->onEnter();
        }
    }
    
    this->resumeSchedulerAndActions();

    m_bIsRunning = true;

    if (m_eScriptType != kScriptTypeNone)
    {
        CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnEnter);
    }
}

也許我不應(yīng)該在onEnter里面addChild,但cocos2d-x更不應(yīng)該讓我在onEnter中添加節(jié)點(diǎn)之后崩潰

向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