cocos2d-x源码剖析之场景管理

           cocos2d中采用树形结构来管理场景中的所有对象。所有的对象都是CCNode的子类。那么我们先探探CCNode类的虚实,先上代码:

class CC_DLL CCNode : public CCObject
{
public:
    virtual void update(float delta);
    virtual void draw(void);
    virtual void visit(void);
protected:
    float m_fRotationX;                 ///< rotation angle on x-axis
    float m_fRotationY;                 ///< rotation angle on y-axis
    
    float m_fScaleX;                    ///< scaling factor on x-axis
    float m_fScaleY;                    ///< scaling factor on y-axis
    
    float m_fVertexZ;                   ///< OpenGL real Z vertex
    
    CCPoint m_obPosition;               ///< position of the node
    
    float m_fSkewX;                     ///< skew angle on x-axis
    float m_fSkewY;                     ///< skew angle on y-axis
    CCCamera *m_pCamera;             ///< a camera
    int m_nZOrder;                                  ///< z-order value that affects the draw order
    CCArray *m_pChildren;                  ///< array of children nodes
    CCNode *m_pParent;                    ///< weak reference to parent node

    CCActionManager *m_pActionManager;  ///< a pointer to ActionManager singleton, which is used to handle all the actions
}

    从上面的结构可以看到用m_pChildren保存所有的子结点,用m_pParent记住了当前结点的父节点。而m_Zorder是表示所有子结点之间的渲染优先级。

    CCDirector::drawScene再剖析

    上一篇文章中每一帧CCDirector::drawScene中会调用:

       m_pScheduler->update(m_fDeltaTime);  

   这个最终会调用到上面的update函数。可能有的读者会觉得奇怪,CCNode需要update么?其实有些结点需要,比如粒子系统就需要每帧更新所有粒子的参数。

   另外在上一篇文章中CCDirector::drawScene中还会调用:

         m_pRunningScene->visit();

   这个m_pRunningScene是一个CCScene对象,也是CCNode的子类,相当于遍历场景结点树并进行绘制,让我们看看CCNode::visit都做了些什么?

void CCNode::visit()
{
    // quick return if not visible. children won't be drawn.
    if (!m_bVisible)
    {
        return;
    }
    kmGLPushMatrix();

    this->transform();

    CCNode* pNode = NULL;
    unsigned int i = 0;


    if(m_pChildren && m_pChildren->count() > 0)
    {
        sortAllChildren();
        // draw children zOrder < 0
        ccArray *arrayData = m_pChildren->data;
        for( ; i < arrayData->num; i++ )
        {
            pNode = (CCNode*) arrayData->arr[i];


            if ( pNode && pNode->m_nZOrder < 0 ) 
            {
                pNode->visit();
            }
            else
            {
                break;
            }
        }
        // self draw
        this->draw();


        for( ; i < arrayData->num; i++ )
        {
            pNode = (CCNode*) arrayData->arr[i];
            if (pNode)
            {
                pNode->visit();
            }
        }        
    }
    else
    {
        this->draw();
    }
 
    kmGLPopMatrix();
}

  从这段代码可以看到,CCNode的所有子结点绘制的顺序是按照m_nZOrder 来的,先绘制m_nZOrder < 0的,然后绘制自身,最后绘制m_nZOrder >=0的。对于每个子类需要重构this->draw()来实现自己的绘制方法。在这个函数中,还有一个非常重要的方法:this->transform(),不同坐标系的变换就靠它了!

void CCNode::transform()
{    
       kmMat4 transfrom4x4;
       CCAffineTransform tmpAffine = this->nodeToParentTransform();
       CGAffineToGL(&tmpAffine, transfrom4x4.mat);
       transfrom4x4.mat[14] = m_fVertexZ;
        kmGLMultMatrix( &transfrom4x4 );

       // XXX: Expensive calls. Camera should be integrated into the cached affine matrix
       if ( m_pCamera != NULL && !(m_pGrid != NULL && m_pGrid->isActive()) )
       {
          m_pCamera->locate();
       }
}

      因为树形结构是采用相对的坐标表示的,所有我们只需要计算相对于父结点的变换矩阵transfrom4x4 ,然后乘以当前的栈顶的变换矩阵,特别注意的是:在访问的过程中变换矩阵式以堆栈的形式保存的

    kmGLPushMatrix();
   ......
    kmGLPopMatrix();

     访问前后分别将变换矩阵状态压栈和出栈。另外如果CCNode有自己的相机的话,则还需要将相机的变换矩阵(也就是我们常说的View Matrix)计算进去。)为什么CCNode可以有自己的相机呢?比如说我想要每个对象缩放,那我可以让这个CCNode的相机拉远,当然我们也可以设置缩放参数。显然后者更简单,不过当碰到前一种前一种情况更适合的时候不要忘记了他的存在。

    变换矩阵的更新过程中都是使用脏更新。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值