【嘮叨】
菜單按鈕在遊戲中是經常被用到的,比如主菜單界面的菜單選項,暫停遊戲時的菜單選項等等。cocos2dx引擎同樣爲我們提供了CCMenu菜單的功能,幷包含了一些簡單的菜單項CCMenuItem。且菜單項附帶觸碰按鈕時,自動放大的效果。
溫馨提示:本節內容比較多,需要大家慢慢分析,不要急於求成。
本節組織結構如下:
一、介紹CCMenu。
二、介紹CCMenuItem,及其具體的六個子類。
三、代碼實戰。
【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”按鈕,精靈圖片變成禁用時的圖片。