一、聲明
筆者分析的是用C++語言實現、版本號為cocos2d-x-3.3rc0的cocos2d框架的源代碼。本文為筆者原創,允許讀者分享和轉載,只要讀者注明文章來源即可。
二、簡介
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。
這些屬性會在后續的源碼分析中做具體介紹。
三、源碼詳解
Node比較龐大,筆者打算在多篇博客中分別詳細介紹Node節點的不同模塊。前面說到Node對象能夠持有其他Node對象作為其子節點,也就是說一個Node對象其實能夠擴展出一個節點樹。所以筆者先介紹節點樹模塊。
節點樹實現
添加子節點
添加子節點的過程需要到下面的屬性。
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取值從負軸到正軸,顯示順序遞減。
函數聲明還提到,如果當前父節點處於running狀態,那么被添加的子節點會被立刻調用onEnter和onEnterTransitionDidFinish函數。下面看此函數的具體實現。
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)。下面看該函數的實現。
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函數,這會產生什么影響筆者今后再做補充。
四、結束
本文就先介紹Node類實現往父節點的節點樹添加子節點的過程。下一篇博客會繼續介紹Node類節點樹的實現。