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都保持了一个关于顶点数据的结构体
// vertex coords, texture coords and color info
ccV3F_C4B_T2F_Quad m_sQuad;
这个Quad字眼的意思是一个矩形,参照ccV3F_C4B_T2F_Quad的定义,可以得知,是包含4个顶点数据的结构体(根据注释可知4个顶点分别为:左上,左下,右上,右下)
//! 4 ccVertex3FTex2FColor4B
typedef struct _ccV3F_C4B_T2F_Quad
{
//! top left
ccV3F_C4B_T2F tl;
//! bottom left
ccV3F_C4B_T2F bl;
//! top right
ccV3F_C4B_T2F tr;
//! bottom right
ccV3F_C4B_T2F br;
} 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 4B
typedef struct _ccV3F_C4B_T2F
{
//! vertices (3F)
ccVertex3F vertices; // 12 bytes
// char __padding__[4];
//! colors (4B)
ccColor4B colors; // 4 bytes
// char __padding2__[4];
// tex coords (2F)
ccTex2F texCoords; // 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;
// vertex
int diff = offsetof(ccV3F_C4B_T2F, vertices);
glVertexPointer(3, GL_FLOAT, kQuadSize, (void*)(offset + diff));
// color
diff = offsetof( ccV3F_C4B_T2F, colors);
glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
// tex coords
diff = 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);
贴上这个长长的计算算法吧,我表示我很笨,没有想到更好的计算算法。。。欢迎留言赐教
void CCScaleNineSprite::CalculateScaleNineVertices(unsigned int widthFromLeft, unsigned int widthFromRight,
unsigned int heightFromTop, unsigned int heightFromBottom)
{
float textureOriginX = m_obRectInPixels.origin.x;
float textureOriginY = m_obRectInPixels.origin.y;
float textureWidth = m_obRectInPixels.size.width;
float textureHeight = m_obRectInPixels.size.height;
CCAssert((widthFromLeft < textureWidth) && (widthFromRight < textureWidth) &&
(heightFromTop < textureHeight) && (heightFromBottom < textureHeight), "The SIZE of Corner is too BIG!");
float contentWidth = m_tContentSizeInPixels.width;
float contentHeight = m_tContentSizeInPixels.height;
unsigned int textureAtlasWidth = getTexture()->getPixelsWide();
unsigned int textureAtlasHeight = getTexture()->getPixelsHigh();
ccV3F_C4B_T2F vertice;
// First Line
vertice.vertices.x = 0;
vertice.vertices.y = contentHeight;
vertice.vertices.z = 0;
vertice.colors.a = 255;
vertice.colors.r = 255;
vertice.colors.g = 255;
vertice.colors.b = 255;
vertice.texCoords.u = textureOriginX / textureAtlasWidth;
vertice.texCoords.v = textureOriginY / textureAtlasHeight;
mScaleNineVertices[0] = vertice;
vertice.vertices.x = (float) widthFromLeft;
vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;
mScaleNineVertices[1] = vertice;
vertice.vertices.x = (float) (contentWidth - widthFromRight);
vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;
mScaleNineVertices[2] = vertice;
vertice.vertices.x = (float) contentWidth;
vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;
mScaleNineVertices[3] = vertice;
// Second Line
vertice.vertices.x = 0;
vertice.vertices.y = (float) (contentHeight - heightFromTop);
vertice.texCoords.u = textureOriginX / textureAtlasWidth;
vertice.texCoords.v = (float) (textureOriginY + heightFromTop) / textureAtlasHeight;
mScaleNineVertices[4] = vertice;
vertice.vertices.x = (float) widthFromLeft;
vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;
mScaleNineVertices[5] = vertice;
vertice.vertices.x = (float) (contentWidth - widthFromRight);
vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;
mScaleNineVertices[6] = vertice;
vertice.vertices.x = (float) contentWidth;
vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;
mScaleNineVertices[7] = vertice;
// Third Line
vertice.vertices.x = 0;
vertice.vertices.y = (float) heightFromBottom;
vertice.texCoords.u = textureOriginX / textureAtlasWidth;
vertice.texCoords.v = (float) (textureOriginY + textureHeight - heightFromBottom) / textureAtlasHeight;
mScaleNineVertices[8] = vertice;
vertice.vertices.x = (float) widthFromLeft;
vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;
mScaleNineVertices[9] = vertice;
vertice.vertices.x = (float) (contentWidth - widthFromRight);
vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;
mScaleNineVertices[10] = vertice;
vertice.vertices.x = (float) contentWidth;
vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;
mScaleNineVertices[11] = vertice;
// Fourth Line
vertice.vertices.x = 0;
vertice.vertices.y = 0;
vertice.texCoords.u = textureOriginX / textureAtlasWidth;
vertice.texCoords.v = (float) (textureOriginY + textureHeight) / textureAtlasHeight;
mScaleNineVertices[12] = vertice;
vertice.vertices.x = (float) widthFromLeft;
vertice.texCoords.u = (float) (textureOriginX + widthFromLeft) / textureAtlasWidth;
mScaleNineVertices[13] = vertice;
vertice.vertices.x = (float) (contentWidth - widthFromRight);
vertice.texCoords.u = (float) (textureOriginX + textureWidth - widthFromRight) / textureAtlasWidth;
mScaleNineVertices[14] = vertice;
vertice.vertices.x = (float) contentWidth;
vertice.texCoords.u = (float) (textureOriginX + textureWidth) / textureAtlasWidth;
mScaleNineVertices[15] = vertice;
}
计算好顶点数据之后,简单修改一下draw()函数就可以了(将之前的m_sQuad替换为mScaleNineVertices)
#define kVertexSize sizeof(ccV3F_C4B_T2F)
if (m_pobTexture)
{
glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());
}
else
{
glBindTexture(GL_TEXTURE_2D, 0);
}
long offset = (long) mScaleNineVertices;
// vertex
int diff = offsetof(ccV3F_C4B_T2F, vertices);
glVertexPointer(3, GL_FLOAT, kVertexSize, (void*)(offset + diff));
// color
diff = offsetof( ccV3F_C4B_T2F, colors);
glColorPointer(4, GL_UNSIGNED_BYTE, kVertexSize, (void*)(offset + diff));
// tex coords
diff = offsetof( ccV3F_C4B_T2F, texCoords);
glTexCoordPointer(2, GL_FLOAT, kVertexSize, (void*)(offset + diff));
glDrawElements(GL_TRIANGLES, 54, GL_UNSIGNED_SHORT, mVerticesIndex);
看起来和之前的差别不大。。。只有两处修改(高亮吧!)
4.Demo
和CCSprite的使用差不太多,只是需要设置一下ContentSize(即展示的尺寸),并且需要设置九宫格切割的比例(以像素为单位,美术比较好理解!)
// Add a Scale Nine Sprite
CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("GreenButton.png");
CCScaleNineSprite* scaleNineSprite = CCScaleNineSprite::scaleNineSpriteWithTexture(texture);
scaleNineSprite->setContentSize(CCSizeMake(200, 100));
scaleNineSprite->CalculateScaleNineVertices(10);
scaleNineSprite->setPosition(CCPointMake(size.width / 2, size.height / 2));
this->addChild(scaleNineSprite);
效果如下:
原资源:
分享到:
相关推荐
Cocos2D-x游戏开发之CCScale9Sprite and CCControlButton
3D ToolKit for cocos2dx 是一个简单的cocos2d-x 3D扩展,可以方便地在cocos2dx环境中创建3D图形。 基于cocos2d-x 2.2开发。 已实现功能: 1,向量和矩阵运算。 2,扩展的3D相机。(支持 透视投影 和 ...
Title: Cocos2d-x by Example: Beginner’s Guide, 2nd Edition Author: Roger Engelbert Length: 250 pages Edition: 1 Language: English Publisher: Packt Publishing Publication Date: 2015-04-30 ISBN-10: ...
cocos2d [1] 是一个基于MIT协议的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。可以让你在创建自己的多平台游戏时节省很多的时间。 Cocos2D也拥有几个主要版本,包括Cocos2D-iPhone、Cocos2D-X,以及被...
cocos2d-x学习笔记(16)--spritesheet(精灵表单).rar
cocos2d是一个基于MIT协议的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。可以让你在创建自己的多平台游戏时节省很多的时间。 Cocos2D也拥有几个主要版本,包括Cocos2D-iPhone、Cocos2D-X,以及被社区...
使用cocos2d-x编写的ui编辑器(界面编辑器),实现了ui的可视化编辑,支持sprite、button、label、scale9sprite等等,导出配置文件格式为json格式-Cocos2d-x prepared ui interface editor, ui visual editing ...
cocos2d-x学习笔记(2)--addSprite.rar
Cocos2d-x is a multi-platform C++ gaming framework in active development maintained by Chukong technologies. It wraps all the essential elements needed for creating a game, making the task of game ...
cocos2d-x的动画资源素材,在cocos2d-x中添加如下代码使用: //动画 //创建一个缓存 auto cache = SpriteFrameCache::getInstance(); //先把图片读入内存 cache->addSpriteFramesWithFile("anim.plist"); ...
Cocos2d-x 本项目用到的功能 Sprite3D Animation3D Mesh Billboard Camera Light New audio engine 更多详情、使用方法,请下载后阅读README.md文件
使用cocos2d-x3.0来给Sprite添加遮罩
本文实践自 Ray Wenderlich、Tony Dahbura 的文章《How to Use Animations and Sprite Sheets in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建一个...
Get a gentle introduction to the Cocos2d-JS framework to begin working with sprite manipulations, animations, and other 2d game development topics. This book covers environment setup and getting ...
cocos2d-x 3.X 接收图片 base64 转码显示,最终图片流显示在 Sprite 上
对于我来说,Cocos2d-x就是一个类库,就类似于MFC、ATL和QT一样,就是一个游戏类库,而我就按照学习类库使用的方法去学习Cocos2d-x。这篇文章叫基础概念,但是还是从整体来说说Cocos2d-x这个类库的。 基础架构 在...
Title: Building Android Games with Cocos2d-x Author: Raydelto Hernandez Length: 147 pages Edition: 1 Language: English Publisher: Packt Publishing Publication Date: 2015-03-27 ISBN-10: 1785283839 ISBN...
Get a gentle introduction to the Cocos2d-JS framework to begin working with sprite manipulations, animations, and other 2d game development topics. This book covers environment setup and getting ...
Debug模式下,需要注释掉 CCGLProgramState.h apply() 方法中 switch语句default分支下的断言 调用方法 CSpriteWithHue *sprite = CCSpriteWithHue::create("HelloWorld....sprite->setHue(4.6);//值在 0 ~ 2 Pi 之间
Get a gentle introduction to the Cocos2d-JS framework to begin working with sprite manipulations, animations, and other 2d game development topics. This book covers environment setup and getting ...