【唠叨】
菜单按钮在游戏中是经常被用到的,比如主菜单界面的菜单选项,暂停游戏时的菜单选项等等。cocos2dx引擎同样为我们提供了CCMenu菜单的功能,并包含了一些简单的菜单项CCMenuItem。且菜单项附带触碰按钮时,自动放大的效果。
温馨提示:本节内容比较多,需要大家慢慢分析,不要急于求成。
本节组织结构如下:
一、介绍CCMenu。
二、介绍CCMenuItem,及其具体的六个子类。
三、代码实战。
【Demo下载】
【3.x】
(1)去掉“CC”
(2)其他函数的增加与删除稍微做了变化,可以自己在开发过程中掌握。
【菜单CCMenu】
菜单CCMenu是专门用来承载菜单按钮的CCLayer图层,图层中的子节点只能够是菜单项CCMenuItem或其子类。通常先创建菜单项CCMenuItem,然后使用一个或多个菜单项生成菜单CCMenu,最后把CCMenu加入当前CCLayer图层。
继承关系如下:
如果直接在层中添加CCMenuItem也可以正常显示,但是无法响应回调函数,因为CCMenu是继承至CCLayer,也就继承了触摸的相关事件,而CCMenuItem只是从CCNode继承而来,并不响应触摸,因此无法调用回调函数。
由于CCMenu的父类为CCLayer,所以锚点为(0,0),且无法设置锚点。CCMenu的默认原点坐标为屏幕正中心(winSize.width/2, winSize.height/2)。
而对于CCMenuItem是添加在CCMenu层中的,所以CCMenuItem的位置是相对于CCMenu层的偏移位置。CCMenuItem相对于CCMenu的偏移量默认为(0,0),且菜单项的锚点默认为(0.5,0.5)。
如下图所示:
值得注意的是:CCMenu包含了多个子菜单项,每个子菜单项的位置都不一样,如果定义了CCMenu的位置,那它作为父节点会影响到所有的子菜单项的位置,所以一般我们都是吧CCMenu的位置设置在CCPointZero,然后设置CCMenuItem的位置(也就是相对父节点的偏移量)来定位整个菜单。
常用操作如下:
// class CC_DLL CCMenu : public CCLayerRGBA { /** * 创建菜单的三个常用方法 */ //创建一个空菜单 static CCMenu* create(); //CCMenu::create(item1,item2,item3,NULL); //用CCMenuItem菜单项创建菜单,最后以NULL表示结束. static CCMenu* create(CCMenuItem* item, ...); //用一个包含CCMenuItem的CCArray数组来创建菜单 static CCMenu* createWithArray(CCArray* pArrayOfItems); /** * 菜单布局方式 * 注意:使用以下函数进行菜单布局时,将会把整体菜单项的相对于CCMenu的偏移坐标设置到(0,0)。 * 所以布局后,应该设置CCMenu菜单的坐标为屏幕正中心(winSize.width/2, winSize.height/2),效果更加。 * alignItemsVertically , alignItemsHorizontally , * alignItemsInColumns , alignItemsInRows */ //让menu的所有item竖着布局 //item1 //item2 //item3 void alignItemsVertically(); //默认间隙:5个像素 void alignItemsVerticallyWithPadding(float padding); //相连两个item间隔为padding //让menu的所有item横着布局 //item1 item2 item3 void alignItemsHorizontally(); //默认间隙:5个像素 void alignItemsHorizontallyWithPadding(float padding); //相连两个item间隔为padding //将items进行分组,然后按列(columns)进行排列 //每一组在同一行,参数columns表示每一组的菜单项个数,并以NULL表示结束。 //alignItemsInColumns(3,2,1,NULL); //item1 item2 item3 // item4 item5 // item6 void alignItemsInColumns(unsigned int columns, ...); //将items进行分组,然后按行(rows)进行排列。与上述类似。 //alignItemsInRows(3,2,1,NULL); //item1 // item4 //item2 item6 // item5 //item3 void alignItemsInRows(unsigned int rows, ...); /** * 添加、删除item菜单项 * addChild , removeChild */ virtual void addChild(CCNode * child); virtual void addChild(CCNode * child, int zOrder); virtual void addChild(CCNode * child, int zOrder, int tag); virtual void removeChild(CCNode* child, bool cleanup); /** * 设置菜单是否可用 * setEnabled */ virtual void setEnabled(bool value) { m_bEnabled = value; }; virtual bool isEnabled() { return m_bEnabled; } }; //
【菜单项CCMenuItem】
CCMenuItem继承自CCNode,所以它的子类菜单项都可以使用CCNode的相关操作。
CCMenuItem是所有菜单项的父类,建议不要直接使用该类,因为它并不包含具体显示的功能。
作为其它菜单项的父类,主要提供了一下三个功能:
(1)提供了基本按钮的状态:正常、选中、禁用。
(2)为按钮实现了基本的回调函数机制。当玩家点积按钮后,就会调用执行相应的回调函数。
(3)触碰菜单项,附有自动放大效果。
菜单项的子类可以分成三类,总共六个:
(1)文字菜单项:CCMenuItemLabel、CCMenuItemAtlasFont、CCMenuItemFont;
(2)图片菜单项:CCMenuItemSprite、CCMenuItemImage;
(3)切换菜单项:CCMenuItemToggle。
继承关系如下图所示:
1、CCMenuItemLabel
CCMenuItemLabel是一个包含了文字标签的菜单项按钮,CCLabel的三个标签CCLabelBMFont ,CCLabelAtlas,CCLabelTTF对象,都可以放置在该按钮对象中。
常用操作如下:
// class CC_DLL CCMenuItemLabel : public CCMenuItem { /** * 创建CCMenuItemLabel * 支持字体标签类:CCLabelBMFont , CCLabelAtlas , CCLabelTTF */ //用label字体标签创建,不设置回调响应事件。 //label可以是CCLabelBMFont , CCLabelAtlas , CCLabelTTF三种文字标签。 static CCMenuItemLabel* create(CCNode *label); //用label字体标签创建,并设置回调响应事件。 // target:执行当前按钮的对象,一般为this。表示由CCLayer图层执行回调响应事件。 // selector:使用菜单回调函数menu_selector。一般在当前CCLayer图层中定义。 //create( label, this , menu_selector( HelloWorld::func1 ) ); static CCMenuItemLabel * create(CCNode*label, CCObject* target, SEL_MenuHandler selector); /** * 属性设置 * setString , setEnabled , setDisabledColor , setLabel */ //设置内部字体标签(CCLabel)的显示文字内容 void setString(const char * label); //设置该CCMenuItemLabel对象是否可用 virtual void setEnabled(bool enabled); virtual bool isSelected(); //禁用时的颜色 virtual void setDisabledColor(ccColor3B&); virtual const ccColor3B& getDisabledColor(); //被渲染的字体,可以是CCLabelBMFont , CCLabelAtlas , CCLabelTTF。 virtual void setLabel(CCNode*); virtual CCNode* getLabel(); }; //
2、CCMenuItemAtlasFont
CCMenuItemAtlasFont的父类为CCMenuItemLabel。
和父类的区别在于:该类在创建时,只要设置显示内容、使用的字体资源.png即可。它默认使用CCLabelAtlas来创建文字标签的菜单项按钮。而省去了父类创建label的步骤。
该类和父类相比,并未做其他属性或函数的扩展。
常用操作如下:
// class CC_DLL CCMenuItemFont : public CCMenuItemLabel { /** * 创建CCMenuItemFont */ //创建基于CCLabelAtlas字体标签的CCMenuItemAtlasFont,不带回调响应事件。 //create("20140818" , "digit.png" , 20 , 20 , '0' ); //create("20140818" , "digit.png" , 20 , 20 , '0' , this , menu_selector( HelloWorld::func2 ) ); static CCMenuItemAtlasFont* create(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap); static CCMenuItemAtlasFont* create(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap, CCObject* target, SEL_MenuHandler selector); }; //
3、CCMenuItemFont
CCMenuItemAtlasFont的父类为CCMenuItemLabel。
和父类的区别在于:该类在创建时,只要设置显示内容即可。它默认使用CCLabelTTF来创建文字标签的菜单项按钮。而省去了父类创建label的步骤。
和父类相比,可以设置字体大小。
常用操作如下:
// class CC_DLL CCMenuItemFont : public CCMenuItemLabel { /** * 创建CCMenuItemFont */ //create(要显示的字符串) //create(要显示的字符串,执行当前按钮的对象,回调函数) // target:执行当前按钮的对象,一般为this。表示由CCLayer图层执行回调响应事件。 // selector:使用菜单回调函数menu_selector。一般在当前CCLayer图层中定义。 //create( "hello" , this , menu_selector( HelloWorld::func3 ) ); static CCMenuItemFont * create(const char *value); static CCMenuItemFont * create(const char *value, CCObject* target, SEL_MenuHandler selector); /** * 属性设置 */ //这是一个全局静态方法,用来设置新创建CCMenuItemFont时的默认字体大小的 //在不进行设置时,创建的CCMenuItemFont,默认大小为32。 //CCMenuItemFont::setFontSize(32); static void setFontSize(unsigned int s); static unsigned int fontSize(); //这是一个全局静态方法,用来设置新创建CCMenuItemFont时的默认字体资源.fnt的 //在不进行设置时,创建的CCMenuItemFont,默认字体为"Marker Felt"。 //CCMenuItemFont::setFontName("Arial"); static void setFontName(const char *name); static const char *fontName(); //设置该对象的字体大小及使用的字体资源名.fnt void setFontSizeObj(unsigned int s); unsigned int fontSizeObj(); void setFontNameObj(const char* name); const char* fontNameObj(); }; //
4、CCMenuItemSprite
CCMenuItemSprite是一个由精灵对象组成的菜单按钮。
此类的内部属性提供了三个精灵对象,分别表示按钮的三个状态:正常、选中、禁用。每种状态都分别对应了一个精灵图片。
精灵是引擎中最为丰富和自由的元素,因此类CCMenuItemSprite算得上是将精灵和按钮功能的结合体。
常用操作如下:
// class CC_DLL CCMenuItemSprite : public CCMenuItem { /** * 创建CCMenuItemSprite */ //参数: // normalSprite: 正常时的默认精灵normalSprite // selectedSprite:被选中时的精灵selectedSprite // disabledSprite:禁用时的精灵disabledSprite // target:执行当前按钮的对象,一般为this。表示由CCLayer图层执行回调响应事件。 // selector:使用菜单回调函数menu_selector。一般在当前CCLayer图层中定义。 //CCSprite* normalSprite = CCSprite::create("sp1.png"); //CCSprite* selectedSprite = CCSprite::create("sp2.png"); //CCSprite* disabledSprite = CCSprite::create("sp3.png"); //create(normalSprite, selectedSprite, disabledSprite, this, menu_selector(HelloWorld::func4) ); static CCMenuItemSprite * create(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite = NULL); static CCMenuItemSprite * create(CCNode* normalSprite, CCNode* selectedSprite, CCObject* target, SEL_MenuHandler selector); static CCMenuItemSprite * create(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite, CCObject* target, SEL_MenuHandler selector); /** * 设置三种状态的精灵CCSprite */ //正常时的默认图片normalSprite //被选中时的图片selectedSprite //禁用时的图片disabledSprite virtual void setNormalImage(CCNode* normalSprite); virtual CCNode* getNormalImage(); virtual void setSelectedImage(CCNode* selectedSprite); virtual CCNode* getSelectedImage(); virtual void setDisabledImage(CCNode* disabledSprite); virtual CCNode* getDisabledImage(); /** * 设置选中、禁用 */ virtual void selected(); //选中 virtual void unselected(); //取消选中 virtual void setEnabled(bool bEnabled); //是否启用。false禁用。 }; //
5、CCMenuItemImage
CCMenuItemImage继承自CCMenuItemSprite,并没有太大的变化。只是提供了更为简捷的方式,将原本按钮中的精灵对象换为了三张纹理图片。无需创建精灵对象,就可以直接创建一个精灵按钮对象。
与父类相比,省去了创建CCSprite精灵对象的过程。实际上在create创建的过程中,已经帮你做了创建CCSprite的过程了。
常用操作如下:
// class CC_DLL CCMenuItemImage : public CCMenuItemSprite { /** * 创建CCMenuItemImage */ //与CCMenuItemSprite创建方式差不多。就是参数变成了图片资源(如*.png) //create("sp1.png", "sp2.png", "sp3.png", this, menu_selector(HelloWorld::func5) ); static CCMenuItemImage* create(const char *normalImage, const char *selectedImage); static CCMenuItemImage* create(const char *normalImage, const char *selectedImage, const char *disabledImage); static CCMenuItemImage* create(const char *normalImage, const char *selectedImage, CCObject* target, SEL_MenuHandler selector); static CCMenuItemImage* create(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector); /** * 属性设置 */ //用CCSpriteFrame精灵帧,设置正常时的精灵帧Normal void setNormalSpriteFrame(CCSpriteFrame* frame); //用CCSpriteFrame精灵帧,设置选中时的精灵帧Selected void setSelectedSpriteFrame(CCSpriteFrame* frame); //用CCSpriteFrame精灵帧,设置禁用时的精灵帧Disabled void setDisabledSpriteFrame(CCSpriteFrame* frame); }; //
6、CCMenuItemToggle
CCMenuItemToggle是比较特殊的。它在内部拥有一个CCMenuItem菜单项数组,用来负责展示不同的菜单项按钮状态。因为使用了一个菜单按钮的数组,所以此类的对象可以实现状态的切换。此类是一个菜单项按钮对象的集合,能够包含很多的菜单项按钮状态,方便开发者进行切换。
例如,CCMenuItemToggle可以用来做开关按钮。
常用操作如下:
// class CC_DLL CCMenuItemToggle : public CCMenuItem { /** * 创建CCMenuItemToggle * create 或 createWithTarget */ //使用一个菜单项创建CCMenuItemToggle对象 //CCMenuItemFont* item = CCMenuItemFont::create("hello"); //CCMenuItemToggle::create(item); static CCMenuItemToggle* create(CCMenuItem *item); //使用菜单项参数列表创建,以NULL结束列表 //item1 = CCMenuItemFont::create("hello"); //item2 = CCSprite::create("sp1.png"): //createWithTarget(this, menu_selector(HelloWorld::func6), item1, item2, NULL); static CCMenuItemToggle* createWithTarget(CCObject* target, SEL_MenuHandler selector, CCMenuItem* item, ...); //使用包含菜单项的数组创建 //createWithTarget(this, menu_selector(HelloWorld::func6), pArray); static CCMenuItemToggle * createWithTarget(CCObject* target, SEL_MenuHandler selector, CCArray* menuItems); /** * 菜单项数组集合相关 * setSelectedIndex , selectedItem , * setSubItems , addSubItem */ //设置当前选中的CCMenuItem的索引值(即数组下标) virtual void setSelectedIndex(unsigned int ); virtual unsigned int getSelectedIndex(); //返回当前选中的菜单项 CCMenuItem* selectedItem(); //设置CCMenuItem菜单项数组集合 virtual void setSubItems(CCArray* ); virtual CCArray* getSubItems(); //添加新的子菜单项 void addSubItem(CCMenuItem *item); }; //
【代码实战】
首先,在实战的过程中会遇到有关回调函数的概念,这里就简单来说一下什么事回调函数。
回调函数其实就是:当按钮被触碰时,会执行相应的函数。类似于鼠标点击绑定的click响应事件处理函数。
1、在HelloWorldScene.h中添加如下两个回调函数
// //添加回调响应函数 void menuItemFont2Func(CCObject* sender); //更改标签内容 void menuItemToggleFunc(CCObject* sender); //更改状态:正常,选中,禁用 //
2、编写测试代码
// bool HelloWorld::init() { if ( !CCLayer::init() ) { return false; } //获取可视区域尺寸大小 CCSize mysize = CCDirector::sharedDirector()->getVisibleSize(); //获取可视区域的原点位置 CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); //屏幕正中心位置 CCPoint midPos = ccp(mysize.width/2, mysize.height/2); /* * 创建CCMenuItemLabel * 使用CCLabelTTF创建 */ CCLabelTTF* lb1 = CCLabelTTF::create("aaaaaa", "Arial", 32); CCMenuItemLabel* menuItemLabel = CCMenuItemLabel::create(lb1); //设置位置 menuItemLabel->setPosition( ccp(120, mysize.height-50) ); /* * 创建CCMenuItemAtlasFont * 创建方式与CCLabelAtlas类似 */ CCMenuItemAtlasFont* menuItemAtlas = CCMenuItemAtlasFont::create("20140818", "fonts/digit.png", 20, 20, '0'); menuItemAtlas->setPosition( ccp(120, mysize.height-120) ); /* * 创建CCMenuItemFont * 创建了两个,有无附带回调响应函数 */ //设置CCMenuItemFont创建时的默认字体大小 CCMenuItemFont::setFontSize(50); //不带回调响应函数,tag编号为1 CCMenuItemFont* menuItemFont1 = CCMenuItemFont::create("11111"); menuItemFont1->setTag(1); //触碰后,执行回调函数menuItemFont2Func。更改menuItemFont1的内容 CCMenuItemFont* menuItemFont2 = CCMenuItemFont::create("Change1", this, menu_selector(HelloWorld::menuItemFont2Func) ); //回调 menuItemFont2->setFontSizeObj(32); //设置字体大小 menuItemFont1->setPosition( ccp(120, mysize.height-190) ); //设置位置 menuItemFont2->setPosition( ccp(120, mysize.height-260) ); //设置位置 /* * 创建CCMenu,tag编号为100 * 菜单项menuItemLabel, menuItemAtlas, menuItemFont1, menuItemFont2 */ CCMenu* menu = CCMenu::create(menuItemLabel, menuItemAtlas, menuItemFont1, menuItemFont2, NULL); //设置位置为(0,0),与HelloWorld层重合 menu->setPosition(CCPointZero); //将CCMenu菜单添加到CCLayer中, tag编号为100 this->addChild(menu, 0, 100); /* * 创建CCMenuItemSprite * 参数为CCSprite精灵 */ CCSprite* sp1 = CCSprite::create("sp1.png"); CCSprite* sp2 = CCSprite::create("sp2.png"); CCSprite* sp3 = CCSprite::create("sp3.png"); CCMenuItemSprite* menuItemSprite = CCMenuItemSprite::create(sp1, sp2, sp3 ); menuItemSprite->setPosition( ccp(mysize.width/2 + 50, mysize.height/2 + 50) ); menu->addChild(menuItemSprite); //添加到菜单层中 menuItemSprite->setTag(2); //tag编号为2 /* * 创建CCMenuItemImage * 参数变成纹理图片png */ //使用CCMenuItemImage创建一个关闭程序的菜单项按钮 CCMenuItemImage* menuItemImage = CCMenuItemImage::create("CloseNormal.png", "CloseSelected.png", this, menu_selector(HelloWorld::menuCloseCallback) ); //回调 menuItemImage->setPosition( ccp(mysize.width - 40, mysize.height - 40) ); menu->addChild(menuItemImage); //添加到菜单层中 /* * 创建CCMenuItemToggle * 参数为CCMenuItem子类 */ CCMenuItemFont::setFontSize(20); CCMenuItemFont* menuItemFont3 = CCMenuItemFont::create("Toggle_Normal"); CCMenuItemFont* menuItemFont4 = CCMenuItemFont::create("Toggle_Selected"); CCMenuItemImage* menuItemImage2 = CCMenuItemImage::create("sp3.png", "sp1.png"); //创建CCMenuItemToggle,回调函数:更改menuItemSprite的状态。 CCMenuItemToggle* menuItemToggle = CCMenuItemToggle::createWithTarget(this, menu_selector(HelloWorld::menuItemToggleFunc), menuItemFont3, menuItemFont4, NULL ); //菜单项参数列表 menuItemToggle->setPosition( ccp(mysize.width/2 + 50, mysize.height/2 - 50) ); //设置位置 //将menuItemImage2添加到menuItemToggle中 menuItemToggle->addSubItem(menuItemImage2); menu->addChild(menuItemToggle); //添加到菜单层中 return true; } //
3、编写回调响应函数的代码
// /* * 回调函数menuItemFont2Func */ //变化menuItemFont1的内容 void HelloWorld::menuItemFont2Func(CCObject* sender) { //获取menuItemFont2 CCMenuItemFont* menuItemFont2 = (CCMenuItemFont*)sender; //从CCLayer中获取CCMenu菜单 CCMenu* menu = (CCMenu*)this->getChildByTag(100); //获取menuItemFont1,其tag为1 //!!!注意!!! // tag是相对父节点而言的:this的子节点中没有tag为1,而menuItemFont1是menu中tag为1的子节点。 CCMenuItemFont* menuItemFont1 = (CCMenuItemFont*)menu->getChildByTag(1); //更改menuItemFont1的内容 //获取menuItemFont2显示的标签内容 CCLabelTTF* lb = (CCLabelTTF*)menuItemFont2->getLabel(); //strcmp判断是否等于Change1 if( strcmp( lb->getString() , "Change1") == 0 ) { lb->setString("Change2"); menuItemFont1->setString("22222"); }else { lb->setString("Change1"); menuItemFont1->setString("11111"); } } /* * 回调函数menuItemToggleFunc */ //更改状态:正常,选中,禁用 void HelloWorld::menuItemToggleFunc(CCObject* sender) { //获取menuItemToggle CCMenuItemToggle* menuItemToggle = (CCMenuItemToggle*)sender; //从CCLayer中获取CCMenu菜单 CCMenu* menu = (CCMenu*)this->getChildByTag(100); //获取menuItemSprite CCMenuItemSprite* menuItemSprite = (CCMenuItemSprite*)menu->getChildByTag(2); //根据menuItemToggle当前被选中的是哪一项,来设置menuItemSprite的状态 switch( menuItemToggle->getSelectedIndex() ) { case 0: //正常 menuItemSprite->setEnabled(true); break; case 1: //选中 menuItemSprite->selected(); break; case 2: //禁用 menuItemSprite->setEnabled(false); break; } } //
4、运行结果截图
点击Change1按钮,执行回调函数menuItemFont2Func。“11111”变成“22222”。
点击“Toggle_Normal”按钮,精灵图片变成选中时的图片。
再次点击“Toggle_Selected”按钮,精灵图片变成禁用时的图片。