• 从零开始のcocos2dx生活(二)Node


    节点 Node

    前言

    建议看完后面章节的知识后,多多回顾节点的代码,有助于加深理解。

    节点是场景图中的基本元素。
    最常见的节点对象有:场景、图层、精灵、菜单、标签、按钮等。

    节点的主要特征是:
    -它们可以包含其他节点对象(’ addChild ‘、’ removeChild ‘等)
    -他们可以安排定期回调(’ schedule ', ’ unschedule ‘等)
    -他们可以执行动作(’ runAction ', ’ stopAction '等)

    子类化一个节点通常意味着(一个/所有):
    -覆盖init来初始化资源和调度回调
    -创建回调函数来处理时间的推移
    -覆盖“绘制”以呈现节点

    节点的属性:
    -位置(默认:x=0, y=0)
    -缩放(默认值:x=1, y=1)
    -旋转(角度,顺时针方向)(默认为0)
    -锚点(默认:x=0, y=0)
    -contentSize(默认:宽度=0,高度=0)
    -可见性(默认为真)

    变量初始化

    static const int INVALID_TAG = -1;  //设置标签初始值
    
    enum {
            FLAGS_TRANSFORM_DIRTY = (1 << 0),  // 1
            FLAGS_CONTENT_SIZE_DIRTY = (1 << 1),  // 2
            FLAGS_RENDER_AS_3D = (1 << 3),  // 8
    
            FLAGS_DIRTY_MASK = (FLAGS_TRANSFORM_DIRTY | FLAGS_CONTENT_SIZE_DIRTY), // 或运算
        };
    
    static Node * create(); //初始化节点,并返回一个加入到自动内存释放池中的节点
    
    static int getAttachedNodeCount(); //获取附加在节点上的节点个数,(统计父节点上的子节点个数)
    

    创建一个节点对象

    Node * Node::create()
    {
        Node * ret = new (std::nothrow) Node(); //创建一个名为ret的Node类型的对象
        if (ret && ret->init())									//判断ret是否创建成功并初始化对象ret
        {
            ret->autorelease();									//将对象ret加入内存自动回收池
        }
        else
        {
            CC_SAFE_DELETE(ret);								//如果创建对象失败,删除这个对象
        }
        return ret;															//返回创建成功的节点对象ret
    }
    

    获取节点依赖的计数器

    int Node::getAttachedNodeCount()
    {
        return __attachedNodeCount;  //返回被依赖的计数
    }
    

    获取节点的描述(获取节点的Tag)

    std::string Node::getDescription() const
    {
        return StringUtils::format("<Node | Tag = %d", _tag); //返回节点的Tag 
      													// _tag(Node::INVALID_TAG) 实际返回的就是INVALID_TAG
    }
    

    初始化器结束


    节点的局部层顺序值(LocalZOrder)

    LocalZOrder是用于对节点相对于其兄弟节点进行排序的“键”。

    节点的父节点将根据LocalZOrder值对所有子节点排序。
    如果两个节点具有相同的LocalZOrder,那么首先添加到子数组中的节点将位于数组中另一个节点的前面。

    此外,场景图使用中序遍历,先遍历左子树,然后根节点,最后是右子树。
    LocalZOrder值< 0的节点是左子树
    而LocalZOrder >=0的节点是右子树。

    设置节点的LocalZOrder的值

    void Node::setLocalZOrder(std::int32_t z)
    {
        if (getLocalZOrder() == z) //如果设置前后值相等,不做操作直接返回
            return;
        
        _setLocalZOrder(z); //否则初始化_setLocalZOrder变量
        if (_parent) //如果存在父节点
        {
            _parent->reorderChild(this, z);  //对父节点的子节点顺序重新排序
        }
        _eventDispatcher->setDirtyForNode(this); //将这个节点加入到事件调度中(加入节点监听器中,并将子节点也做相同操作)
    }
    //兼容旧版本
    CC_DEPRECATED_ATTRIBUTE virtual void setZOrder(std::int32_t localZOrder) { setLocalZOrder(localZOrder); }
    

    获取节点的LocalZOrder的值

    virtual std::int32_t getLocalZOrder() const { return _localZOrder; }
    //兼容旧版本
    CC_DEPRECATED_ATTRIBUTE virtual std::int32_t getZOrder() const { return getLocalZOrder(); }
    

    相同等级子节点具有相同ZOrder时设置绘制顺序

    void updateOrderOfArrival();
    

    节点的全局层顺序值(GlobalZOrder)

    设置节点的GlobalZOrder值

    定义节点渲染的顺序。
    globalZOrder较低的节点首先渲染。

    如果两个或多个节点具有相同的globalZOrder,则不保证渲染顺序。
    当节点的globalZOrder == 0时。在这种情况下,使用场景图顺序。

    默认情况下,所有节点的globalZOrder为0。这意味着默认情况下,一定会使用场景图顺序用于呈现节点。

    当需要以 与场景图顺序不同的顺序 呈现节点时,GlobalZOrder非常有用。

    void Node::setGlobalZOrder(float globalZOrder)
    {
        if (_globalZOrder != globalZOrder) //如果设置前后不相等
        {
            _globalZOrder = globalZOrder;  //给_globalZOrder赋值
            _eventDispatcher->setDirtyForNode(this);  //将这个节点加入到事件调度中(加入节点监听器中,并将子节点也做相同操作)
        }
    }
    

    获取节点的GlobalZOrder值

    virtual float getGlobalZOrder() const { return _globalZOrder; }
    

    节点的缩放(scale)

    设置缩放大小

    //设置一个比例因子,用于乘以节点及其 子节点 的宽度。
    void Node::setScaleX(float scaleX)
    {
        if (_scaleX == scaleX)
            return;
        
        _scaleX = scaleX;
        _transformUpdated = _transformDirty = _inverseDirty = true; 
    }
    //设置一个比例因子,用于乘以节点及其 子节点 的高度。
    void Node::setScaleY(float scaleY)
    {
        if (_scaleY == scaleY)
            return;
        
        _scaleY = scaleY;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    //设置一个比例因子,用于乘以节点及其 子节点 的高度。
    void Node::setScaleZ(float scaleZ)
    {
        if (_scaleZ == scaleZ)
            return;
        
        _scaleZ = scaleZ;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    //设置一个比例因子,用于乘以节点及其 子节点 的宽度、高度、深度。
    void Node::setScale(float scale)
    {
        if (_scaleX == scale && _scaleY == scale && _scaleZ == scale)
            return;
        
        _scaleX = _scaleY = _scaleZ = scale;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    //它是一个比例因子,乘以节点及其子节点的宽度和高度。
    void Node::setScale(float scaleX,float scaleY)
    {
        if (_scaleX == scaleX && _scaleY == scaleY)
            return;
        
        _scaleX = scaleX;
        _scaleY = scaleY;
        _transformUpdated = _transformDirty = _inverseDirty = true; //标记脏数据
    }
    

    获取缩放大小

    float Node::getScaleX() const
    {
        return _scaleX;
    }
    float Node::getScaleY() const
    {
        return _scaleY;
    }
    float Node::getScaleZ() const
    {
        return _scaleZ;
    }
    
    //获取节点的缩放因子,当X和Y具有相同的缩放因子时
    float Node::getScale(void) const
    {
        CCASSERT( _scaleX == _scaleY, "CCNode#scale. ScaleX != ScaleY. Don't know which one to return"); // ?
        return _scaleX;
    }
    

    节点位置(pisition)

    设置节点在父坐标系中的位置

    //设置x,y轴的位置
    void Node::setPosition(const Vec2& position)
    {
        setPosition(position.x, position.y);  //(x, y)
    }
    //使用0-1设置x,y轴的位置
    void Node::setPositionNormalized(const Vec2& position)
    {
        if (_normalizedPosition.equals(position)) //修改前后相等,不做修改直接返回
            return;
    
        _normalizedPosition = position;  //给_normalizedPosition赋值
        _usingNormalizedPosition = true;  //标记 使用规范化位置
        _normalizedPositionDirty = true;	//标记 使用规范化位置的Dirty  // ? 标记脏数据
        _transformUpdated = _transformDirty = _inverseDirty = true;  // ? 标记脏数据重新计算渲染坐标矩阵  //在上面的修改之后,渲染用的坐标系矩阵未经计算,造成值的不对应,在当调用getNodeToParentTransform() 重新计算渲染用的坐标系矩阵之后会使用修改之后的值渲染,渲染之后的值对应了,不存在脏数据了,会将_transformDirty再设为false
    }
    //直接使用x、y设置位置
    virtual void setPosition(float x, float y);
    virtual void getPosition(float* x, float* y) const;
    virtual void  setPositionX(float x);
    virtual float getPositionX(void) const;
    virtual void  setPositionY(float y);
    virtual float getPositionY(void) const;
    //设置节点的三维属性
    void Node::setPosition3D(const Vec3& position)
    {
        setPositionZ(position.z);
        setPosition(position.x, position.y);
    }
    //设置三维的z坐标
    virtual void setPositionZ(float positionZ)
    {
        if (_positionZ == positionZ)
            return;
        
        _transformUpdated = _transformDirty = _inverseDirty = true;
    
        _positionZ = positionZ;
    }
    

    获取节点在父坐标系中的位置

    //_position
    const Vec2& Node::getPosition() const
    {
        return _position;
    }
    //获取节点的规范化位置 0-1
    const Vec2& Node::getPositionNormalized() const
    {
        return _normalizedPosition;
    }
    //获取节点的三维属性
    Vec3 Node::getPosition3D() const
    {
        return Vec3(_position.x, _position.y, _positionZ);
    }
    //获取节点的z坐标
    float Node::getPositionZ() const
    {
        return _positionZ;
    }
    

    兼容旧版本

    virtual void setNormalizedPosition(const Vec2 &position) { setPositionNormalized(position); }
    
    virtual const Vec2& getNormalizedPosition() const { return getPositionNormalized(); }
    
    CC_DEPRECATED_ATTRIBUTE virtual void setVertexZ(float vertexZ) { setPositionZ(vertexZ); }
    
    CC_DEPRECATED_ATTRIBUTE virtual float getVertexZ() const { return getPositionZ(); }
    

    节点倾斜(Skew)

    设置倾斜角

    void Node::setSkewX(float skewX)
    {
        if (_skewX == skewX)
            return;
        
        _skewX = skewX;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    void Node::setSkewY(float skewY)
    {
        if (_skewY == skewY)
            return;
        
        _skewY = skewY;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    

    获取倾斜角

    float Node::getSkewX() const
    {
        return _skewX;
    }
    float Node::getSkewY() const
    {
        return _skewY;
    }
    

    节点锚点(AnchorPoint)

    锚点是所有转换和定位操作发生的点。
    它就像节点上的一个大头针,它被“连接”到它的父节点上。
    锚点是标准化的,比如百分比。(0,0)表示左下角,(1,1)表示右上角。
    但是也可以使用大于(1,1)和小于(0,0)的值。
    默认锚点是(0,0),因此它从节点的左下角开始。

    设置锚点

    //以百分比设置锚点
    void Node::setAnchorPoint(const Vec2& point)
    {
        if (! point.equals(_anchorPoint))
        {
            _anchorPoint = point;
            _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
            _transformUpdated = _transformDirty = _inverseDirty = true;
        }
    }
    
    

    获取锚点

    //以百分比为单位返回锚点
    const Vec2& Node::getAnchorPoint() const
    {
        return _anchorPoint;
    }
    //以绝对像素点为单位返回锚点
    const Vec2& Node::getAnchorPointInPoints() const
    {
        return _anchorPointInPoints;
    }
    

    设置节点的锚点是否为(0,0)

    //默认值为false,而在图层和场景中为true
    void Node::setIgnoreAnchorPointForPosition(bool newValue)
    {
        if (newValue != _ignoreAnchorPointForPosition) 
        {
            _ignoreAnchorPointForPosition = newValue;
            _transformUpdated = _transformDirty = _inverseDirty = true;
        }
    }
    //兼容旧版本
    CC_DEPRECATED_ATTRIBUTE virtual void ignoreAnchorPointForPosition(bool ignore) { setIgnoreAnchorPointForPosition(ignore); }
    

    获取节点的锚点是否是(0,0)

    bool Node::isIgnoreAnchorPointForPosition() const
    {
        return _ignoreAnchorPointForPosition;
    }
    

    节点的原始大小(ContentSize)

    设置节点的原始大小

    void Node::setContentSize(const Size & size)
    {
        if (! size.equals(_contentSize))
        {
            _contentSize = size;
    
            _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
            _transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
        }
    }
    

    获取节点的原始大小

    const Size& Node::getContentSize() const
    {
        return _contentSize;
    }
    

    节点可见性(Visible)

    设置节点可见性

    //默认为可见的
    void Node::setVisible(bool visible)
    {
        if(visible != _visible)
        {
            _visible = visible;
            if(_visible)
                _transformUpdated = _transformDirty = _inverseDirty = true;
        }
    }
    

    确定节点是否可见

    //可见即返回true,不可见返回false
    bool Node::isVisible() const
    {
        return _visible;
    }
    

    ##节点的旋转角度(Rotation)

    设置节点的旋转角度

    //设置节点的旋转角度,这里是直接旋转,和下面的RotationSkew不一样
    void Node::setRotation(float rotation)
    {
        if (_rotationZ_X == rotation)
            return;
        
        _rotationZ_X = _rotationZ_Y = rotation;
        _transformUpdated = _transformDirty = _inverseDirty = true;
        
        updateRotationQuat();    //?
    }
    

    获取节点的旋转角度

    float Node::getRotation() const
    {
        CCASSERT(_rotationZ_X == _rotationZ_Y, "CCNode#rotation. RotationX != RotationY. Don't know which one to return");
        return _rotationZ_X;
    }
    

    节点的3d旋转(Rotation3D)

    设置节点的3d旋转

    void Node::setRotation3D(const Vec3& rotation)
    {
        if (_rotationX == rotation.x &&
            _rotationY == rotation.y &&
            _rotationZ_X == rotation.z)
            return;
        
        _transformUpdated = _transformDirty = _inverseDirty = true;
    
        _rotationX = rotation.x;
        _rotationY = rotation.y;
    
        // rotation Z is decomposed in 2 to simulate Skew for Flash animations
      	//模拟flash动画的倾斜
        _rotationZ_Y = _rotationZ_X = rotation.z;
        
        updateRotationQuat();
    }
    
    

    获取节点的3d旋转

    Vec3 Node::getRotation3D() const
    {
        // rotation Z is decomposed in 2 to simulate Skew for Flash animations
        CCASSERT(_rotationZ_X == _rotationZ_Y, "_rotationZ_X != _rotationZ_Y");
    
        return Vec3(_rotationX,_rotationY,_rotationZ_X);
    }
    

    节点的四元数(Quat)

    对于四元数的学习可以看我的另外一篇博客
    cocos2dx 四元数

    设置节点的四元数

    //按四元数设置旋转
    void Node::setRotationQuat(const Quaternion& quat)
    {
        _rotationQuat = quat;
        updateRotation3D();
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    

    获取节点的四元数

    Quaternion Node::getRotationQuat() const
    {
        return _rotationQuat;
    }
    

    节点的旋转倾斜(RotationSkew)

    设置旋转倾斜

    //设置水平旋转倾斜,模拟Flash的歪斜功能
    void Node::setRotationSkewX(float rotationX)
    {
        if (_rotationZ_X == rotationX)
            return;
        
        _rotationZ_X = rotationX;
        _transformUpdated = _transformDirty = _inverseDirty = true;
        
        updateRotationQuat();
    }
    //设置垂直旋转倾斜的角度,模拟Flash的歪斜功能
    void Node::setRotationSkewY(float rotationY)
    {
        if (_rotationZ_Y == rotationY)
            return;
        
        _rotationZ_Y = rotationY;
        _transformUpdated = _transformDirty = _inverseDirty = true;
        
        updateRotationQuat();
    }
    
    

    获取旋转倾斜

    float Node::getRotationSkewX() const
    {
        return _rotationZ_X;
    }
    float getRotationSkewY() const
    {
        return _rotationZ_Y;
    }
    

    兼容旧版本

    CC_DEPRECATED_ATTRIBUTE virtual void setRotationX(float rotationX) { return setRotationSkewX(rotationX); }
    
    CC_DEPRECATED_ATTRIBUTE virtual void setRotationY(float rotationY) { return setRotationSkewY(rotationY); }
    
    C_DEPRECATED_ATTRIBUTE virtual float getRotationY() const { return getRotationSkewY(); }
    

    子节点(Child)

    添加子节点

    void Node::addChild(Node *child)
    {
        CCASSERT( child != nullptr, "Argument must be non-nil"); //判断节点是否是空节点
        this->addChild(child, child->getLocalZOrder(), child->_name);//
    }
    //添加子节点同时设置localZOrder属性
    void Node::addChild(Node *child, int zOrder)
    {
        CCASSERT( child != nullptr, "Argument must be non-nil");
        this->addChild(child, zOrder, child->_name);
    }
    //添加子节点同时设置localZOrder和tag属性
    void Node::addChild(Node *child, int localZOrder, int tag)
    {    
        CCASSERT( child != nullptr, "Argument must be non-nil");
        CCASSERT( child->_parent == nullptr, "child already added. It can't be added again");
    
        addChildHelper(child, localZOrder, tag, "", true);
    }
    //添加子节点同时设置localZOrder和name属性
    void Node::addChild(Node* child, int localZOrder, const std::string &name)
    {
        CCASSERT(child != nullptr, "Argument must be non-nil");
        CCASSERT(child->_parent == nullptr, "child already added. It can't be added again");
        
        addChildHelper(child, localZOrder, INVALID_TAG, name, false);
    }
    //帮助添加子节点
    void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
    {
        auto assertNotSelfChild
            ( [ this, child ]() -> bool
              {
                  for ( Node* parent( getParent() ); parent != nullptr;
                        parent = parent->getParent() )
                      if ( parent == child )
                          return false;
                  
                  return true;
              } );
        (void)assertNotSelfChild;
        
        CCASSERT( assertNotSelfChild(),
                  "A node cannot be the child of his own children" );
        
        if (_children.empty())
        {
            this->childrenAlloc();
        }
        
        this->insertChild(child, localZOrder);
        
        if (setTag)
            child->setTag(tag);
        else
            child->setName(name);
        
        child->setParent(this);
    
        child->updateOrderOfArrival();
    
        if( _running )
        {
            child->onEnter(); //调用onEnter 增加依附计数 完成过渡
            // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
            if (_isTransitionFinished)
            {
                child->onEnterTransitionDidFinish(); //结束过渡
            }
        }
        
        if (_cascadeColorEnabled)
        {
            updateCascadeColor();
        }
        
        if (_cascadeOpacityEnabled)
        {
            updateCascadeOpacity();
        }
    }
    

    获取子节点

    //遍历子节点,通过tag找到所需的节点
    Node* Node::getChildByTag(int tag) const
    {
        CCASSERT(tag != Node::INVALID_TAG, "Invalid tag");
    
        for (const auto child : _children)
        {
            if(child && child->_tag == tag)
                return child;
        }
        return nullptr;
    }
    //返回具有给定名称的节点,该节点可以转换为类型T
    template <typename T>
        T getChildByTag(int tag) const { return static_cast<T>(getChildByTag(tag)); }
    //通过name找到所需的节点
    Node* Node::getChildByName(const std::string& name) const
    {
        CCASSERT(!name.empty(), "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;
    }
    //返回具有给定名称的节点,该节点可以转换为类型T
     template <typename T>
        T getChildByName(const std::string& name) const { return static_cast<T>(getChildByName(name)); }
    //查找节点
    //  '//':只能放在搜索字符串的开头。这表明它将递归搜索。
    //  '..':搜索应该向上移动到节点的父节点。只能放在字符串的末尾。
    //  '/' :当搜索字符串放置在除开始位置以外的任何位置时,这表示搜索应该移动到节点的子节点。
    //例如:
    //enumerateChildren("//MyName", ...)  // 递归查找子节点
    //enumerateChildren("[[:alnum:]]+", ...) // 匹配子节点的每个节点
    //enumerateChildren("A[[:digit:]]", ...) // 查找A0-A9
    //enumerateChildren("Abby/Normal", ...)  // 搜索并返回名为Normal父节点为Abby的任何节点
    //enumerateChildren("//Abby/Normal", ...) // 递归搜索并返回名为Normal父节点为Abby的任何节点
    void Node::enumerateChildren(const std::string &name, std::function<bool (Node *)> callback) const
    {
        CCASSERT(!name.empty(), "Invalid name");
        CCASSERT(callback != nullptr, "Invalid callback function");
        
        size_t length = name.length();
        
        size_t subStrStartPos = 0;  // sub string start index
        size_t subStrlength = length; // sub string length
        
        // Starts with '//'?
        bool searchRecursively = false;
        if (length > 2 && name[0] == '/' && name[1] == '/')
        {
            searchRecursively = true;
            subStrStartPos = 2;
            subStrlength -= 2;
        }
        
        // End with '/..'?
        bool searchFromParent = false;
        if (length > 3 &&
            name[length-3] == '/' &&
            name[length-2] == '.' &&
            name[length-1] == '.')
        {
            searchFromParent = true;
            subStrlength -= 3;
        }
        
        // Remove '//', '/..' if exist
        std::string newName = name.substr(subStrStartPos, subStrlength);
    
        if (searchFromParent)
        {
            newName.insert(0, "[[:alnum:]]+/");
        }
        
        
        if (searchRecursively)
        {
            // name is '//xxx'
            doEnumerateRecursive(this, newName, callback);
        }
        else
        {
            // name is xxx
            doEnumerate(newName, callback);
        }
    }
    //返回节点的子节点数组
    virtual Vector<Node*>& getChildren() { return _children; }
    virtual const Vector<Node*>& getChildren() const { return _children; }
    //获取子节点的个数
    ssize_t Node::getChildrenCount() const
    {
        return _children.size();
    }
    
    

    添加父节点

    void Node::setParent(Node * parent)
    {
        _parent = parent;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    

    获取父节点

    virtual Node* getParent() { return _parent; }
    virtual const Node* getParent() const { return _parent; }
    

    移除(remove)

    //将这个节点从父节点中移除,如果没有父节点则不操作
    void Node::removeFromParent()
    {
        this->removeFromParentAndCleanup(true);
    }
    void Node::removeFromParentAndCleanup(bool cleanup)
    {
        if (_parent != nullptr)
        {
            _parent->removeChild(this,cleanup);
        } 
    }
    void ParallaxNode::removeChild(Node* child, bool cleanup)
    {
        for( int i=0;i < _parallaxArray->num;i++)
        {
            PointObject *point = (PointObject*)_parallaxArray->arr[i];
            if (point->getChild() == child)
            {
                ccArrayRemoveObjectAtIndex(_parallaxArray, i, true);
                break;
            }
        }
        Node::removeChild(child, cleanup);
    }
    //移除子节点
    //如果所有正在运行的动作和回调函数都在子节点上,cleanup为true,否则为false
    void Node::removeChild(Node* child, bool cleanup /* = true */)
    {
        if (_children.empty())
        {
            return;
        }
    
        ssize_t index = _children.getIndex(child);
        if( index != CC_INVALID_INDEX )
            this->detachChild( child, index, cleanup );
    }
    //通过tag移除子节点
    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);
        }
    }
    //通过Name移除子节点
    void Node::removeChildByName(const std::string &name, bool cleanup)
    {
        CCASSERT(!name.empty(), "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);
    }
    //删除所有子节点,并根据cleanup参数对所有正在运行的操作执行清理(cleanup)
    void Node::removeAllChildrenWithCleanup(bool cleanup)
    {
        // not using detachChild improves speed here
        for (const auto& child : _children)
        {
            // IMPORTANT:
            //  -1st do onExit
            //  -2nd cleanup
            if(_running)
            {
                child->onExitTransitionDidStart();
                child->onExit();
            }
    
            if (cleanup)
            {
                child->cleanup();
            }
    #if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
            auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
            if (sEngine)
            {
                sEngine->releaseScriptObject(this, child);
            }
    #endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
            // set parent nil at the end
            child->setParent(nullptr);
        }
        
        _children.clear();
    }
    

    排序(Sort) !

    void Node::reorderChild(Node *child, int zOrder)
    {
        CCASSERT( child != nullptr, "Child must be non-nil");
        _reorderChildDirty = true;
        child->updateOrderOfArrival();
        child->_setLocalZOrder(zOrder);
    }
    //对所有的子节点排序,不需要手动调用,只会在绘制之前执行一次,以提高性能
    void Node::sortAllChildren()
    {
        if (_reorderChildDirty)
        {
            sortNodes(_children);
            _reorderChildDirty = false;
            _eventDispatcher->setDirtyForNode(this);
        }
    }
    //    
    template<typename _T> inline
        static void sortNodes(cocos2d::Vector<_T*>& nodes)
        {
            static_assert(std::is_base_of<Node, _T>::value, "Node::sortNodes: Only accept derived of Node!");
    #if CC_64BITS
            std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
                return (n1->_localZOrder$Arrival < n2->_localZOrder$Arrival);
            });
    #else
            std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
                return (n1->_localZOrder == n2->_localZOrder && n1->_orderOfArrival < n2->_orderOfArrival) || n1->_localZOrder < n2->_localZOrder;
            });
    #endif
        }
    

    数据和用户标签

    设置

    UserData基本不用,一般使用UserDefault来记录数据,生成类似xml的文件。

        virtual void setTag(int tag);
        virtual void setName(const std::string& name);
        virtual void setUserData(void *userData);//可以看到这里的UserData是void类型的,也就是说可以记录任意数据
        virtual void setUserObject(Ref *userObject);  
    

    获取

        virtual int getTag() const;
        virtual const std::string& getName() const;
        virtual void* getUserData() { return _userData; }
        virtual const void* getUserData() const { return _userData; }
        virtual Ref* getUserObject() { return _userObject; }
        virtual const Ref* getUserObject() const { return _userObject; }
    
        virtual int getTag() const;
        virtual const std::string& getName() const;
        virtual void* getUserData() { return _userData; }
        virtual const void* getUserData() const { return _userData; }
        virtual Ref* getUserObject() { return _userObject; }
        virtual const Ref* getUserObject() const { return _userObject; }
    

    OPenGL

    获取OPenGL程序的状态

    //每个node私有的状态
    GLProgram * Node::getGLProgram() const
    {
        return _glProgramState ? _glProgramState->getGLProgram() : nullptr;
    }
    //兼容旧版本
    CC_DEPRECATED_ATTRIBUTE GLProgram* getShaderProgram() const { return getGLProgram(); }
    //通用的状态
    GLProgramState* Node::getGLProgramState() const
    {
        return _glProgramState;
    }
    
    

    设置OPenGL程序的状态

    //设置私有的程序状态
    void Node::setGLProgram(GLProgram* glProgram)
    {
        if (_glProgramState == nullptr || (_glProgramState && _glProgramState->getGLProgram() != glProgram))
        {
            CC_SAFE_RELEASE(_glProgramState);
            _glProgramState = GLProgramState::getOrCreateWithGLProgram(glProgram);
            _glProgramState->retain();
    
            _glProgramState->setNodeBinding(this);
        }
    }
    //兼容旧版本
    CC_DEPRECATED_ATTRIBUTE void setShaderProgram(GLProgram *glprogram) { setGLProgram(glprogram); }
    //设置通用的程序状态
    void Node::setGLProgramState(cocos2d::GLProgramState* glProgramState)
    {
        if (glProgramState != _glProgramState)
        {
            CC_SAFE_RELEASE(_glProgramState);
            _glProgramState = glProgramState;
            CC_SAFE_RETAIN(_glProgramState);
    
            if (_glProgramState)
                _glProgramState->setNodeBinding(this);
        }
    }
    

    isRunning()

    //返回节点是否正在运行
    //如果节点正在运行,它将接受onEnter()、onExit()、update()等事件回调
    bool Node::isRunning() const
    {
        return _running;
    }
    

    onEnter()

    //过渡效果开始之前调用,预处理一些动作
    void Node::onEnter()
    {
        if (!_running) //没有运行说明还未依附,之后依附,给依附计数器+1
        {
            ++__attachedNodeCount;
        }
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeJavascript)
        {
            if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnter))
                return;
        }
    #endif
        
        if (_onEnterCallback)
            _onEnterCallback(); //调用自己的回调函数
    
        if (_componentContainer && !_componentContainer->isEmpty())
        {
            _componentContainer->onEnter();
        }
        
        _isTransitionFinished = false;
        
        for( const auto &child: _children) //子节点也会进入场景
            child->onEnter();
        
        this->resume();
        
        _running = true; //已经依附
        
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeLua)
        {
            ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnter);
        }
    #endif
    }
    //完全进入场景时调用,添加背景乐等动作
    void Node::onEnterTransitionDidFinish()
    {
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeJavascript)
        {
            if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnterTransitionDidFinish))
                return;
        }
    #endif
        
        if (_onEnterTransitionDidFinishCallback)
            _onEnterTransitionDidFinishCallback(); //调用结束过渡的回调函数
    
        _isTransitionFinished = true;
        for( const auto &child: _children) //子节点同步
            child->onEnterTransitionDidFinish();
        
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeLua)
        {
            ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnterTransitionDidFinish);
        }
    #endif
    }
    

    onExit()

    /* if(_running)
            {
                child->onExitTransitionDidStart();
                child->onExit();
            }
    */ //belong to void Node::removeAllChildren()
    
    //在退出场景之前调用
    void Node::onExitTransitionDidStart()
    {
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeJavascript)
        {
            if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExitTransitionDidStart))
                return;
        }
    #endif
        
        if (_onExitTransitionDidStartCallback)
            _onExitTransitionDidStartCallback(); //调用回调函数
        
        for( const auto &child: _children) //子节点同步
            child->onExitTransitionDidStart();
        
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeLua)
        {
            ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExitTransitionDidStart);
        }
    #endif
    }
    
    
    //退出场景时调用
    void Node::onExit()
    {
        if (_running)
        {
            --__attachedNodeCount; //依附计数器-1
        }
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeJavascript)
        {
            if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExit))
                return;
        }
    #endif
        
        if (_onExitCallback)
            _onExitCallback(); //调用自己的回调函数
        
        if (_componentContainer && !_componentContainer->isEmpty())
        {
            _componentContainer->onExit();
        }
        
        this->pause(); //暂停事务
        
        _running = false; //不在运行中
        
        for( const auto &child: _children) //子节点也会被退出
            child->onExit(); 
        
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeLua)
        {
            ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExit);
        }
    #endif
    }
    

    cleanup()

    //停止所有的操作和调度器
    void Node::cleanup()
    {
    #if CC_ENABLE_SCRIPT_BINDING
        if (_scriptType == kScriptTypeJavascript)
        {
            if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnCleanup))
                return;
        }
        else if (_scriptType == kScriptTypeLua)
        {
            ScriptEngineManager::sendNodeEventToLua(this, kNodeOnCleanup);
        }
    #endif // #if CC_ENABLE_SCRIPT_BINDING
        
        // actions 
        this->stopAllActions();  
        // timers
        this->unscheduleAllCallbacks();  
    
        
        for( const auto &child: _children) //子节点同步
            child->cleanup();
    }
    

    补充:
    1、GlobalZorder、LocalZorder和orderOfArrival的区别
    //渲染的优先级是Globalzorder > LocalZorder > orderOfArrival,类似于权重
    值越大的越迟渲染,因为节点是使用二叉树的中序遍历来渲染的。
    会将所有Globalzorder < 0,Globalzorder > 0,Globalzorder > 0,的节点都放在数组中。
    localZorder区别节点本身与兄弟节点的渲染顺序,当两个或多个节点的localZorder值相等时会比较orderOfArrival的值。
    orderOfArrival的值不需要人为的特别设置,在给节点添加子节点时会自动的自增, orderOfArrival的值默认为0。

    2、setIgnoreAnchorPointForPosition();//一般Node的锚点默认为(0.5, 0.5),而Layer的锚点则在左下角(0,0),这个函数用来给Layer使用,layer的_ignoreAnchorPointForPosition值默认为true,忽略了锚点设置,如果自己设置了锚点也不会生效getChildren()

    3、getChildren()有const修饰的问题 如果节点被const修饰,则只能调用被const修饰的getChildren函数。

    4、_componentContainer的问题 相当于级联的操作

  • 相关阅读:
    我的收藏:第一章:汇总
    实战:第二章:关于EZDML工具使用踩的坑
    Docker:第二章:部署项目,对镜像,容器的操作
    电商网站项目:第一章:进度
    多线程:第一章:我(线程)这一生
    性能优化:第一章:我们应该怎么优化
    关于EZDML数据库表结构制作设计工具使用踩的坑
    集群与分布的区别
    strip_tags--php
    关于html的一些杂技
  • 原文地址:https://www.cnblogs.com/sakuraneo/p/11992060.html
Copyright © 2020-2023  润新知