热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Cocos2d-xCCNode的节点操作——添加、移除和渲染

CCNode是Cocos2d-x的一个非常重要的概念,所有可以被渲染或者包含可渲染的都是一个CCNode。最主要的CCNode有:CCScene,CCLayer,CCSprite

        CCNode是Cocos2d-x的一个非常重要的概念,所有可以被渲染或者包含可渲染的都是一个CCNode。最主要的CCNode有:CCScene,CCLayer,CCSprite,CCMenu。关于CCNode的源码分析网上大把,但是因为介绍的东西太多而导致里面的很多细节都不能涉及到。这篇文章主要是讨论一下节点的添加、移除和渲染,也就是我们经常用到的addChild、removeChild和draw。


1、addChild:

        addChild有三个重载函数:参数分别是child(添加的节点)、order(渲染顺序)、tag(标签)

/* 
* “add”逻辑只能在这个方法执行
* 如果一个类不拓展'addChild'的行为则只需重写这个方法
*/
void CCNode::addChild(CCNode *child, int zOrder, int tag)
{
CCAssert( child != NULL, "Argument must be non-nil");
CCAssert( child->m_pParent == NULL, "child already added. It can't be added again");

// 1、孩子数组为空则先申请空间
if( ! m_pChildren )
{
this->childrenAlloc();
}

// 2、插入子节点
this->insertChild(child, zOrder);

child->m_nTag = tag;

// 3、设置父节点和到达的顺序
child->setParent(this);
child->setOrderOfArrival(s_globalOrderOfArrival++);

// 4、如果添加的父节点正在运行,则调用子节点的下面两个方法
if( m_bRunning )
{
child->onEnter();
child->onEnterTransitionDidFinish();
}
}

void CCNode::addChild(CCNode *child, int zOrder)
{
CCAssert( child != NULL, "Argument must be non-nil");
this->addChild(child, zOrder, child->m_nTag);
}

void CCNode::addChild(CCNode *child)
{
CCAssert( child != NULL, "Argument must be non-nil");
this->addChild(child, child->m_nZOrder, child->m_nTag);
}
我们只看第一个函数就行,后面两个都是调用第一个函数的。


重点1:insertChild

// helper used by reorderChild & add
void CCNode::insertChild(CCNode* child, int z)
{
m_bReorderChildDirty = true; // 用于sortAllChildren排序
ccArrayAppendObjectWithResize(m_pChildren->data, child); // 将child添加到m_pChildren中
child->_setZOrder(z); // 设置渲染顺序
}
// 添加一个对象到指定的数组中。如果需要可以增加数组的容量
void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr);

重点2:如果父节点处于运行状态,则会调用子节点的onEnter()和onEnterTransitionDidFinish()函数



2、removeChild

void CCNode::removeFromParent()
{
this->removeFromParentAndCleanup(true);
}

void CCNode::removeFromParentAndCleanup(bool cleanup)
{
if (m_pParent != NULL)
{
m_pParent->removeChild(this,cleanup);
}
}

void CCNode::removeChild(CCNode* child)
{
this->removeChild(child, true);
}

/* "remove" logic MUST only be on this method
* If a class want's to extend the 'removeChild' behavior it only needs
* to override this method
*/
void CCNode::removeChild(CCNode* child, bool cleanup)
{
// explicit nil handling
if (m_pChildren == NULL)
{
return;
}

// 如果包含该子节点,调用detachChild
if ( m_pChildren->containsObject(child) )
{
this->detachChild(child,cleanup);
}
}

void CCNode::removeChildByTag(int tag)
{
this->removeChildByTag(tag, true);
}

void CCNode::removeChildByTag(int tag, bool cleanup)
{
CCAssert( tag != kCCNodeTagInvalid, "Invalid tag");

CCNode *child = this->getChildByTag(tag);

if (child == NULL)
{
CCLOG("cocos2d: removeChildByTag(tag = %d): child not found!", tag);
}
else
{
this->removeChild(child, cleanup);
}
}

void CCNode::removeAllChildren()
{
this->removeAllChildrenWithCleanup(true);
}

