Cocos2d-x学习(二十二):cocos2d-x中CCScale9Sprite的另一种实现

来源:互联网 发布:linux 网络接口 编辑:程序博客网 时间:2024/05/18 00:11

cocos2d 2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图片资源的中间部分是纯色或者是简单的渐变了!


1.cocos2d中九宫格CCScale9Sprite的实现

(1)原理

cocos2d的实现非常巧妙,是通过1个CCSpriteBatchNode和9个CCSprite来实现的,原理很简单,通过将原纹理资源切割成9部分(PS: 这也是叫九宫格的原因),根据想要的尺寸,完成以下的三个步骤:

a. 保持4个角部分不变形

b. 单向拉伸4条边(即在4个角两两之间的边,比如上边,只做横向拉伸)

c. 双向拉伸中间部分(即九宫格的中间部分,横向,纵向同时拉伸,PS:拉伸比例不一定相同)


(PS: 更多原理可参考 http://yannickloriot.com/2011/12/create-buttons-in-cocos2d-by-using-cccontrolbutton/)

(2)实现

CCSpriteBatchNode的资源为整个的纹理,9个CCSprite对应于纹理的9个部分(根据纹理不同,9部分所占比例会有所不同),根据想要的尺寸,将9部分拼装在一起!

(3)优缺点

优点:思路简单清晰;使用CCSpriteBatchNode,只需要一次绘制,效率较高

缺点:内存占用大,需要1个CCSpriteBatchNode和9个CCSprite对象;不支持CCSpriteBatchNode(如果控件很多,我们都需要对每个控件单独绘制一次,会影响效率)

2.cocos2d-x中CCSprite的绘制

在介绍我的九宫格实现之前,先简单介绍一下CCSprite的绘制原理

(1)顶点数据

每一个CCSprite都保持了一个关于顶点数据的结构体

[cpp] view plaincopy
  1. <span style="font-size:12px;">// vertex coords, texture coords and color info  
  2.     ccV3F_C4B_T2F_Quad m_sQuad;</span>  
这个Quad字眼的意思是一个矩形,参照ccV3F_C4B_T2F_Quad的定义,可以得知,是包含4个顶点数据的结构体(根据注释可知4个顶点分别为:左上,左下,右上,右下)

[cpp] view plaincopy
  1. <span style="font-size:12px;">//! 4 ccVertex3FTex2FColor4B  
  2. typedef struct _ccV3F_C4B_T2F_Quad  
  3. {  
  4.     //! top left  
  5.     ccV3F_C4B_T2F   tl;  
  6.     //! bottom left  
  7.     ccV3F_C4B_T2F   bl;  
  8.     //! top right  
  9.     ccV3F_C4B_T2F   tr;  
  10.     //! bottom right  
  11.     ccV3F_C4B_T2F   br;  
  12. } ccV3F_C4B_T2F_Quad;</span>  
而ccV3F_C4B_T2F又是一个关于顶点信息的结构体,包括坐标(x, y, z),颜色(r, g, b, a),纹理坐标(x, y)

(PS:2D游戏中,坐标的z都为0,这里的z并不是Z-Order,Z-Order是指渲染的先后属性,z是代表3D的z轴坐标)

[cpp] view plaincopy
  1. <span style="font-size:12px;">//! a Point with a vertex point, a tex coord point and a color 4B  
  2. typedef struct _ccV3F_C4B_T2F  
  3. {  
  4.     //! vertices (3F)  
  5.     ccVertex3F      vertices;           // 12 bytes  
  6. //  char __padding__[4];  
  7.   
  8.     //! colors (4B)  
  9.     ccColor4B       colors;             // 4 bytes  
  10. //  char __padding2__[4];  
  11.   
  12.     // tex coords (2F)  
  13.     ccTex2F         texCoords;          // 8 byts  
  14. } ccV3F_C4B_T2F;</span>  

(2)绘制

在初始化精灵之后,就将纹理的四个顶点信息保存在m_sQuad中了,接下来要做的,就是根据m_sQuad的信息来绘制

由于OpenGL是状态机的设计,所以要先将顶点信息保存,再根据顶点的关系进行绘制,主要的绘制代码如下:

[cpp] view plaincopy
  1. <span style="font-size:12px;">#define kQuadSize sizeof(m_sQuad.bl)  
  2.     int size = sizeof(m_sQuad.bl);  
  3.     if (m_pobTexture)  
  4.     {  
  5.         glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());  
  6.     }  
  7.     else  
  8.     {  
  9.         glBindTexture(GL_TEXTURE_2D, 0);  
  10.     }  
  11.   
  12.     long offset = (long)&m_sQuad;  
  13.   
  14.     // vertex  
  15.     int diff = offsetof(ccV3F_C4B_T2F, vertices);  
  16.     glVertexPointer(3, GL_FLOAT, kQuadSize, (void*)(offset + diff));  
  17.   
  18.     // color  
  19.     diff = offsetof( ccV3F_C4B_T2F, colors);  
  20.     glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));  
  21.       
  22.     // tex coords  
  23.     diff = offsetof( ccV3F_C4B_T2F, texCoords);  
  24.     glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));  
  25.       
  26.     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);</span>  

