不要随便在onEnter()里面addChild()

使用任何版本的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的时候,会根据当前数组的容量,进行扩容。

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

上面的代码用realloc重新分配了内存,但是,在CCNode的onEnter中,是在遍历这个数组,执行所有子节点的onEnter

 1 void CCNode::onEnter()
 2 {
 3     arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*);
 4
 5     this->resumeSchedulerAndActions();
 6
 7     m_bIsRunning = true;
 8
 9     if (m_eScriptType != kScriptTypeNone)
10     {
11         CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnEnter);
12     }
13 }

在arrayMakeObjectsPerformSelector中,调用到了2dx底层的一个宏,CCARRAY_FOREACH

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

这个宏用于遍历CCArray,它是用指针偏移的方式进行遍历,所以,当我们的数组扩容之后,指针的地址就变了,CCARRAY_FOREACH还在对原先的指针进行访问,当然崩溃了
其实这个BUG很好解决,只需要修改一下CCARRAY_FOREACH的遍历方式,改为下标访问即可,在CCNode::onEnter函数下,将代码调整为如下所示,BUG解决

 1 void CCNode::onEnter()
 2 {
 3     //arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*);
 4     if (NULL != m_pChildren)
 5     {
 6         for (int i = 0; i < m_pChildren->count(); ++i)
 7         {
 8             ((CCNode*)(m_pChildren->data->arr[i]))->onEnter();
 9         }
10     }
11
12     this->resumeSchedulerAndActions();
13
14     m_bIsRunning = true;
15
16     if (m_eScriptType != kScriptTypeNone)
17     {
18         CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnEnter);
19     }
20 }
时间: 2024-11-05 15:56:27

不要随便在onEnter()里面addChild()的相关文章

关于Cocos2d-x中addchild和removeChild方法的参数的解析

一.addchild virtual void addchild( Node * child , int localZOrder , int tag )添加一个子节点到容器中,有Z轴顺序和一个标记. 1.如果子节点被添加到了一个“running(活动着的)”节点,那么'onEnter'和 'onEnterTransitionDidFinish' 将会立即调用. 2.参数解析 addChild:是指要添加的子成员(Node *类型,可移式Sprite, Layer等等): z:是指添加的ZOrde

cocos2d-html5 onEnter init ctor构造函数 ----js特有特性(和c++有一点不一样)

ctor 构造函数, new 一个对象的时候调用-----coco2d-js , 默认ctor,为对象的构造函数,其它也可以默认其它函数为构造函数. 说白了就是: ctor构造函数 new 对象后自动调用,init 在cocos2d-x 里面是 静态函数 create() 方式创建对象自动调用(是因为cocos2d-x 提供的宏,会自动调用),但是再 js 里面,需要手工调用. -------------ctor构造函数创建对象,传入ctor函数方法的不同,调用初始化init方法不一样.onEn

cocos2djs ctor init onEnter的区别

cocos2d-html5 onEnter init ctor构造函数 ---js特有特性(和c++有点不一样 ctor 构造函数, new 一个对象的时候调用-----coco2d-js , 默认ctor,为对象的构造函数,其它也可以默认其它函数为构造函数. 说白了就是: ctor构造函数 new 对象后自动调用, init 在cocos2d-x 里面是 静态函数 create() 方式创建对象自动调用(是因为cocos2d-x 提供的宏,会自动调用),但是再 js 里面,需要手工调用. --

idea启动tomcat服务失败 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild:

我的工程是从eclipse生成的,个人习惯用idea开发.重复了一遍以往正常的不能再正常了的导入配置,结果遇到了如下问题: SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start comp

java.lang.IllegalStateException: ContainerBase.addChild: start

java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina] Tomcat部署Servlet时出错 caused by: Caused by: java.lang.IllegalArgumentException: Invalid <url-patt

Jquery 随便写些知识点

针对jQuery随便写些觉得还挺实用的一些东西,也没系统的去理一番,只是想到哪写到哪,写的不完全也请多见谅. jQuery和其他javascript库产生$符号冲突了?$符号想必用jQuery的人都不生疏,$即代表着jQuery函数.然而$符号并不是jQuery私有的一个符号,其他javascript库也可以使用$符号作为他们的主函数.那么,当我们用的其他的库与jQuery库发生$符号冲突时该怎么办呢?我们用代码来验证下: console.log(jQuery === $); //true jQ

【随便走走】Vietnam

从来没有一个地方让我如此留念过.   初到越南印象就是乱,满街轰轰轰的摩托车,狭窄的街道,各种小酒店小商店.从机场出来的路上还看到了不少中国品牌如豪爵摩托等等. 落地办理了落地签,从大陆是不能办的.越南对绝大部分国家是不免签的.当你跟一群白人一起填表的时候才会觉得有点公道. 机场的ATM最小的取款金额是800w,折合人民币就是2400.不知道那时候脑子怎么抽了,居然取了1600W,才停留3天啊! 我出门有个习惯就是不订酒店,有好处也有坏处.好处是走到哪里就在哪里住,坏处是有时候价格上会吃亏,或者

第五讲:OpenGL坐标系和UIKit坐标系、锚点、addChild函数详解

一.坐标系 OpenGl坐标系 原点在左下角(0,0),与数据的二维坐标系一致 UIKit坐标系 又称为屏幕坐标系,原点在左上角,X轴越右越大,Y轴越下越大: 由OpenGL转化为UIKit的方法: CCPoint point = CCDirector::sharedDirector()->convertToUI(sp1->getPosition); 二.锚点 锚点默认为(0.5,0.5) 就是在精灵的中间 : *****坐标系已锚点定位(先确定锚点在哪,ccp(X,Y)是描述锚点在哪个位置)

模拟新浪微博随便看看栏目

使用ListView来模仿微博随便看看栏 [分析] 要完成这个效果,我们需要: 1.ListView及ListView_Item 2.实体类的编写 3.自定义适配器的书写 4.ListView的绑定数据源与控件 [编码] package cn.edu.bzu.adapter; import java.util.List; import cn.edu.bzu.entity.Information;import cn.edu.bzu.weibo.R;import android.content.Co