x中CCScale9Sprite的另一种实现

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

1.cocos2d中九宫格CCScale9Sprite的实现(1)原理

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

a. 保持4个角部分不变形b. 单向拉伸4条边(即在4个角两两之间的边,比如上边,只做横向拉伸)c. 双向拉伸中间部分(即九宫格的中间部分,横向,纵向同时拉伸,PS:拉伸比例不一定相同)

(PS: 更多原理可参考 )

(2)实现

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

(3)优缺点

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

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

2.cocos2d-x中CCSprite的绘制

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

(1)顶点数据

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

// vertex coords, texture coords and color infoccV3F_C4B_T2F_Quad m_sQuad;这个Quad字眼的意思是一个矩形,参照ccV3F_C4B_T2F_Quad的定义,可以得知,是包含4个顶点数据的结构体(根据注释可知4个顶点分别为:左上,左下,右上,右下)//! 4 ccVertex3FTex2FColor4Btypedef struct _ccV3F_C4B_T2F_Quad{//! top leftccV3F_C4B_T2Ftl;//! bottom leftccV3F_C4B_T2Fbl;//! top rightccV3F_C4B_T2Ftr;//! bottom rightccV3F_C4B_T2Fbr;} ccV3F_C4B_T2F_Quad;而ccV3F_C4B_T2F又是一个关于顶点信息的结构体,包括坐标(x, y, z),颜色(r, g, b, a),纹理坐标(x, y)

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

//! a Point with a vertex point, a tex coord point and a color 4Btypedef struct _ccV3F_C4B_T2F{//! vertices (3F)ccVertex3Fvertices;// 12 bytes//char __padding__[4];//! colors (4B)ccColor4Bcolors;// 4 bytes//char __padding2__[4];// tex coords (2F)ccTex2FtexCoords;// 8 byts} ccV3F_C4B_T2F;(2)绘制

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

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

#define kQuadSize sizeof(m_sQuad.bl)int size = sizeof(m_sQuad.bl);if (m_pobTexture){glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());}else{glBindTexture(GL_TEXTURE_2D, 0);}long offset = (long)&m_sQuad;// vertexint diff = offsetof(ccV3F_C4B_T2F, vertices);glVertexPointer(3, GL_FLOAT, kQuadSize, (void*)(offset + diff));// colordiff = offsetof( ccV3F_C4B_T2F, colors);glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));// tex coordsdiff = offsetof( ccV3F_C4B_T2F, texCoords);glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

(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的数据

public:void CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int widthFromRight, unsigned int heightFromTop, unsigned int heightFromBottom);void CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int heightFromTop);void CalculateScaleNineVertices(unsigned int offsetFromEdge);

贴上这个长长的计算算法吧,我表示我很笨,没有想到更好的计算算法。。。欢迎留言赐教也站在未路让我牵挂的人。

x中CCScale9Sprite的另一种实现

相关文章:

你感兴趣的文章:

标签云: