使用任何版本的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的更多相关文章

  1. [Cocos2d-x For WP8]Layer 层

        层(CCLayer) 从概念上说,层就是场景里的背景. CCLayer同样是CCNode的子类,通常用addChild方法添加子节点.CCLayer对象定义了可描绘的区域,定义了描绘的规则.C ...

  2. 骨骼动画 cocos2d-x + cocoStudio <cocos2d-x : version 2.2.0>

    cocos2d-x version 2.2.0 首先,在 HelloWorldScene.cpp 中的 init()函数中 添加如下代码 (资源文件可以直接到 cocos2d-x 中获取) #incl ...

  3. 【转】cocos2d-x 3.2 Fast TileMap

    概述 在游戏中常常会有丰富的背景元素,如果直接使用大的背景图实现,这会造成资源浪费.TileMap就是为了解决这问题而产生的.Cocos2d-x支持使用Tile地图编辑器创建的TMX格式的地图. Co ...

  4. 实例介绍Cocos2d-x物理引擎:碰撞检测

    碰撞检测是使用物理引擎的一个重要目的,使用物理引擎可以进行精确的碰撞检测,而且执行的效率也很高.在Cocos2d-x 3.x中使用事件派发机制管理碰撞事件,EventListenerPhysicsCo ...

  5. Make Things Move -- Javascript html5版(二)实现最基本的Sprite类和stage管理对象

    现在这几篇写的都是比较基础的东西,有过相应开发经验的朋友可直接忽略啦. 计算机模拟的动画都是由很多静态的一连串影像(sprite)在一定帧率(fps)内逐帧播放出来的. 对于js来说,我们可以用提供的 ...

  6. Cocos2d 3.0继承自Sprite的类在addChild后出现故障

    当继承自Sprite的类被addChild到其它的Node里后出现例如以下图问题,说明没有调用父类Sprite::init()的方法.由于父类Sprite里的_textureAtlas须要初始化为nu ...

  7. cocos2d触碰例子代码

    // // TestLayer.h // MiniTD // // Created by OnePiece on 12-7-30. // Copyright 2012年 __MyCompanyName ...

  8. [cocos2d]场景切换以及切换进度显示

    本文主要分两个部分叙述,第一是场景切换,第二是场景切换的进度显示. 一.场景切换 参考learn-iphone-and-ipad-cocos2d-game-development 第五章内容 coco ...

  9. 使用过渡场景在多个场景的切换COCOS2D(4)

    CCNode有三个方法,使用CCDirector的replaceScene方法替换场景时,每个节点都会调用这三个方法: onEnter与onExit方法在改变场景过程中的特定时刻被调用,这取决于是否使 ...

随机推荐

  1. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  2. SQL Server相关书籍

    SQL Server相关书籍 (排名不分先后) Microsoft SQL Server 企业级平台管理实践 SQL Server 2008数据库技术内幕 SQL Server性能调优实战 SQL S ...

  3. Castle Core 4.0.0 alpha001发布

    时隔一年多以后Castle 项目又开始活跃,最近刚发布了Castle Core 4.0.0 的alpha版本, https://github.com/castleproject/Core/releas ...

  4. C# Excel导入、导出【源码下载】

    本篇主要介绍C#的Excel导入.导出. 目录 1. 介绍:描述第三方类库NPOI以及Excel结构 2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图.NOPI以及C#代 ...

  5. 初步认识TDD

    TDD,测试驱动开发(Test Driven Development)是极限编程中倡导的程序开发方法,以其倡导先写测试程序,然后编码实现其功能得名.本文将对TDD有一个较为系统的认识.    基础属性 ...

  6. Springboot搭建web项目

    最近因为项目需要接触了springboot,然后被其快速零配置的特点惊呆了.关于springboot相关的介绍我就不赘述了,大家自行百度google. 一.pom配置 首先,建立一个maven项目,修 ...

  7. EC笔记:第4部分:21、必须返回对象时,别返回引用

    使用应用可以大幅减少构造函数与析构函数的调用次数,但是引用不可以滥用. 如下: struct St { int a; }; St &func(){ St t; return t; } 在返回t ...

  8. DDD领域驱动设计 - 设计文档模板

    设计文档模板: 系统背景和定位 业务需求描述 系统用例图 关键业务流程图 领域语言整理,主要是整理领域中的各种术语的定义,名词解释 领域划分(分析出子域.核心域.支撑域) 每个子域的领域模型设计(实体 ...

  9. css知多少之绝对定位小记

    一.position定位常见属性 对于属性position来说,属性值有static/relative/absolute/fixed/inherit以下只对绝对定位position:absolute详 ...

  10. js attribute 和 jquery attr 方法

    attribute 是原生js dom 对象上的一个属性,这个属性有很多子属性,比如 isId(判断属性是否是Id) , name (获取属性名称) , value (获取属性值),attribute ...