渲染系統是遊戲引擎裏面最重要的一個模塊之一了,如何遍歷UI樹,如何將UI合理的渲染在屏幕上,如何選擇渲染的順序,這是渲染系統最需要考慮的。其實遍歷的順序就決定了渲染的順序。
cocos2d-x的渲染函數是通過Node::visit來進行的,首先看看這個函數幹了什麼吧
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.
if (!_visible)
{
return;
}
uint32_t flags = processParentFlags(parentTransform, parentFlags);
bool visibleByCamera = isVisitableByVisitingCamera();
int i = 0;
if(!_children.empty())
{
sortAllChildren();
// draw children zOrder < 0
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);
if (node && node->_localZOrder < 0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
}
然後可以開始一步一步分析代碼了,首先快速去掉不可見的元素,並且不繪製它的子元素。_visible的判斷就幹了這件事情。
然後判斷子節點是否爲空,子節點爲空的判斷是否在攝像機可視範圍內,只有在可視範圍內才進行繪製,如下:
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
然後重點就是子節點非空的情況了,子節點非空的時候,先將所有子節點進行排序,根據什麼排序呢?走進sortAllChildren函數看一看
void Node::sortAllChildren()
{
if (_reorderChildDirty)
{
std::sort(std::begin(_children), std::end(_children), nodeComparisonLess);
_reorderChildDirty = false;
}
}
還要繼續深入nodeComparisonLess函數
bool nodeComparisonLess(Node* n1, Node* n2)
{
return( n1->getLocalZOrder() < n2->getLocalZOrder() ||
( n1->getLocalZOrder() == n2->getLocalZOrder() && n1->getOrderOfArrival() < n2->getOrderOfArrival() )
);
}
可以看到它是根據LocalZOrder進行排序的,當LocalZOrder相同的情況下,根據加入UI樹的順序排序。那麼LocalZOrder就是非常重要的元素了,排序好的子節點是基於LocalZOrder遞增的。接下來看看子節點是如何進行繪製的。
// draw children zOrder < 0
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);
if (node && node->_localZOrder < 0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
首先繪製_localZOrder小於0的,然後繪製自身 最後繪製大於等於0的
// self draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
下面是一張圖便於理解
所以UI樹的遍歷方法就是中序遍歷的深度優先算法。總結如下:
1.遍歷左邊子節點
2.遍歷根節點
3.遍歷右邊子節點
通過這樣的方法,可以保證UI的繪製順序可以通過_localZOrder 來調節,小於0的將被首先繪製,然後再繪製父節點。然後再繪製大於0的。
所以綜上所述,_localZOrder 越小的元素將會被優先繪製。