(PS: offsetof()函数是得到结构体中某一数据的地址偏移量)

根据注释可知,先将顶点的坐标数据保存,再将顶点的颜色数据保存,最后将顶点的纹理映射坐标保存

(吐槽一下:#define kQuadSize sizeof(m_sQuad.bl) 这个宏的名字把我迷惑了,我不知道为什么会有Quad字眼,我觉得应该是kVertexSize)

3. CCScaleNineSprite的实现

(吐槽一下:我没找到更好的关于九宫格的名字,于是偷懒将9换成了Nine。。。)

我的九宫格的实现和CCScale9Sprite略有不同,只是优化了其内存的问题,我将1个CCSpriteBatchNode和9个CCSprite用1个CCSprite来实现了,通过纹理映射做拉伸!

(PS:我目前也没有解决支持CCSpriteBatchNode,因为CCSpriteBatchNode的子节点要求是CCSprite类型,而我的CCScaleNineSprite并不是继承于CCSprite,而是于CCSprite是兄弟关系,因为其顶点的数据不同,所以我认为不是继承关系,当然可以考虑把CCSprite的顶点数据修改,使其不再被限制于固定4个顶点)

我偷懒将CCSprite.h和CCSprite.cpp拷贝了一份,注释掉了一些不常用的方法,以及对CCSpriteBatchNode的支持。。。

将m_sQuad替换为ccV3F_C4B_T2F mScaleNineVertices[16](九宫格需要16个顶点,请根据上面的图计算,包括顶点和切割线的交点)

额外增加了1个设置九宫格比例的方法(重载了3份),通过比例计算出mScaleNineVertices的数据

[cpp] view plaincopy
  1. <span style="font-size:12px;">public:  
  2.     void CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int widthFromRight,   
  3.         unsigned int heightFromTop, unsigned int heightFromBottom);  
  4.     void CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int heightFromTop);  
  5.     void CalculateScaleNineVertices(unsigned int offsetFromEdge);</span>  


贴上这个长长的计算算法吧,我表示我很笨,没有想到更好的计算算法。。。欢迎留言赐教

[cpp] view plaincopy
  1. void CCScaleNineSprite::CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int widthFromRight,   
  2.     unsigned int heightFromTop, unsigned int heightFromBottom)  
  3. {  
  4.     float textureOriginX = m_obRectInPixels.origin.x;  
  5.     float textureOriginY = m_obRectInPixels.origin.y;  
  6.   
  7.     float textureWidth = m_obRectInPixels.size.width;  
  8.     float textureHeight = m_obRectInPixels.size.height;  
  9.   
  10.     CCAssert((widthFromLeft < textureWidth) && (widthFromRight < textureWidth) &&   
  11.         (heightFromTop < textureHeight) && (heightFromBottom < textureHeight), "The SIZE of Corner is too BIG!");  
  12.   
  13.     float contentWidth = m_tContentSizeInPixels.width;  
  14.     float contentHeight = m_tContentSizeInPixels.height;  
  15.       
  16.     unsigned int textureAtlasWidth = getTexture()->getPixelsWide();  
  17.     unsigned int textureAtlasHeight = getTexture()->getPixelsHigh();  
  18.   
  19.     ccV3F_C4B_T2F vertice;  
  20.   
  21.     // First Line  
  22.     vertice.vertices.x = 0;  
  23.     vertice.vertices.y = contentHeight;  
  24.     vertice.vertices.z = 0;  
  25.     vertice.colors.a = 255;  
  26.     vertice.colors.r = 255;  
  27.     vertice.colors.g = 255;  
  28.     vertice.colors.b = 255;  
  29.     vertice.texCoords.u = textureOriginX / textureAtlasWidth;  
  30.     vertice.texCoords.v = textureOriginY / textureAtlasHeight;  
  31.     mScaleNineVertices[0] = vertice;  
  32.   
  33.     vertice.vertices.x = (float) widthFromLeft;  
  34.     vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;  
  35.     mScaleNineVertices[1] = vertice;  
  36.   
  37.     vertice.vertices.x = (float) (contentWidth - widthFromRight);  
  38.     vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;  
  39.     mScaleNineVertices[2] = vertice;  
  40.   
  41.     vertice.vertices.x = (float) contentWidth;  
  42.     vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;  
  43.     mScaleNineVertices[3] = vertice;  
  44.   
  45.     // Second Line  
  46.     vertice.vertices.x = 0;  
  47.     vertice.vertices.y = (float) (contentHeight - heightFromTop);  
  48.     vertice.texCoords.u = textureOriginX / textureAtlasWidth;  
  49.     vertice.texCoords.v = (float) (textureOriginY + heightFromTop) / textureAtlasHeight;  
  50.     mScaleNineVertices[4] = vertice;  
  51.   
  52.     vertice.vertices.x = (float) widthFromLeft;  
  53.     vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;  
  54.     mScaleNineVertices[5] = vertice;  
  55.   
  56.     vertice.vertices.x = (float) (contentWidth - widthFromRight);  
  57.     vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;  
  58.     mScaleNineVertices[6] = vertice;  
  59.   
  60.     vertice.vertices.x = (float) contentWidth;  
  61.     vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;  
  62.     mScaleNineVertices[7] = vertice;  
  63.   
  64.     // Third Line  
  65.     vertice.vertices.x = 0;  
  66.     vertice.vertices.y = (float) heightFromBottom;  
  67.     vertice.texCoords.u = textureOriginX / textureAtlasWidth;  
  68.     vertice.texCoords.v = (float) (textureOriginY + textureHeight - heightFromBottom) / textureAtlasHeight;  
  69.     mScaleNineVertices[8] = vertice;  
  70.   
  71.     vertice.vertices.x = (float) widthFromLeft;  
  72.     vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;  
  73.     mScaleNineVertices[9] = vertice;  
  74.   
  75.     vertice.vertices.x = (float) (contentWidth - widthFromRight);  
  76.     vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;  
  77.     mScaleNineVertices[10] = vertice;  
  78.   
  79.     vertice.vertices.x = (float) contentWidth;  
  80.     vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;  
  81.     mScaleNineVertices[11] = vertice;  
  82.   
  83.     // Fourth Line  
  84.     vertice.vertices.x = 0;  
  85.     vertice.vertices.y = 0;  
  86.     vertice.texCoords.u = textureOriginX / textureAtlasWidth;  
  87.     vertice.texCoords.v = (float) (textureOriginY + textureHeight) / textureAtlasHeight;  
  88.     mScaleNineVertices[12] = vertice;  
  89.   
  90.     vertice.vertices.x = (float) widthFromLeft;  
  91.     vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;  
  92.     mScaleNineVertices[13] = vertice;  
  93.   
  94.     vertice.vertices.x = (float) (contentWidth - widthFromRight);  
  95.     vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;  
  96.     mScaleNineVertices[14] = vertice;  
  97.   
  98.     vertice.vertices.x = (float) contentWidth;  
  99.     vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;  
  100.     mScaleNineVertices[15] = vertice;  
  101. }  



