Cocos2d之Node類詳解之節點樹(一)

1、聲明

筆者分析的是用C++語言實現、版本號爲cocos2d-x-3.3rc0的cocos2d框架的源代碼。本文爲筆者原創,容許讀者分享和轉載,只要讀者註明文章來源便可。node

2、簡介

Node對象時場景圖的基本元素,而且場景圖的基本元素必須是Node對象和Node的子類對象。常見的Node類的子類有:Scene、Layer、Sprite、Menu和Label類。數組

Node類主要實現幾個特性:

  • Node對象的 addChild(Node *child)、getChildByTag(int tag)、removeChild(Node *child, bool cleanup=true) 可以使其持有別的Node對象做爲其子節點。
  • Node對象的調度器可以定時的調用毀掉函數。
  • Node對象可以執行動做(動做由Action對象表示)。

Node子類通常實現下面幾點:

  • 重寫Node類的init函數,使子類可以初始化資源和回調函數。
  • 爲Node子類編寫回調函數,並交由調度器定時調用。
  • 重寫draw函數來渲染Node子類。

Node類有下面幾個經常使用屬性:

  • position(位置)。此屬性表示Node對象的中心點在座標系中渲染的位置,默認初始化成(x = 0, y = 0)。
  • anchor point(錨點)。默認爲(x = 0, y = 0),可是Node的子類的初始值可能會有差別。
  • scale(縮放)。默認寬和高的縮放比例都爲1.
  • rotation(旋轉)。此屬性表示順時針旋轉的角度,默認是0度。
  • contentSize(內容大小)。默認長和寬都爲0.
  • visible(可見性)。默認爲true。

這些屬性會在後續的源碼分析中作具體介紹。框架

3、源碼詳解

Node比較龐大,筆者打算在多篇博客中分別詳細介紹Node節點的不一樣模塊。前面說到Node對象可以持有其餘Node對象做爲其子節點,也就是說一個Node對象其實可以擴展出一個節點樹。因此筆者先介紹節點樹模塊。ide

節點樹實現

添加子節點

添加子節點的過程須要到下面的屬性。函數

int _localZOrder;               ///< Local order (relative to its siblings) used to sort the node
float _globalZOrder;            ///< Global order used to sort the node
Vector<Node*> _children;        ///< array of children nodes
Node *_parent;                  ///< weak reference to parent node
int _tag;                         ///< a tag. Can be any number you assigned just to identify this node
std::string _name;               ///<a string label, an user defined string to identify this node
int _orderOfArrival;            ///< used to preserve sequence while sorting children with the same localZOrder
bool _running;                  ///< is running

下面看此addChild函數的聲明。源碼分析

/**
     * Adds a child to the container with z order and tag
     *
     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
     *
     * @param child     A child node
     * @param zOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`
     * @param tag       An integer to identify the node easily. Please refer to `setTag(int)`
     * 
     * Please use `addChild(Node* child, int localZOrder, const std::string &name)` instead.
     */
     virtual void addChild(Node* child, int localZOrder, int tag);
    /**
     * Adds a child to the container with z order and tag
     *
     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
     *
     * @param child     A child node
     * @param zOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`
     * @param name      A string to identify the node easily. Please refer to `setName(int)`
     *
     */
    virtual void addChild(Node* child, int localZOrder, const std::string &name);

LocalZOrder參數決定了子節點被添加到節點樹的位置,子節點在節點樹中的位置決定了節點顯示的順序。關於節點樹的遍歷會在後續的博客中介紹,讀者如今只須要知道LocalZOrder取值從負軸到正軸,顯示順序遞減。this

函數聲明還提到,若是當前父節點處於running狀態,那麼被添加的子節點會被馬上調用onEnter和onEnterTransitionDidFinish函數。下面看此函數的具體實現。spa

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);
}

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 addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)。下面看該函數的實現。code

void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
{
    if (_children.empty())
    {
        this->childrenAlloc();
    }
    
    this->insertChild(child, localZOrder);
    
    if (setTag)
        child->setTag(tag);
    else
        child->setName(name);
    
    child->setParent(this);
    
    /* 筆者注
     * 設置節點到達順序,若是節點樹中不一樣節點有相同的LocalZOrder時,
     * 到達順序小的節點先畫
     */
    child->setOrderOfArrival(s_globalOrderOfArrival++);
    
    /* 筆者注
     * 若是使用了物理引擎,須要爲節點添加物理世界的性質
     */
#if CC_USE_PHYSICS
    // Recursive add children with which have physics body.
    Scene* scene = this->getScene();
    if (scene != nullptr && scene->getPhysicsWorld() != nullptr)
    {
        child->updatePhysicsBodyTransform(scene);
        scene->addChildToPhysicsWorld(child);
    }
#endif
    
    if( _running )
    {
        child->onEnter();
        // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
        if (_isTransitionFinished) {
            child->onEnterTransitionDidFinish();
        }
    }
    
    if (_cascadeColorEnabled)
    {
        updateCascadeColor();
    }
    
    if (_cascadeOpacityEnabled)
    {
        updateCascadeOpacity();
    }
}

從實現源碼不難看出,全部的子節點都被保存到 _children 數組中。若是父節點不處於 _running 狀態,那麼子節點在添加時就不會被調用 onEnter和onEnterTransitionDidFinished函數,這會產生什麼影響筆者從此再作補充。orm

4、結束

本文就先介紹Node類實現往父節點的節點樹添加子節點的過程。下一篇博客會繼續介紹Node類節點樹的實現。

相關文章
相關標籤/搜索