zoukankan      html  css  js  c++  java
  • Cocos2d之Node类详解之节点树(二)

    一、声明

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

    笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析。这篇文章承接上篇《Cocos2d之Node类详解之节点树(一)》

    二、简介

    节点

    一个Node对象。

    节点树

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

    三、源码详解

    Cocos2d之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<std::string> 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(这个属性的介绍在《Cocos2d之Node类详解之节点树(一)》添加子节点部分由介绍)。

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

    #include <iostream>     // std::cout
    #include <algorithm>    // std::sort
    #include <vector>       // std::vector
    
    bool myfunction (int i,int j) { return (i<j); }
    
    struct myclass {
      bool operator() (int i,int j) { return (i<j);}
    } myobject;
    
    int main () {
      int myints[] = {32,71,12,45,26,80,53,33};
      std::vector<int> 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类实现节点树的添加、获取、删除子节点等功能的内容就到此结束咯。

  • 相关阅读:
    lazy懒载入(延迟载入)UITableView
    POJ 3277 City Horizon
    Effective C++ Item 26 尽可能延后变量定义式的出现时间
    2014 百度之星题解1001
    搭建和測试Android JAVA NDK
    Oracle数据库案例整理-Oracle系统执行时故障-内存过少导致分配共享内存失败
    “以房养老”保险方案为啥行不通?
    Mysql上的RAC:Percona XtraDB Cluster负载均衡集群安装部署手冊
    mysql 数据库查询最后两条数据
    00109_反射概述
  • 原文地址:https://www.cnblogs.com/chenshi/p/4088571.html
Copyright © 2011-2022 走看看