计算好顶点数据之后,简单修改一下draw()函数就可以了(将之前的m_sQuad替换为mScaleNineVertices)

[cpp] view plaincopy
  1. <span style="font-size:12px;">#define kVertexSize sizeof(ccV3F_C4B_T2F)  
  2.     if (m_pobTexture)  
  3.     {  
  4.         glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());  
  5.     }  
  6.     else  
  7.     {  
  8.         glBindTexture(GL_TEXTURE_2D, 0);  
  9.     }  
  10.   
  11.     <span style="color:#ff0000;">long offset  = (long) mScaleNineVertices;</span>  
  12.   
  13.     // vertex  
  14.     int diff = offsetof(ccV3F_C4B_T2F, vertices);  
  15.     glVertexPointer(3, GL_FLOAT, kVertexSize, (void*)(offset + diff));  
  16.   
  17.     // color  
  18.     diff = offsetof( ccV3F_C4B_T2F, colors);  
  19.     glColorPointer(4, GL_UNSIGNED_BYTE, kVertexSize, (void*)(offset + diff));  
  20.       
  21.     // tex coords  
  22.     diff = offsetof( ccV3F_C4B_T2F, texCoords);  
  23.     glTexCoordPointer(2, GL_FLOAT, kVertexSize, (void*)(offset + diff));  
  24.       
  25.     <span style="color:#ff0000;">glDrawElements(GL_TRIANGLES, 54, GL_UNSIGNED_SHORT, mVerticesIndex);</span></span>  


看起来和之前的差别不大。。。只有两处修改(高亮吧!)

4.Demo

和CCSprite的使用差不太多,只是需要设置一下ContentSize(即展示的尺寸),并且需要设置九宫格切割的比例(以像素为单位,美术比较好理解!)

[cpp] view plaincopy
  1. <span style="font-size:12px;">// Add a Scale Nine Sprite  
  2.         CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("GreenButton.png");  
  3.         CCScaleNineSprite* scaleNineSprite = CCScaleNineSprite::scaleNineSpriteWithTexture(texture);  
  4.         scaleNineSprite->setContentSize(CCSizeMake(200, 100));  
  5.         scaleNineSprite->CalculateScaleNineVertices(10);  
  6.         scaleNineSprite->setPosition(CCPointMake(size.width / 2, size.height / 2));  
  7.         this->addChild(scaleNineSprite);</span>  

效果如下:



原资源: