cocos2dx-ui的渲染机制

15 篇文章 0 订阅

渲染系统是游戏引擎里面最重要的一个模块之一了,如何遍历UI树,如何将UI合理的渲染在屏幕上,如何选择渲染的顺序,这是渲染系统最需要考虑的。其实遍历的顺序就决定了渲染的顺序。

Cocos2d-x的渲染函数是通过Node::visit来进行的,首先看看这个函数干了什么吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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的判断就干了这件事情。

然后判断子节点是否为空,子节点为空的判断是否在摄像机可视范围内,只有在可视范围内才进行绘制,如下:

1
2
3
4
else  if  (visibleByCamera)  
{  
     this ->draw(renderer, _modelViewTransform, flags);  
}

然后重点就是子节点非空的情况了,子节点非空的时候,先将所有子节点进行排序,根据什么排序呢?走进sortAllChildren函数看一看

1
2
3
4
5
6
7
8
void  Node::sortAllChildren()  
{  
     if  (_reorderChildDirty)  
     {  
         std::sort(std::begin(_children), std::end(_children), nodeComparisonLess);  
         _reorderChildDirty =  false ;  
     }  
}

还要继续深入nodeComparisonLess函数

1
2
3
4
5
6
bool  nodeComparisonLess(Node* n1, Node* n2)  
{  
     return ( n1->getLocalZOrder() < n2->getLocalZOrder() ||  
            ( n1->getLocalZOrder() == n2->getLocalZOrder() && n1->getOrderOfArrival() < n2->getOrderOfArrival() )  
            );  
}

可以看到它是根据LocalZOrder进行排序的,当LocalZOrder相同的情况下,根据加入UI树的顺序排序。那么LocalZOrder就是非常重要的元素了,排序好的子节点是基于LocalZOrder递增的。接下来看看子节点是如何进行绘制的。

1
2
3
4
5
6
7
8
9
10
// 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的

1
2
3
4
5
6
// self draw  
         if  (visibleByCamera)  
             this ->draw(renderer, _modelViewTransform, flags);  
   
         for (auto it=_children.cbegin()+i; it != _children.cend(); ++it)  
             (*it)->visit(renderer, _modelViewTransform, flags);

下面是一张图便于理解

1.jpg

所以UI树的遍历方法就是中序遍历的深度优先算法。总结如下:

1.遍历左边子节点

2.遍历根节点

3.遍历右边子节点

通过这样的方法,可以保证UI的绘制顺序可以通过_localZOrder 来调节,小于0的将被首先绘制,然后再绘制父节点。然后再绘制大于0的。

所以综上所述,_localZOrder 越小的元素将会被优先绘制

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值