轉自:http://www.cocos2dev.com/?p=295
前段時間看CCEditBox的時候,發現里面有個利用9宮格圖縮放圖片的,也就是縮放帶圓角的圖片。
這個比較有用處,很多游戲中有很多不同尺寸的圓角圖片作為背景。有了CCScale9Sprite之后,只需要提供一個非常小尺寸的圓角圖片就可以自由縮放其他尺寸的圓角圖。是個不錯的東西。
使用方法:
1、導入頭文件及命名空間
#include “cocos-ext.h”
USING_NS_CC_EXT;
2、初始化代碼:
CCScale9Sprite* labBg1 = CCScale9Sprite::create(“wd_bg_text.png”); labBg1->setAnchorPoint(ccp(.5,.5)); labBg1->setPreferredSize(CCSizeMake(255, 20)); labBg1->setPosition(ccp(size.width/2, size.height/2)); addChild(labBg1);
說明:
setPreferredSize 就是設置需要生成的尺寸大小。
看效果:
擴展:轉自:http://blog.csdn.net/onerain88/article/details/8273219
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); } // 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(即展示的尺寸),並且需要設置九宮格切割的比例(以像素為單位,美術比較好理解!)
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);
效果如下:
原資源