博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cocos2d之Node类详解之节点树(二)
阅读量:7216 次
发布时间:2019-06-29

本文共 7562 字,大约阅读时间需要 25 分钟。

一、声明

本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可。

笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析。这篇文章承接上篇《。

二、简介

节点

一个Node对象。

节点树

上篇文章介绍到,Node类有一个成员变量 Vector<Node*> _children,这是一个保存所有子节点的数组,因为Node类采用遍历树的方式获取子节点进行渲染,所以我管这两个东西的结合叫节点树。

三、源码详解

《一文中已经介绍了Node对象如何往节点树中添加子节点,现在介绍从节点树中获取节点和删除节点的实现过程。

获取子节点

相关函数的声明:

/**     * Gets a child from the container with its tag     *     * @param tag   An identifier to find the child node.     *     * @return a Node object whose tag equals to the input parameter     *     * Please use `getChildByName()` instead     */     virtual Node * getChildByTag(int tag) const;    /**     * Gets a child from the container with its name     *     * @param name   An identifier to find the child node.     *     * @return a Node object whose name equals to the input parameter     *     * @since v3.2     */    virtual Node* getChildByName(const std::string& name) const;

相关函数实现:

Node* Node::getChildByTag(int tag) const{    CCASSERT( tag != Node::INVALID_TAG, "Invalid tag");    for (auto& child : _children)    {        if(child && child->_tag == tag)            return child;    }    return nullptr;}Node* Node::getChildByName(const std::string& name) const{    CCASSERT(name.length() != 0, "Invalid name");        std::hash
h; size_t hash = h(name); for (const auto& child : _children) { // Different strings may have the same hash code, but can use it to compare first for speed if(child->_hashOfName == hash && child->_name.compare(name) == 0) return child; } return nullptr;}

从源码可以看出,Node类提供分别以name、tag为关键字查询子节点的方式。每种方式的实现都是对 _children 子节点数组进行遍历匹配。值得注意的是,在 getChildByName 函数中,为了提高字符串匹配的效率,先进行哈希匹配再进行字符串内容对比。

 

删除子节点

相关函数声明。

/**     * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.     *     * @param child     The child node which will be removed.     * @param cleanup   true if all running actions and callbacks on the child node will be cleanup, false otherwise.     */    virtual void removeChild(Node* child, bool cleanup = true);    /**     * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter     *     * @param tag       An interger number that identifies a child node     * @param cleanup   true if all running actions and callbacks on the child node will be cleanup, false otherwise.     *     * Please use `removeChildByName` instead.     */     virtual void removeChildByTag(int tag, bool cleanup = true);    /**     * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter     *     * @param name       A string that identifies a child node     * @param cleanup   true if all running actions and callbacks on the child node will be cleanup, false otherwise.     */    virtual void removeChildByName(const std::string &name, bool cleanup = true);    /**     * Removes all children from the container with a cleanup.     *     * @see `removeAllChildrenWithCleanup(bool)`     */    virtual void removeAllChildren();

删除子节点函数实现。

void Node::removeChild(Node* child, bool cleanup /* = true */){    // explicit nil handling    if (_children.empty())    {        return;    }    ssize_t index = _children.getIndex(child);    if( index != CC_INVALID_INDEX )        this->detachChild( child, index, cleanup );}void Node::removeChildByTag(int tag, bool cleanup/* = true */){    CCASSERT( tag != Node::INVALID_TAG, "Invalid tag");    Node *child = this->getChildByTag(tag);    if (child == nullptr)    {        CCLOG("cocos2d: removeChildByTag(tag = %d): child not found!", tag);    }    else    {        this->removeChild(child, cleanup);    }}void Node::removeChildByName(const std::string &name, bool cleanup){    CCASSERT(name.length() != 0, "Invalid name");        Node *child = this->getChildByName(name);        if (child == nullptr)    {        CCLOG("cocos2d: removeChildByName(name = %s): child not found!", name.c_str());    }    else    {        this->removeChild(child, cleanup);    }}void Node::removeAllChildren(){    this->removeAllChildrenWithCleanup(true);}

从源码中可以看出,addChild函数并没有直接将子节点从 _children 数组中删除,而是获取子节点在 _children 数组中的位置,然后调用 detachChild( child, index, cleanup )函数。detachChild函数的声明如下:

/// Removes a child, call child->onExit(), do cleanup, remove it from children array.void detachChild(Node *child, ssize_t index, bool doCleanup);

该函数的实现如下:

void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup){    // IMPORTANT:    //  -1st do onExit    //  -2nd cleanup    if (_running)    {        child->onExitTransitionDidStart();        child->onExit();    }    #if CC_USE_PHYSICS    child->removeFromPhysicsWorld();#endif    // If you don't do cleanup, the child's actions will not get removed and the    // its scheduledSelectors_ dict will not get released!    if (doCleanup)    {        child->cleanup();    }    // set parent nil at the end    child->setParent(nullptr);    _children.erase(childIndex);}

detachChild 函数其实就是去释放子节点拥有的资源,这种设置是很合理的。

节点树重排序

相关函数声明:

/**     * Reorders a child according to a new z value.     *     * @param child     An already added child node. It MUST be already added.     * @param localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int)     */    virtual void reorderChild(Node * child, int localZOrder);    /**     * Sorts the children array once before drawing, instead of every time when a child is added or reordered.     * This appraoch can improves the performance massively.     * @note Don't call this manually unless a child added needs to be removed in the same frame     */    virtual void sortAllChildren();

函数实现:

void Node::reorderChild(Node *child, int zOrder){    CCASSERT( child != nullptr, "Child must be non-nil");    _reorderChildDirty = true;    child->setOrderOfArrival(s_globalOrderOfArrival++);    child->_localZOrder = zOrder;}void Node::sortAllChildren(){    if( _reorderChildDirty ) {        std::sort( std::begin(_children), std::end(_children), nodeComparisonLess );        _reorderChildDirty = false;    }}

值得注意的是, reorderChild 函数只是改变子节点 _localZOrder 的值,还有将父节点的 _reorderChildDirty 标志位置true。_reorderChildDirty 标志位为true说明子节点的 _localZOrder 发生了改变,因此需要调用 sortAllChildren 函数对所有子节点进行排序。还有一点要注意的是,改变一次子节点的_localZOrder时,s_globalOrderOfArrival属性加1(这个属性的介绍在《添加子节点部分由介绍)。

sortAllChildren函数使用了C++标准库提供的 sort函数对所有子节点进行排序。笔者通过下面这个例子详细为读者解释这个函数的用法。

#include 
// std::cout#include
// std::sort#include
// std::vectorbool myfunction (int i,int j) { return (i
myvector (myints, myints+8); // 32 71 12 45 26 80 53 33 // using default comparison (operator <): std::sort (myvector.begin(), myvector.begin()+4); //(12 32 45 71)26 80 53 33 // using function as comp std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80) // using object as comp std::sort (myvector.begin(), myvector.end(), myobject); //(12 26 32 33 45 53 71 80) return 0;}

了解了 sort 函数的用法之后,我们看Node类是怎么具体使用sort函数的吧。

void Node::sortAllChildren(){    if( _reorderChildDirty ) {        std::sort( std::begin(_children), std::end(_children), nodeComparisonLess );        _reorderChildDirty = false;    }}bool nodeComparisonLess(Node* n1, Node* n2){    return( n1->getLocalZOrder() < n2->getLocalZOrder() ||           ( n1->getLocalZOrder() == n2->getLocalZOrder() && n1->getOrderOfArrival() < n2->getOrderOfArrival() )           );}

 

四、结束

介绍Node类实现节点树的添加、获取、删除子节点等功能的内容就到此结束咯。

转载于:https://www.cnblogs.com/chenshi/p/4088571.html

你可能感兴趣的文章
多迪将企业的Python工程师定位成哪几类?
查看>>
Rom 检测
查看>>
【iOS工具】rvm、Ruby环境和CocoaPods安装使用及相关报错问题解决(2016 12 15 更新)...
查看>>
Weex学习指南
查看>>
TiDB DevCon 2019 报名开启:年度最高规格的 TiDB 技术大会
查看>>
React Native 初体验
查看>>
数据结构与算法 | 线性表 —— 链表
查看>>
Python3 websocket通信
查看>>
使用MarkDown画矩阵
查看>>
JavaScript函数式编程学习
查看>>
ESXi6.7安装流程和bug处理
查看>>
Alibaba Cluster Data 开放下载:270GB 数据揭秘你不知道的阿里巴巴数据中心
查看>>
巧用这19条MySQL优化,效率至少提高3倍
查看>>
【译】Swift算法俱乐部-查找最大/最小值
查看>>
跟着老司机玩转Node自定义命令行
查看>>
react-redux的Provider和connect
查看>>
杂七杂八的前端基础01——函数作用域
查看>>
new操作符具体干了啥
查看>>
iOS响应链
查看>>
『中级篇』docker容器安装wordpress(37)
查看>>