void CCNode::removeAllChildrenWithCleanup(bool cleanup)
{
// not using detachChild improves speed here
if ( m_pChildren && m_pChildren->count() > 0 )
{
CCObject* child;
CCARRAY_FOREACH(m_pChildren, child)
{
CCNode* pNode = (CCNode*) child;
if (pNode)
{
// IMPORTANT:
// -1st do onExit
// -2nd cleanup
if(m_bRunning)
{
pNode->onExitTransitionDidStart();
pNode->onExit();
}

if (cleanup)
{
pNode->cleanup();
}
// set parent nil at the end
pNode->setParent(NULL);
}
}

m_pChildren->removeAllObjects();
}

}

void CCNode::detachChild(CCNode *child, bool doCleanup)
{
// IMPORTANT:
// -1st do onExit
// -2nd cleanup
// 第一步执行OnExit,第二步执行cleanup
if (m_bRunning)
{
child->onExitTransitionDidStart();
child->onExit();
}

// If you don't do cleanup, the child's actions will not get removed and the
// its scheduledSelectors_ dict will not get released!
// 如果不执行cleanup,子节点的动作不会被移除,定时器也不会被释放
if (doCleanup)
{
child->cleanup();
}

// set parent nil at the end
// 设置父节点为空
child->setParent(NULL);

// 从数组中移除
m_pChildren->removeObject(child);
}

不管是removeFromParent还是removeChildByTag还是removeAllChild,最终都是调用removeChild,removeChild则调用detachChild。
1、如果父节点正在运行,调用子节点的onExitTransitionDidStart()和onExit()。
2、关于cleanup问题,如果执行cleanup函数,则会移除该节点的所有动作、定时器和其子节点的cleanup。不执行的话子节点动作不会被移除,定时器也不会被释放。
3、设置父节点为空并从数组中移除。


关于cleanup:

void CCNode::cleanup()
{
// 停止所有动作和定时器
this->stopAllActions();
this->unscheduleAllSelectors();

if ( m_eScriptType != kScriptTypeNone)
{
CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnCleanup);
}

// 调用所有子节点的cleanup函数
arrayMakeObjectsPerformSelector(m_pChildren, cleanup, CCNode*);
}



3、draw

    // 重写这个方法来绘制你的自身节点
virtual void draw(void);

// 递归访问该节点的子节点并渲染
virtual void visit(void);

draw是给子节点重写的,因为CCNode只是作为一个基类,所以这个方法是空的,但是实体类比如CCSprite或者CCMenu都是重写该方法。
visit是递归访问该节点的子节点并渲染。

void CCNode::visit()
{
// quick return if not visible. children won't be drawn.
// 1.先行处理
if (!m_bVisible)
{
return;
}
kmGLPushMatrix(); // 矩阵压栈

// 处理Grid特效
if (m_pGrid && m_pGrid->isActive())
{
m_pGrid->beforeDraw();
}

// 2.应用转换,在这里进行坐标转换
this->transform();

// 3.递归画图
CCNode* pNode = NULL;
unsigned int i = 0;

if(m_pChildren && m_pChildren->count() > 0)
{
// 存在的子节点排序
sortAllChildren();
// draw children zOrder <0
// 绘制 zOrder <0 的子节点
ccArray *arrayData = m_pChildren->data;
for( ; i num; i++ )
{
pNode = (CCNode*) arrayData->arr[i];

if ( pNode && pNode->m_nZOrder <0 )
{
pNode->visit();
}
else
{
break;
}
}
// self draw
// 绘制自身
this->draw();

// 绘制剩余的子节点
for( ; i num; i++ )
{
pNode = (CCNode*) arrayData->arr[i];
if (pNode)
{
pNode->visit();
}
}
}
else
{
// 没有子节点:直接绘制自身
this->draw();
}

// reset for next frame
// 4.恢复工作
m_uOrderOfArrival = 0;

if (m_pGrid && m_pGrid->isActive())
{
m_pGrid->afterDraw(this);
}

kmGLPopMatrix(); // 矩阵出栈
}

关于节点的就到这里,希望对大家有帮助!


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
author-avatar
mobiledu2502906183
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有