您好,登錄后才能下訂單哦!
使用任何版本的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)之后崩潰
免責(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)容。