学习cocos2d-x中的菜单主要需要了解:菜单(CCMenu)和菜单项(CCMenuItem)以及CCMenuItem的具体子类。
a. 下面来学习一下相关的类。
1. CCMenu
菜单,是CCLayer的子类,是一个层(容器),可以往里面添加菜单项。下面是它的类结构图:
CCMenu默认接受触屏事件的优先级是-128(优先级很高,因为值越小,响应触屏事件的优先级越高),可以通过继承它实现自定义的效果,创建CCMenu对象的函数:

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的子类,用来渲染文字标签的,可以指定字体,每次设置字符串内容时都需要重新创建纹理和渲染,性能不好,可以看它的相关源码:

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格式的文件(字库),如:

CCLabelBMFont *label = CCLabelBMFont::labelWithString("Bitmap Font Atlas", "fonts/bitmapFontTest.fnt");

这个fnt文件包含了这些信息:对应图片的名字(图片包含了所有你要绘制的字符)、图片中的字符对应的unicode编码、字符在图片中的坐标、宽高等。初始化CCLabelBMFont对象时,会把图片添加到缓存(CCTextureCache)中,解析fnt文件,把fnt文件中对应的信息保存到一个ccBMFontDef类型的数组里面,数组的索引是charId(字符的unicode编码值),ccBMFontDef是一个结构体:

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的部分代码:

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对象的代码如下:

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)中:

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类,分别表示左上,左下,右上,右下。看源码:

//! 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码值的偏移量,得到该字符在图片上的区域的,然后生成绘制矩形所需要的数据,源码:

//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有两个子类CCMenuItemAtlasFontCCMenuItemFont,CCMenuItemAtlasFont是使用CCLabelAtlas创建MenuItemLabel的辅助类,CCMenuItemFont是使用CCLabelTTF创建MenuItemLabel的辅助类。如下源码所示:

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的子类。可以使用三套图片:未选中状态、选中状态、不可用状态,前面两种状态的图片是必需的,不可用状态的图片可选。如下代码所示:

 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。代码示例:

 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. 分析了菜单的各个相关类的原理和用法后,现在来看看如何使用它们,下面示例代码整合了各种菜单项的创建:

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中定义了:

#define kCCItemSize 32
static unsigned int _fontSize = kCCItemSize;
static std::string _fontName = "Marker Felt";

cocos2d-x手游性能优化总结

近段时间在使用cocos2d-x开发2D手游,技术方案使用的是cocos2d-x+lua,因为游戏使用的是cocos2d-x 2.1.5版本,有些优化方案在最新版的cocos2d-x版本已经实现...

阅读全文

cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的。游戏体...

阅读全文

【cocos2d-x开发实战 特训99-终结篇】移植到android平台和添加admob广告

上一篇已经完成特性99在win32平台下的开发,现在把它移植到android上,首先修改Android.mk文件,内容如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_...

阅读全文

7 条评论

  1. “How To Put On Your UGG Classic Short Boots In Style,UGG is termed as snow boots,As the identify indicates, they might be worn in chilly weather,Indeed, these boots are crafted from genuine merino sheepskin,The material is superfunctional in preserving wearers’ ft warm even it really is snowing,On the other hand, UGG is completely over only a title or pattern,Boots from this line are fashionable and universal things,They could also be worn even in sizzling local weather,Specific fiber factors on sheepskin assure feet to breathe extremely effectively,Ft stay completely away from hotness or sweat in sizzling days,As one of the most beloved boots by gals and youngsters, UGG classic short boots develop into ever more favorite above the previous several years,With regards to sporting them, it’s possible the wearer need to be considered a short sensible to complete a fashionable seem with them,Right here follows some solutions,These UGG classic short boots feature a calfheight shafts for the casual appearance,This straightforward but classic design seems to be truly fantastic with any type of jeans,Also it may be worn with pants external of your boot,The delicate and adaptable sheepskin does not only deliver snugly comfort and ease in supreme but additionally permits these boots to become worn cuffed more than to change the height for a distinct type,That enjoyable sheepskin fringe can include a lot more elegance and female to its chic look too,This even may make it a flexible accessory for out search,As sheepskin can be dyed to match whichever the coloring you may consider, the shades for these boots are definitely numerous,The favourite shades tend to be the classics like as chestnut, chocolate, sand, grey and black due to the fact that they can go along with almost everything and also have by far the most flexibility in any wardrobe,However, other shades with brighter and bolder look for them can be excellent to spice up possibly a plain or colorful outfit,And leather coats could be definitely the very best to act in cooperation with these toasty UGGs,Remember, these UGGs are designed in delicate sheepskin and magic fleece lining for being worn barefoot for the snugly comfort in supreme,With a lot of colors accessible in full size, you’ll be able to definitely obtain your fittest between these Classic Short UGG boots and I’ m guaranteed you are going to get rid of your heart to the way they fit and feel,Uggs Outlet are a very versatile piece of footwear,If you are a keen aficionado of the Ugg Boots Classic Short, with a mere clicking of the mouse, you will be able to find these Ugg Boots Outlet sale over the Internet, wherein you get original accessories at crazy low prices,UGG is termed as snow boots,As the identify indicates, they might be worn in chilly weather,Indeed, these boots are crafted from genuine merino sheepskin,The material is superfunctional in preserving wearers’ ft warm even it really is snowing,On the other hand, UGG is completely over only a title or pattern,Boots from this line are fashionable and universal things,They could also be worn even in sizzling local weather,Specific fiber factors on sheepskin assure feet to breathe extremely effectively,Ft stay completely away from hotness or sweat in sizzling days,As one of the most beloved boots by gals and youngsters, UGG classic short boots develop into ever more favorite above the previous several years,With regards to sporting them, it’s possible the wearer need to be considered a short sensible to complete a fashionable seem with them,Right here follows some solutions,These UGG classic short boots feature a calfheight shafts for the casual appearance,This straightforward but classic design seems to be truly fantastic with any type of jeans,Also it may be worn with pants external of your boot,The delicate and adaptable sheepskin does not only deliver snugly comfort and ease in supreme but additionally permits these boots to become worn cuffed more than to change the height for a distinct type,That enjoyable sheepskin fringe can include a lot more elegance and female to its chic look too,This even may make it a flexible accessory for out search,As sheepskin can be dyed to match whichever the coloring you may consider, the shades for these boots are definitely numerous,The favourite shades tend to be the classics like as chestnut, chocolate, sand, grey and black due to the fact that they can go along with almost everything and also have by far the most flexibility in any wardrobe,However, other shades with brighter and bolder look for them can be excellent to spice up possibly a plain or colorful outfit,And leather coats could be definitely the very best to act in cooperation with these toasty UGGs,Remember, these UGGs are designed in delicate sheepskin and magic fleece lining for being worn barefoot for the snugly comfort in supreme,With a lot of colors accessible in full size, you’ll be able to definitely obtain your fittest between these Classic Short UGG boots and I’ m guaranteed you are going to get rid of your heart to the way they fit and feel,Uggs Outlet are a very versatile piece of footwear,If you are a keen aficionado of the Ugg Boots Classic Short, with a mere clicking of the mouse, you will be able to find these Ugg Boots Outlet sale over the Internet, wherein you get original accessories at crazy low prices,”

      1. yayy thanx jury, members of the staff and my dear friends lol….i was hoping for the camera bt agp was better i knew that…congrats to winners and once again yayyy :p

欢迎留言