Cocos2dx学习笔记(17)——菜单CCMenuItem

菜单可以说是游戏必不可少的一部分,快来看看怎么使用吧。

本文仅供个人记录和复习,不用于其他用途

菜单的作用

不管是开始界面还是游戏设置界面以及等等,都需要设计菜单。菜单可不仅仅就是一张图片,我们点击了按键之后还必须有一定的反应,比如跳转到某个界面等等。

3.x版本改变

  • 取消了menu_selector,采用新的回调函数
  • 各项类的创建方法有所改变,请自行查阅源代码

CCMenu

菜单CCMenu是一个CCLayer,但是子节点只能够存放CCMenuItem以及其子类。换句话说,CCMenu是用来存放菜单按钮的。我们一般先创建CCMenuItem,全部设置好了之后,统一加入到CCMenu,最后将CCMenu加入到当前图层中。

CCMenuItem可以直接加入层中,但是没有办法响应回调事件,因为CCMenu继承自CCLayer,而CCMenuItem继承自CCNode。注意以下几点:

  • CCMenu的锚点为(0, 0),默认原点坐标为屏幕中央
  • CCMenuItem的锚点为(0.5, 0.5),默认原点坐标为(0, 0)

由于CCMenuItem的坐标是相对于CCMenu而言的,因此,CCMenuItem看起来是位于图层中心,实际上位于CCMenu的左下角。所以为了方便,我们一般将CCMenu的位置设置为(0, 0),让它与当前图层重合,方便调整CCMenuItem的位置。

1
CCMenu menu = CCMenu::create(..., NULL);

CCMenu的初始化需要我们传入所有CCMenuItem,并且以NULL结尾来表示参数列表结束。这里的create()使用的是可变参数列表。

部分重要方法如下:

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
67
68
69
70
71
72
73
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是菜单项的父类,一般不直接使用,因为无法显示字体之类的内容。至于功能以下几个:

  • 三个按钮状态:正常、选中、禁用
  • 点击按钮之后,会自动执行回调函数
  • 点击按钮后会放大

菜单项可以分为三个类别:

  • 文字菜单项:CCMenuItemLabelCCMenuItemAtlasFontCCMenuItemFont
  • 图片菜单项:CCMenuItemSpriteCCMenuItemImage
  • 切换菜单项:CCMenuItemToggle

下面我们将一一介绍。

CCMenuItemLabel

看到名字我们就可以知道,CCMenuItemLabel可以存放文字标签:

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
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();
};

CCMenuItemAtlasFont

这个是CCMenuItemLabel的子类,使用CCLabelAtlas来创建文字标签的菜单项按钮,需要提供显示的字符串内容以及.png文件:

1
2
3
4
5
6
7
8
9
10
11
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);
};

CCMenuItemFont

这个同样也是CCMenuItemLabel的子类,使用CCLabelTTF创建菜单项,可以直接设置字符串内容,无需提供字体文件:

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
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();
};

CCMenuItemSprite

顾名思义,这是一个由精灵组成的菜单按钮,内部有三个精灵,分别为正常、选中、禁用,所以我们可以根据三种状态设置不同的图案:

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
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禁用。
};

CCMenuItemImage

这个继承自CCMenuItemSprite,我们无需创建精灵便可以直接创建菜单项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
};

CCMenuItemToggle

此类是一个菜单项按钮对象的集合,能够包含很多的菜单项按钮状态,方便开发者进行切换:

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
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);
};

代码实现

类的实现有些枯燥,我们来实际的创建一个菜单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool T11CCMenu::init()
{
CCLayer::init();
CCMenuItem *fontItem = CCMenuItemFont::create("fontmenu");
fontItem->setTarget(this, menu_selector(T11CCMenu::menuCallBack));
CCMenu *menu = CCMenu::create();
menu->addChild(fontItem);
this->addChild(menu);
return true;
}
void T11CCMenu::menuCallBack()
{
}

这是一个很简单的菜单,我们只加入了一个菜单项,同时也没有对回调函数编写其他的功能。不出意外的话,你可以看到屏幕中间出现fontmenu的字样。但是呢,它的结构其实是这样的:

![](http://obkyr9y96.bkt.clouddn.com/menu.png)

CCMenu的锚点为(0, 0),坐标为层的中央。CCMenuItem的锚点为(0.5, 0.5),坐标为(0, 0)。所以呢,我们就能看到现在的结构。不过为了方便调整菜单项的位置,我们一般将菜单的位置调整为(0, 0),让它与层重合。

1
2
menu->getContentSize().width;
menu->getContentSize().height;

我们可以调用一下子方法,就会发现CCMenuCCLayer是一样大的。

1
2
3
4
CCMenuItem *fontItem2 = CCMenuItemFont::create("fontmenu2");
fontItem2->setTarget(this, menu_selector(T11CCMenu::menuCallBack));
menu->addChild(fontItem2);

我们再加入一个菜单项,会发现两个菜单项的位置重叠在了一起,这时候怎么办呢?

1
menu->alignItemsVertically();

不用手动去调整位置,这样就可以让菜单项上下排列好。

1
menu->alignItemsVerticallyWithPadding(40);

我们还可以改变一下菜单项的上下间隔。