`
445822357
  • 浏览: 738823 次
文章分类
社区版块
存档分类
最新评论

Cocos2d-x学习(二十二):cocos2d-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: 更多原理可参考 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

    Cocos2D-x游戏开发之CCScale9Sprite and CCControlButton

    cocos2d-x3D扩展3DToolKitforcocos2d-x.zip

    3D ToolKit for cocos2dx 是一个简单的cocos2d-x 3D扩展,可以方便地在cocos2dx环境中创建3D图形。 基于cocos2d-x 2.2开发。 已实现功能: 1,向量和矩阵运算。 2,扩展的3D相机。(支持 透视投影 和 ...

    Cocos2d-x.by.Example.Beginners.Guide.2nd.Edition.1785288857

    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-x游戏开发详细教程

    cocos2d [1] 是一个基于MIT协议的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。可以让你在创建自己的多平台游戏时节省很多的时间。 Cocos2D也拥有几个主要版本,包括Cocos2D-iPhone、Cocos2D-X,以及被...

    cocos2d-x学习笔记(16)--spritesheet(精灵表单).rar

    cocos2d-x学习笔记(16)--spritesheet(精灵表单).rar

    cocos2d-x游戏开发详细教程, 附带超详细cocos2d学习路径图

    cocos2d是一个基于MIT协议的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。可以让你在创建自己的多平台游戏时节省很多的时间。 Cocos2D也拥有几个主要版本,包括Cocos2D-iPhone、Cocos2D-X,以及被社区...

    Cocos2d-x-UIEditor

    使用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学习笔记(2)--addSprite.rar

    Building Android Games with Cocos2d-x(PACKT,2015)

    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的动画资源素材,在cocos2d-x中添加如下代码使用: //动画 //创建一个缓存 auto cache = SpriteFrameCache::getInstance(); //先把图片读入内存 cache-&gt;addSpriteFramesWithFile("anim.plist"); ...

    Cocos2d-x v3.8驱动的 3D样例 游戏_C++_代码_下载

    Cocos2d-x 本项目用到的功能 Sprite3D Animation3D Mesh Billboard Camera Light New audio engine 更多详情、使用方法,请下载后阅读README.md文件

    使用cocos2d-x3.0来给Sprite添加遮罩

    使用cocos2d-x3.0来给Sprite添加遮罩

    AnimBear cocos2d-x

    本文实践自 Ray Wenderlich、Tony Dahbura 的文章《How to Use Animations and Sprite Sheets in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建一个...

    Rapid Game Development Using Cocos2d-JS(Apress,2016)

    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 转码显示

    cocos2d-x 3.X 接收图片 base64 转码显示,最终图片流显示在 Sprite 上

    Cocos2d-x 3.x入门教程(一):基础概念

    对于我来说,Cocos2d-x就是一个类库,就类似于MFC、ATL和QT一样,就是一个游戏类库,而我就按照学习类库使用的方法去学习Cocos2d-x。这篇文章叫基础概念,但是还是从整体来说说Cocos2d-x这个类库的。 基础架构 在...

    Building.Android.Games.with.Cocos2d-x.1785283839

    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...

    Rapid.Game.Development.Using.Cocos2d-JS

    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 ...

    cocos2dx 3.x实现精灵色相的修改

    Debug模式下,需要注释掉 CCGLProgramState.h apply() 方法中 switch语句default分支下的断言 调用方法 CSpriteWithHue *sprite = CCSpriteWithHue::create("HelloWorld....sprite-&gt;setHue(4.6);//值在 0 ~ 2 Pi 之间

    英文原版-Rapid Game Development Using Cocos2dJS 1st Edition

    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 ...

Global site tag (gtag.js) - Google Analytics