您好,登錄后才能下訂單哦!
使用任何版本的Cocos2d-x(1.x,2.x,3.0),在onEnter中調用addChild,都要小心謹慎,因為它有可能導致兩種莫名其妙的BUG,莫名其妙的BUG當然難以定位了!更何況這個BUG隱藏在引擎的底層。
接下來是場景還原:
在某個節點下,需要執行這樣一段邏輯,在游戲場景中,添加幾個節點,由于游戲場景就是該節點的父節點,于是就直接getParent然后調用父節點的addChild,在onEnter函數中添加看上去比較合適,因為這時候該節點的父節點可以訪問,而在init函數中,還沒有被添加到游戲場景中
神奇的事情發生了,在這之后添加的節點,都無法播放動畫了,而把節點添加的位置,移到該節點之前進行添加,動畫就可以正常播放,檢查了一下代碼,無果,先記下該問題
接下來又有一件神奇的事情發生了,我們的程序崩潰了!用排除法發現,是在onEnter下添加節點導致的崩潰,但是有趣的是,onEnter下的一個for循環添加5個節點,當我把節點數量該為4的時候,程序又可以正常執行了!而添加到5或者更多的時候,程序又崩潰了!
看到這里我仿佛明白了什么,打開2dx的CCNode::addChild的代碼,在每次addChild的時候,會根據當前數組的容量,進行擴容
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重新分配了內存,但是,在CCNode的onEnter中,是在遍歷這個數組,執行所有子節點的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中,調用到了2dx底層的一個宏,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++)
這個宏用于遍歷CCArray,它是用指針偏移的方式進行遍歷,所以,當我們的數組擴容之后,指針的地址就變了,CCARRAY_FOREACH還在對原先的指針進行訪問,當然崩潰了
其實這個BUG很好解決,只需要修改一下CCARRAY_FOREACH的遍歷方式,改為下標訪問即可,在CCNode::onEnter函數下,將代碼調整為如下所示,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); } }
也許我不應該在onEnter里面addChild,但cocos2d-x更不應該讓我在onEnter中添加節點之后崩潰
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。