cocos2d-x 源码剖析(21)

今天有个朋友在开发中遇到一个字符渲染的问题,原来cocos2d-x不支持LTR和RTL混合字符的渲染,我当初压根没有意识到还有这个问题的存在。跟踪之后发现,cocos2d-x确实没有支持,而且用ttf改起来颇为费劲,关键是实现逻辑的地方太深了,没有看到Android的情况,要做好移植很难。不过用bmf改起来还是比较方便的,能直接修改C++代码,不用关心底层,能很好的移植。但是现实起来还是要做不少工作。这位同学是写Lua的,基本上不狠下一番功夫是无望了。cocos2d-x xna上有个issues是关于这个的,但是这个分支已经死掉了。看,读cocos2d-x的源码还是很有用的吧。起来能快速判定问题所在,不是无头苍蝇瞎找一气了。

在群里聊天的时候,发现有些cocos2d-x的代码确实很烂。有些水平停留在只是会写C++代码的人也在上面提交关键代码。缩进连一个起码的要求都没有。我一直不看好cocos studio,不支持mac不说,UI实在太难看的。我把这部分搞完就准备自己实现一个2d引擎,算作我游戏开发的总结。一个真正易于扩展易于使用的引擎,如果有时间还会配套一些开发工具,比如TP和Animation之类的。

上一节讲到了CCSpriteBatchNode,这节要讲CCSprite了。CCSprite估计是游戏中是用的最多的东西,他的作用就是显示一张图片。其他一些需要显示图片的工作也是他来做的。CCSprite的内容很简单,我们之前基本上把关键点都讲完了。CCSprite之所以看起来那么多代码就是应为有两种不同的渲染模式存在,而且是可以转化的。这增加了不少的复杂度,这种设计就是典型的用if代替类的使用。他的初始化代码可以分成3组:

static CCSprite* create(const char *pszFileName);
static CCSprite* createWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
static CCSprite* createWithTexture(CCTexture2D *pTexture);


真正起作用的是第三个函数。这几个函数有个漏洞,我clone一个sprite十分不方便。而这些东西用SpriteFrame就已经封装好了的。

其实来说CCSprite的这些所有逻辑都是为了正确的显示一张图片。draw函数是在非CCSpriteBatchNode中,显示的时候调用的。那么一些属性生效的逻辑就不能在这里,而是在visit函数中。我们之前看到过CCNode的visit的代码,当时了解的不够深入,现在看来draw函数千万不要有任何非显示的逻辑存在,切记切记。我们来看看visit中的transform这个函数,这个函数例行公事,做了一些通用逻辑,需要注意的是nodeToParentTransform这个函数,他就是哪些属性生效的地方了:

CCAffineTransform CCNode::nodeToParentTransform(void)
{
    if (m_bTransformDirty) 
    {
 
// Translate values
        float x = m_obPosition.x;
        float y = m_obPosition.y;
 
        if (m_bIgnoreAnchorPointForPosition) 
        {
            x += m_obAnchorPointInPoints.x;
            y += m_obAnchorPointInPoints.y;
        }
 
// Rotation values
// Change rotation code to handle X and Y
// If we skew with the exact same value for both x and y then we're simply just rotating
        float cx = 1, sx = 0, cy = 1, sy = 0;
        if (m_fRotationX || m_fRotationY)
        {
            float radiansX = -CC_DEGREES_TO_RADIANS(m_fRotationX);
            float radiansY = -CC_DEGREES_TO_RADIANS(m_fRotationY);
            cx = cosf(radiansX);
            sx = sinf(radiansX);
            cy = cosf(radiansY);
            sy = sinf(radiansY);
        }
 
        bool needsSkewMatrix = ( m_fSkewX || m_fSkewY );
 
// optimization:
// inline anchor point calculation if skew is not needed
// Adjusted transform calculation for rotational skew
        if (! needsSkewMatrix && !m_obAnchorPointInPoints.equals(CCPointZero))
        {
            x += cy * -m_obAnchorPointInPoints.x * m_fScaleX + -sx * -m_obAnchorPointInPoints.y * m_fScaleY;
            y += sy * -m_obAnchorPointInPoints.x * m_fScaleX +  cx * -m_obAnchorPointInPoints.y * m_fScaleY;
        }
 
// Build Transform Matrix
// Adjusted transform calculation for rotational skew
        m_sTransform = CCAffineTransformMake( cy * m_fScaleX,  sy * m_fScaleX,
            -sx * m_fScaleY, cx * m_fScaleY,
            x, y );
 
// XXX: Try to inline skew
// If skew is needed, apply skew and then anchor point
        if (needsSkewMatrix) 
        {
            CCAffineTransform skewMatrix = CCAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(m_fSkewY)),
                tanf(CC_DEGREES_TO_RADIANS(m_fSkewX)), 1.0f,
                0.0f, 0.0f );
            m_sTransform = CCAffineTransformConcat(skewMatrix, m_sTransform);
 
// adjust anchor point
            if (!m_obAnchorPointInPoints.equals(CCPointZero))
            {
                m_sTransform = CCAffineTransformTranslate(m_sTransform, -m_obAnchorPointInPoints.x, -m_obAnchorPointInPoints.y);
            }
        }
 
        if (m_bAdditionalTransformDirty)
        {
            m_sTransform = CCAffineTransformConcat(m_sTransform, m_sAdditionalTransform);
            m_bAdditionalTransformDirty = false;
        }
 
        m_bTransformDirty = false;
    }
 
    return m_sTransform;
}


