Cocos2d-x不要随便在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的时候,会根据当前数组的容量,进行扩容
void ccArrayDoubleCapacity(ccArray *arr)
{
arr->max *= ;
CCObject** newArr = (CCObject**)realloc( arr->arr, arr->max * sizeof(CCObject*) );
// will fail when there's not enough memory
CCAssert(newArr != , "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 > ) \
for(CCObject** arr = (__array__)->data->arr, **end = (__array__)->data->arr + (__array__)->data->num-; \
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 = ; 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中添加节点之后崩溃
Cocos2d-x不要随便在onEnter里面addChild的更多相关文章
- [Cocos2d-x For WP8]Layer 层
层(CCLayer) 从概念上说,层就是场景里的背景. CCLayer同样是CCNode的子类,通常用addChild方法添加子节点.CCLayer对象定义了可描绘的区域,定义了描绘的规则.C ...
- 骨骼动画 cocos2d-x + cocoStudio <cocos2d-x : version 2.2.0>
cocos2d-x version 2.2.0 首先,在 HelloWorldScene.cpp 中的 init()函数中 添加如下代码 (资源文件可以直接到 cocos2d-x 中获取) #incl ...
- 【转】cocos2d-x 3.2 Fast TileMap
概述 在游戏中常常会有丰富的背景元素,如果直接使用大的背景图实现,这会造成资源浪费.TileMap就是为了解决这问题而产生的.Cocos2d-x支持使用Tile地图编辑器创建的TMX格式的地图. Co ...
- 实例介绍Cocos2d-x物理引擎:碰撞检测
碰撞检测是使用物理引擎的一个重要目的,使用物理引擎可以进行精确的碰撞检测,而且执行的效率也很高.在Cocos2d-x 3.x中使用事件派发机制管理碰撞事件,EventListenerPhysicsCo ...
- Make Things Move -- Javascript html5版(二)实现最基本的Sprite类和stage管理对象
现在这几篇写的都是比较基础的东西,有过相应开发经验的朋友可直接忽略啦. 计算机模拟的动画都是由很多静态的一连串影像(sprite)在一定帧率(fps)内逐帧播放出来的. 对于js来说,我们可以用提供的 ...
- Cocos2d 3.0继承自Sprite的类在addChild后出现故障
当继承自Sprite的类被addChild到其它的Node里后出现例如以下图问题,说明没有调用父类Sprite::init()的方法.由于父类Sprite里的_textureAtlas须要初始化为nu ...
- cocos2d触碰例子代码
// // TestLayer.h // MiniTD // // Created by OnePiece on 12-7-30. // Copyright 2012年 __MyCompanyName ...
- [cocos2d]场景切换以及切换进度显示
本文主要分两个部分叙述,第一是场景切换,第二是场景切换的进度显示. 一.场景切换 参考learn-iphone-and-ipad-cocos2d-game-development 第五章内容 coco ...
- 使用过渡场景在多个场景的切换COCOS2D(4)
CCNode有三个方法,使用CCDirector的replaceScene方法替换场景时,每个节点都会调用这三个方法: onEnter与onExit方法在改变场景过程中的特定时刻被调用,这取决于是否使 ...
随机推荐
- 移动先行之谁主沉浮? 带着你的Net飞奔吧!
移动系源码:https://github.com/dunitian/Windows10 移动系文档:https://github.com/dunitian/LoTDotNet/tree/master/ ...
- 博客使用BOS上传图片
1.博客平台的选定 从大学开始做个人主页算起,最开始是使用html,CSSS写简单的页面,后面大学毕业之后接触到了WordPress,就开始用WordPress搭建网站.现在还维护着一个农村网站.ht ...
- 理解nodejs模块的scope
描述 原文档地址:https://docs.npmjs.com/misc/scope 所有npm模块都有name,有的模块的name还有scope.scope的命名规则和name差不多,同样不能有ur ...
- PayPal高级工程总监:读完这100篇论文 就能成大数据高手(附论文下载)
100 open source Big Data architecture papers for data professionals. 读完这100篇论文 就能成大数据高手 作者 白宁超 2016年 ...
- 接口--interface
“interface”(接口)关键字使抽象的概念更深入了一层.我们可将其想象为一个“纯”抽象类.它允许创建者规定一个类的基本形式:方法名.自变量列表以及返回类型,但不规定方法主体.接口也包含了基本数据 ...
- 【云知道】LoadRunner 录制问题集锦
关键词:各路录制小白汇集于此 虽然知道君对录制不感冒,但总是看到扎堆的人说这些问题,忍不住要站出来了. 百度虽好,帮助了很多小白,但关键是百度并没有排除错误内容,经过历史的几年传播,错的都快变对的了, ...
- javascript动画系列第二篇——磁性吸附
× 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性 ...
- mount报错: you must specify the filesystem type
在linux mount /dev/vdb 到 /home 分区时报错: # mount /dev/vdb /homemount: you must specify the filesystem ty ...
- Java中常用集合操作
一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...
- arcpy+PyQt+py2exe快速开发桌面端ArcGIS应用程序
前段时间有一个项目,大体是要做一个GIS数据处理工具. 一般的方法是基于ArcObjects来进行开发,因为我对ArcObjects不太熟悉,所以就思考有没有其他简单快速的方法来做. 在查看ArcGI ...