1. CCMenu
菜單,是CCLayer的子類,是一個層(容器),可以往裏面添加菜單項。下面是它的類結構圖:
CCMenu默認接受觸屏事件的優先級是-128(優先級很高,因爲值越小,響應觸屏事件的優先級越高),可以通過繼承它實現自定義的效果,創建CCMenu對象的函數:
1
2
|
static
CCMenu* menuWithItems(CCMenuItem* item, ...); static
CCMenu* menuWithItem(CCMenuItem* item); |
2. CCMenuItem
菜單項,開發中一般是直接使用它的子類。CCMenuItem有三個直接子類:
CCMenuItemLabel(字符標籤菜單)、CCMenuItemSprite(圖片菜單)、CCMenuItemToggle(開關菜單)。
下面是CCMenuItem的類結構圖:
現在分別來了解一下各個不同的菜單項。
(1) CCMenuItemLabel:使用文字標籤創建菜單項
所有支持CCLabelProtocol的節點都可以用來創建CCMenuItemLabel,CCLabelProtocol是標籤的共同接口。CCLabelProtocol也有三個直接子類,下面是類結構圖:
CCLabelTTF:同時也是CCSprite的子類,用來渲染文字標籤的,可以指定字體,每次設置字符串內容時都需要重新創建紋理和渲染,性能不好,可以看它的相關源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
void
CCLabelTTF::setString( const
char *label) { if
(m_pString) {
delete m_pString;
m_pString = NULL; } m_pString =
new std::string(label); CCTexture2D *texture; if ( CCSize::CCSizeEqualToSize( m_tDimensions, CCSizeZero ) ) {
texture = new
CCTexture2D();
texture->initWithString(label, m_pFontName->c_str(), m_fFontSize); } else {
texture = new
CCTexture2D();
texture->initWithString(label, m_tDimensions, m_eAlignment, m_pFontName->c_str(), m_fFontSize); } this ->setTexture(texture); texture->release(); CCRect rect = CCRectZero; rect.size = m_pobTexture->getContentSize(); this ->setTextureRect(rect); } |
可以用CCLabelBMFont或者CCLabelAtlas代替它。
CCLabelBMFont:也是CCSpriteBatchNode的子類,創建CCLabelBMFont對象需要一個字符串和一個fnt格式的文件(字庫),如:
1
|
CCLabelBMFont *label = CCLabelBMFont::labelWithString( "Bitmap Font Atlas" ,
"fonts/bitmapFontTest.fnt" ); |
這個fnt文件包含了這些信息:對應圖片的名字(圖片包含了所有你要繪製的字符)、圖片中的字符對應的unicode編碼、字符在圖片中的座標、寬高等。初始化CCLabelBMFont對象時,會把圖片添加到緩存(CCTextureCache)中,解析fnt文件,把fnt文件中對應的信息保存到一個ccBMFontDef類型的數組裏面,數組的索引是charId(字符的unicode編碼值),ccBMFontDef是一個結構體:
1
2
3
4
5
6
7
8
9
10
11
12
|
typedef
struct _BMFontDef { //! ID of the character unsigned
int charID; //! origin and size of the font CCRect rect; //! The X amount the image should be offset when drawing the image (in pixels) int
xOffset; //! The Y amount the image should be offset when drawing the image (in pixels) int
yOffset; //! The amount to move the current position after drawing the character (in pixels) int
xAdvance; } ccBMFontDef; |
繪製字符串時,根據字符對應的unicode碼去查找ccBMFontDef信息,從緩存中取出圖片,再根據ccBMFontDef中座標、寬高取出對應區域的字符圖片,把字符在字符串中的索引位置作爲tag添加到CCLabelBMFont中,因爲CCLabelBMFont本身是CCSpriteBatchNode,這樣就實現了批處理渲染精靈,提高了性能。下面是創建字符對應的CCSprite的部分代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
void
CCLabelBMFont::createFontChars() { /** .... */ //以下代碼是遍歷字符串時:for循環內的代碼 const
ccBMFontDef& fontDef = (*(m_pConfiguration->m_pBitmapFontArray))[c]; CCRect rect = fontDef.rect; CCSprite *fontChar; fontChar = (CCSprite*)( this ->getChildByTag(i)); if ( ! fontChar ) { fontChar =
new CCSprite(); fontChar->initWithBatchNodeRectInPixels( this , rect); this ->addChild(fontChar, 0, i); fontChar->release(); } else { // reusing fonts fontChar->setTextureRectInPixels(rect,
false , rect.size); // restore to default in case they were modified fontChar->setIsVisible( true ); fontChar->setOpacity(255); } /** .... */ } |
CCLabelAtlas:也是CCAtlasNode的子類,創建一個CCLabelAtlas對象的代碼如下:
1
2
3
|
static
CCLabelAtlas * labelWithString( const
char *label,
const char
*charMapFile, unsigned int
itemWidth, unsigned int
itemHeight, unsigned char
startCharMap); //示例 CCLabelAtlas* label1 = CCLabelAtlas::labelWithString( "123 Test" ,
"fonts/tuffy_bold_italic-charmap.png" , 48, 64,
' ' ); |
參數的含義:要繪製的字符,圖片文件,圖片文件中每個字符的寬度,圖片文件中每個字符的高度,圖片的起始字符。
CCAtlasNode封裝了一個CCTextureAtlas的變量,CCTextureAtlas初始化圖片文件的時候會把圖片加載到緩存(CCTextureCache)中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
bool
CCTextureAtlas::initWithFile( const
char * file, unsigned int
capacity) { // retained in property CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(file); if
(texture) { return
initWithTexture(texture, capacity); } else { CCLOG( "cocos2d: Could not open file: %s" , file); delete
this ; return
NULL; } } |
接下來CCTextureAtlas負責管理該大圖,可以隨意繪製圖片的某一矩形區域,渲染方式採用的是OpenGL ES VBO(頂點緩衝對象,保存在顯存中)。 CCTextureAtlas有一個m_pQuads屬性,它是CCTextureAtlas類的核心,是一個ccV3F_C4B_T2F_Quad類型的數組,ccV3F_C4B_T2F_Quad是一個結構體,有四個成員屬性,它們都是ccV3F_C4B_T2F類,分別表示左上,左下,右上,右下。看源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//! 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; //! 4 ccVertex2FTex2FColor4B Quad typedef
struct _ccV2F_C4B_T2F_Quad { //! bottom left ccV2F_C4B_T2F bl; //! bottom right ccV2F_C4B_T2F br; //! top left ccV2F_C4B_T2F tl; //! top right ccV2F_C4B_T2F tr; } ccV2F_C4B_T2F_Quad; |
ccV3F_C4B_T2F有三個成員,分別表示:頂點、顏色、紋理座標。
CCTextureAtlas類就是根據這個數組來繪製矩形的,數組的容量就是要繪製的字符數量。指定字符串的時候:是根據指定字符的ASCII碼值跟startCharMap(圖片起始字符)ASCII碼值的偏移量,得到該字符在圖片上的區域的,然後生成繪製矩形所需要的數據,源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
//CCLabelAtlas - CCLabelProtocol void
CCLabelAtlas::setString( const
char *label) { /** .... */ this ->updateAtlasValues(); /** .... */ } //CCLabelAtlas - Atlas generation void
CCLabelAtlas::updateAtlasValues() { unsigned
int n = m_sString.length(); ccV3F_C4B_T2F_Quad quad; const
unsigned char
*s = (unsigned char *)m_sString.c_str(); CCTexture2D *texture = m_pTextureAtlas->getTexture(); float
textureWide = ( float ) texture->getPixelsWide(); float
textureHigh = ( float ) texture->getPixelsHigh(); for (unsigned
int i = 0; i < n; i++) {
unsigned char
a = s[i] - m_cMapStartChar;
float row = ( float ) (a % m_uItemsPerRow);
float col = ( float ) (a / m_uItemsPerRow); #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
// Issue #938. Don't use texStepX & texStepY
float left = (2 * row * m_uItemWidth + 1) / (2 * textureWide);
float right = left + (m_uItemWidth * 2 - 2) / (2 * textureWide);
float top = (2 * col * m_uItemHeight + 1) / (2 * textureHigh);
float bottom = top + (m_uItemHeight * 2 - 2) / (2 * textureHigh); #else
float left = row * m_uItemWidth / textureWide;
float right = left + m_uItemWidth / textureWide;
float top = col * m_uItemHeight / textureHigh;
float bottom = top + m_uItemHeight / textureHigh; #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
quad.tl.texCoords.u = left;
quad.tl.texCoords.v = top;
quad.tr.texCoords.u = right;
quad.tr.texCoords.v = top;
quad.bl.texCoords.u = left;
quad.bl.texCoords.v = bottom;
quad.br.texCoords.u = right;
quad.br.texCoords.v = bottom;
quad.bl.vertices.x = ( float ) (i * m_uItemWidth);
quad.bl.vertices.y = 0;
quad.bl.vertices.z = 0.0f;
quad.br.vertices.x = ( float )(i * m_uItemWidth + m_uItemWidth);
quad.br.vertices.y = 0;
quad.br.vertices.z = 0.0f;
quad.tl.vertices.x = ( float )(i * m_uItemWidth);
quad.tl.vertices.y = ( float )(m_uItemHeight);
quad.tl.vertices.z = 0.0f;
quad.tr.vertices.x = ( float )(i * m_uItemWidth + m_uItemWidth);
quad.tr.vertices.y = ( float )(m_uItemHeight);
quad.tr.vertices.z = 0.0f;
m_pTextureAtlas->updateQuad(&quad, i); } } |
所以圖片上的字符排列順序要按照ASCII碼錶的順序連續排列。CCLabelAtlas的繪製效率高,但是限制性太多,沒有CCLabelBMFont靈活。
從類結構圖可以看到CCMenuItemLabel有兩個子類CCMenuItemAtlasFont和CCMenuItemFont,CCMenuItemAtlasFont是使用CCLabelAtlas創建MenuItemLabel的輔助類,CCMenuItemFont是使用CCLabelTTF創建MenuItemLabel的輔助類。如下源碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
bool
CCMenuItemAtlasFont::initFromString( const
char *value,
const char
*charMapFile, int
itemWidth, int
itemHeight, char
startCharMap, CCObject* target, SEL_MenuHandler selector) { CCAssert( value != NULL &&
strlen (value) != 0,
"value length must be greater than 0" ); CCLabelAtlas *label =
new CCLabelAtlas(); label->initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap); label->autorelease(); if
(CCMenuItemLabel::initWithLabel(label, target, selector)) {
// do something ? } return
true ; }
bool
CCMenuItemFont::initFromString( const
char *value, CCObject* target, SEL_MenuHandler selector) { CCAssert( value != NULL &&
strlen (value) != 0,
"Value length must be greater than 0" ); m_strFontName = _fontName; m_uFontSize = _fontSize; CCLabelTTF *label = CCLabelTTF::labelWithString(value, m_strFontName.c_str(), ( float )m_uFontSize); if
(CCMenuItemLabel::initWithLabel(label, target, selector)) {
// do something ? } return
true ; } |
2. CCMenuItemSprite和CCMenuItemImage:本質上都是使用圖片創建菜單項,前者是使用精靈對象創建,後者使用圖片名稱創建,CCMenuItemImage是CCMenuItemSprite的子類。可以使用三套圖片:未選中狀態、選中狀態、不可用狀態,前面兩種狀態的圖片是必需的,不可用狀態的圖片可選。如下代碼所示:
1
2
3
4
|
static
CCMenuItemSprite * itemFromNormalSprite(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite = NULL); static
CCMenuItemImage* itemFromNormalImage( const
char *normalImage,
const char
*selectedImage);
static
CCMenuItemImage* itemFromNormalImage( const
char *normalImage,
const char
*selectedImage, const
char *disabledImage); |
3. CCMenuItemToggle: 開關菜單
它是一個容器,可以切換包含的子項(可以是任何的MenuItem對象)。它封裝了一個CCMutableArray<CCMenuItem*>*類型的屬性m_pSubItems。代碼示例:
1
2
3
4
5
|
static
CCMenuItemToggle* itemWithTarget(CCObject* target, SEL_MenuHandler selector, CCMenuItem* item, ...);
CCMenuItemToggle* item1 = CCMenuItemToggle::itemWithTarget( this ,menu_selector(MenuLayer4::menuCallback),
CCMenuItemFont::itemFromString(
"On" ), CCMenuItemFont::itemFromString(
"Off" ),NULL ); |
b. 分析了菜單的各個相關類的原理和用法後,現在來看看如何使用它們,下面示例代碼整合了各種菜單項的創建:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
void
MenuLayer::onEnter() { CCLayer::onEnter(); CCSize winSize = CCDirector::sharedDirector()->getWinSize(); /**---CCMenuItemLabel:由指定的字符串標籤創建菜單--**/ //CCMenuItemFont:內部使用CCLabelTTF CCMenuItemFont::setFontName( "Arial" ); CCMenuItemFont::setFontSize(22); CCMenuItemFont* pFontMenuItem = CCMenuItemFont::itemFromString( "font item" ,
this , menu_selector(MenuLayer::menuCallback)); CCMenu* pFontMenu = CCMenu::menuWithItems(pFontMenuItem,NULL); pFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 30) ); this ->addChild(pFontMenu); //CCMenuItemAtlasFont:內部使用CCLabelAtlas CCMenuItemAtlasFont* pAtlasFontMenuItem = CCMenuItemAtlasFont::itemFromString( "123456789" , s_imgPathNum,
15, 19, '0' ,
this , menu_selector(MenuLayer::menuCallback)); CCMenu* pAtlasFontMenu = CCMenu::menuWithItems(pAtlasFontMenuItem,NULL); pAtlasFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 60) ); this ->addChild(pAtlasFontMenu); //CCLabelBMFont CCLabelBMFont* pBMFontLabel = CCLabelBMFont::labelWithString( "configuration" , s_imgPathBMFont); CCMenuItemLabel* pItemBMFontLabel = CCMenuItemLabel::itemWithLabel(pBMFontLabel,
this , menu_selector(MenuLayer::menuCallback)); CCMenu* pBMFontMenu = CCMenu::menuWithItems(pItemBMFontLabel,NULL); pBMFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 90) ); this ->addChild(pBMFontMenu); /**--CCMenuItemSprite:由指定的精靈類創建菜單--**/ CCSprite* spriteNormal = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*2,115,23)); CCSprite* spriteSelected = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*1,115,23)); CCSprite* spriteDisabled = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*0,115,23)); CCMenuItemSprite* pMenuItemSprite = CCMenuItemSprite::itemFromNormalSprite(spriteNormal, spriteSelected, spriteDisabled,
this , menu_selector(MenuLayer::menuCallback)); CCMenu* pSpriteMenu = CCMenu::menuWithItems(pMenuItemSprite,NULL); pSpriteMenu->setPosition( ccp(winSize.width/2,winSize.height - 120) ); this ->addChild(pSpriteMenu); //CCMenuItemImage:由指定的圖片文件名創建菜單 CCMenuItemImage* pMenuItemImage = CCMenuItemImage::itemFromNormalImage(s_imgPathCloseNormal, s_imgPathCloseSelected,
this , menu_selector(MenuLayer::menuCallback) ); CCMenu* pImageMenu = CCMenu::menuWithItem(pMenuItemImage); pImageMenu->setPosition( ccp(winSize.width/2,winSize.height - 150) ); this ->addChild(pImageMenu); //CCMenuItemToggle:開關菜單,切換效果 //這裏只使用了CCMenuItemFont,還可以使用其他的CCMenuItem CCMenuItemToggle* pMenuItemToggle = CCMenuItemToggle::itemWithTarget( this , menu_selector(MenuLayer::menuCallback),
CCMenuItemFont::itemFromString( "On"
),
CCMenuItemFont::itemFromString( "Off" ),
NULL ); CCMenu* pToggleMenu = CCMenu::menuWithItems(pMenuItemToggle,NULL); pToggleMenu->setPosition( ccp(winSize.width/2,winSize.height - 180) ); this ->addChild(pToggleMenu); } |
運行效果如下:
程序使用的圖片素材:
CCLabelBMFont代碼段使用的素材是:cocos2d-x安裝目錄/tests/Resources/fonts/bitmapFontTest3.fnt和對應的png文件
ps:CCMenuItem默認使用的字體是Marker Felt,字體大小是32,在CCMenuItem.h中定義了:
1
2
3
|
#define kCCItemSize 32 static
unsigned int
_fontSize = kCCItemSize; static
std::string _fontName = "Marker Felt" ;
轉載請註明來自:Alex Zhou的程序世界,本文鏈接:http://codingnow.cn/cocos2d-x/832.html |