我的数学不好就不做解释了,这个转换作用于OpenGL的绘制矩阵而不是自己的顶点坐标,这点要清楚。需要明晰下面两个函数的不同:

virtual void setScaleX(float fScaleX);
void setFlipX(bool bFlipX);

在CCSpriteBatchNode的visit中,transform也是被调用了的,所以这些都有效果。至于真正的显示,就就本上和绘制纹理的情况类似了。在CCSpriteBatchNode中管理Children的方式和普通的CCSprite不同,所以CCSprite的一些逻辑都做了特殊的处理。我们来看看CCSprite添加到CCSpriteBatchNode的转化过程:

void CCSprite::setBatchNode(CCSpriteBatchNode *pobSpriteBatchNode)
{
  m_pobBatchNode = pobSpriteBatchNode; // weak reference
 
  // self render
  if( ! m_pobBatchNode ) {
    m_uAtlasIndex = CCSpriteIndexNotInitialized;
    setTextureAtlas(NULL);
    m_bRecursiveDirty = false;
    setDirty(false);
 
    float x1 = m_obOffsetPosition.x;
    float y1 = m_obOffsetPosition.y;
    float x2 = x1 + m_obRect.size.width;
    float y2 = y1 + m_obRect.size.height;
    m_sQuad.bl.vertices = vertex3( x1, y1, 0 );
    m_sQuad.br.vertices = vertex3( x2, y1, 0 );
    m_sQuad.tl.vertices = vertex3( x1, y2, 0 );
    m_sQuad.tr.vertices = vertex3( x2, y2, 0 );
 
  } else {
      // using batch
    m_transformToBatch = CCAffineTransformIdentity;
      setTextureAtlas(m_pobBatchNode->getTextureAtlas()); // weak ref
  }
}

意外的简洁,做事的函数交到这里来做了:

void CCSpriteBatchNode::appendChild(CCSprite* sprite)
{
  m_bReorderChildDirty=true;
  sprite->setBatchNode(this);
  sprite->setDirty(true);
 
  if(m_pobTextureAtlas->getTotalQuads() == m_pobTextureAtlas->getCapacity()) {
    increaseAtlasCapacity();
  }
 
  ccArray *descendantsData = m_pobDescendants->data;
 
  ccArrayAppendObjectWithResize(descendantsData, sprite);
 
  unsigned int index=descendantsData->num-1;
 
  sprite->setAtlasIndex(index);
 
  ccV3F_C4B_T2F_Quad quad = sprite->getQuad();
  m_pobTextureAtlas->insertQuad(&quad, index);
 
    // add children recursively
 
  CCObject* pObj = NULL;
  CCARRAY_FOREACH(sprite->getChildren(), pObj)
  {
    CCSprite* child = (CCSprite*)pObj;
    appendChild(child);
  }
}

递归调用,注意注意。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值