不管是多么华丽,多么耐玩的游戏,与用户第一次交互的往往却是菜单.在cocos2d里,提供了专门的CCMenu和CCMenuItem帮助开发这快捷 的实现游戏菜单.并且,借助各种动画效果,可以把游戏菜单做得很酷.
这次,我们依然是从学习cocos2d的官方例子入手.官方的MenuTest例子,提供了4个层来展示各种菜单效果和用户交互的实现.
我们逐个来分析一下关键代码.已经学习cocos2d好几天了,所以有些重复出现的代码,在学习笔记里会省略掉,只分析关键代码.
@implementation AppController
-
(
void
) applicationDidFinishLaunching
:
(UIApplication
*
)application
{
// 以上省略了大量初始化代码
CCScene
*scene
=
[CCScene node
];
// 这里是关键要学习的部分.
// 既往我理解,放在同一个Scene里的多个Layer,同时按不同的z-order显示
// 存在先后显示顺序的Layer,应该放入不同的Scene里显示.
// 而这个问题,在发现CCMultiplexLayer的使用后,才得以真正理解.
// 利用switchTo方法,可以改变激活显示的Layer,在Scene里改变显示的Layer
CCMultiplexLayer
*layer
=
[CCMultiplexLayer layerWithLayers
:
[Layer1 node
],
[Layer2 node
],
[Layer3 node
],
[Layer4 node
],
nil
];
[scene addChild
: layer z
:
0
];
[window makeKeyAndVisible
];
[
[CCDirector sharedDirector
] runWithScene
: scene
];
}
//以下省略了大量代码
@end
@implementation Layer1
-
(
id
) init
{
if
(
(self
=
[super init
]
)
)
{
//设置默认的菜单项字体属性
[CCMenuItemFont setFontSize
:
30
];
[CCMenuItemFont setFontName
:
@
"Courier New"
];
//创建一个菜单项
//这个菜单项完全使用精灵完成.
//与其说是菜单项,不如说是个按钮,它像按钮一样,提供普通状态,点中状态和禁用状态三种不同状 态下需要显示的Sprite
//并且,与UIButton类似的,点击事件通过对target的回调方法来实现.
CCSprite
*spriteNormal
=
[CCSprite spriteWithFile
:
@
"menuitemsprite.png" rect
:CGRectMake
(
0,
23
*
2,
115,
23
)
];
CCSprite
*spriteSelected
=
[CCSprite spriteWithFile
:
@
"menuitemsprite.png" rect
:CGRectMake
(
0,
23
*
1,
115,
23
)
];
CCSprite
*spriteDisabled
=
[CCSprite spriteWithFile
:
@
"menuitemsprite.png" rect
:CGRectMake
(
0,
23
*
0,
115,
23
)
];
CCMenuItemSprite
*item1
=
[CCMenuItemSprite itemFromNormalSprite
:spriteNormal selectedSprite
:spriteSelected disabledSprite
:spriteDisabled target
:self selector
:
@selector
(menuCallback
:
)
];
//创建第二个菜单项.
//这个菜单项与第一个类似,不同之处是直接使用图片完称,比精灵更简洁.
CCMenuItem
*item2
=
[CCMenuItemImage itemFromNormalImage
:
@
"SendScoreButton.png"selectedImage
:
@
"SendScoreButtonPressed.png" target
:self selector
:
@selector
(menuCallback2
:
)
];
//创建第三个菜单项
//这个菜单项使用的是文本Label方式
//值得一提的是,cocos2d提供了超级方便的自定义字库调用方法.
//如下方法中,设置好字库文件,并且以ascii字符排序顺序定义好字库的初始字符,就可以使用 字库创建文本Label.
//以label方式创建的菜单项,会自动以点击放大作为菜单的操作效果来处理.
//如果要求护理特别特殊的点中效果,也可以继承CCMenuItem之后复写selected方 法来实现
CCLabelAtlas
*labelAtlas
=
[CCLabelAtlas labelAtlasWithString
:
@
"0123456789"charMapFile
:
@
"fps_images.png" itemWidth
:
16 itemHeight
:
24 startCharMap
:
'.'
];
CCMenuItemLabel
*item3
=
[CCMenuItemLabel itemWithLabel
:labelAtlas target
:self selector
:
@selector
(menuCallbackDisabled
:
)
];
item3.disabledColor
= ccc3
(
32,
32,
64
);
item3.color
= ccc3
(
200,
200,
255
);
// 创建第四个菜单项,这是一个纯文本的菜单项.
CCMenuItem
*item4
=
[CCMenuItemFont itemFromString
:
@
"I toggle enable items" target
: self selector
:
@selector
(menuCallbackEnable
:
)
];
// 创建第五个菜单项.
// 这也是一个使用文本配合字库文件创建的菜单项.
// 不同的是,这里使用了fnt格式的字库描述文件.关于fnt格式的字库描述文件,我们今后再做详细研究.
CCBitmapFontAtlas
*label
=
[CCBitmapFontAtlas bitmapFontAtlasWithString
:
@
"configuration"fntFile
:
@
"bitmapFontTest3.fnt"
];
CCMenuItemLabel
*item5
=
[CCMenuItemLabel itemWithLabel
:label target
:self selector
:
@selector
(menuCallbackConfig
:
)
];
// 创建第六个菜单项
// 这是一个最普通的文本菜单项,但是它搭配了一个动画效果.文本颜色,会一直变化
CCMenuItemFont
*item6
=
[CCMenuItemFont itemFromString
:
@
"Quit" target
:self selector
:
@selector
(onQuit
:
)
];
id color_action
=
[CCTintBy actionWithDuration
:0.5f red
:
0 green
:-
255 blue
:-
255
];
id color_back
=
[color_action reverse
];
id seq
=
[CCSequence actions
:color_action, color_back,
nil
];
[item6 runAction
:
[CCRepeatForever actionWithAction
:seq
]
];
// 使用上述所有菜单项,构造菜单.
CCMenu
*menu
=
[CCMenu menuWithItems
: item1, item2, item3, item4, item5, item6,
nil
];
// 将菜单居中对齐
[menu alignItemsVertically
];
// 给菜单制作一个展现效果.
// 以下的算法,实现了菜单项交叉从左右飞入的效果,还是很酷滴.
// 关于cocos2d各种动画效果的使用,我觉得有必要单独开一篇学习笔记来学习,今天就先略过吧.
CGSize s
=
[
[CCDirector sharedDirector
] winSize
];
int i
=
0;
for
( CCNode
*child
in
[menu children
]
)
{
CGPoint dstPoint
= child.position;
int offset
= s.width
/
2
+
50;
if
( i
%
2
==
0
)
offset
=
-offset;
child.position
= ccp
( dstPoint.x
+ offset, dstPoint.y
);
[child runAction
:
[CCEaseElasticOut actionWithAction
:
[CCMoveBy actionWithDuration
:
2 position
:ccp
(dstPoint.x
- offset,
0
)
]
period
: 0.35f
]
];
i
++;
}
//将第三个菜单项设为禁用,用于演示toggle菜单项的功能
disabledItem
=
[item3 retain
];
disabledItem.isEnabled
=
NO;
[self addChild
: menu
];
}
return self;
}
-
(
void
) menuCallback
:
(
id
) sender
{
//点击菜单项时,将激活Layer切换为另外一个,相当于进入2级菜单
[
(CCMultiplexLayer
*
)parent_ switchTo
:
1
];
}
-
(
void
) menuCallbackEnable
:
(
id
) sender
{
//点击菜单项时,将某个菜单项禁用
disabledItem.isEnabled
= ~disabledItem.isEnabled;
}
-
(
void
) onQuit
:
(
id
) sender
{
//点击退出按钮的处理
//在SDK 3.0以上,苹果不允许应用程序自己执行退出.所以,应用程序其实不应该有自己的Quit菜单项.
// http://developer.apple.com/iphone/library/qa/qa2008/qa1561.html
[
[CCDirector sharedDirector
] end
];
if
(
[
[UIApplication sharedApplication
] respondsToSelector
:
@selector
(terminate
)
]
)
[
[UIApplication sharedApplication
] performSelector
:
@selector
(terminate
)
];
else
NSLog
(
@
"YOU CAN'T TERMINATE YOUR APPLICATION PROGRAMATICALLY in SDK 3.0+"
);
}
//省略了其他代码.
@end
/第四个层演示了比较经典的设置界面.主要通过CCMenuItemToggle来实现
@implementation Layer4
-(id) init
{
[super init];
//创建一组开关菜单项,简单地说,就是on/of
//这组菜单项由两部分组成
//title是显示用的文字,不可以接受用户点击
[CCMenuItemFont setFontName: @"American Typewriter"];
[CCMenuItemFont setFontSize:18];
CCMenuItemFont *title1 = [CCMenuItemFont itemFromString: @"Sound"];
[title1 setIsEnabled:NO];
//接下来的是可以接受用户点击的菜单项
//这个菜单项提供了两个item,在call时,可以根据选中的index切换显示
[CCMenuItemFont setFontName: @"Marker Felt"];
[CCMenuItemFont setFontSize:34];
CCMenuItemToggle *item1 = [CCMenuItemToggle itemWithTarget:self selector:@selector(menuCallback:) items:
[CCMenuItemFont itemFromString: @"On"],
[CCMenuItemFont itemFromString: @"Off"],
nil];
//与上一个菜单项项相同,省略了两组共四个菜单项的代码
//这个菜单项与上面的基本相同
[CCMenuItemFont setFontName: @"American Typewriter"];
[CCMenuItemFont setFontSize:18];
CCMenuItemFont *title4 = [CCMenuItemFont itemFromString: @"Orientation"];
[title4 setIsEnabled:NO];
[CCMenuItemFont setFontName: @"Marker Felt"];
[CCMenuItemFont setFontSize:34];
CCMenuItemToggle *item4 = [CCMenuItemToggle itemWithTarget:self selector:@selector(menuCallback:) items:
[CCMenuItemFont itemFromString: @"Off"], nil];
//不同的是,这个菜单项使用了subItems的addObjectsFromArray方法来设置更多 的可选项.
NSArray *more_items = [NSArray arrayWithObjects:
[CCMenuItemFont itemFromString: @"33%"],
[CCMenuItemFont itemFromString: @"66%"],
[CCMenuItemFont itemFromString: @"100%"],
nil];
[item4.subItems addObjectsFromArray: more_items];
item4.selectedIndex = 2;//设置一个初选中的项目
// 设置第五组菜单项
[CCMenuItemFont setFontName: @"Marker Felt"];
[CCMenuItemFont setFontSize:34];
CCBitmapFontAtlas *label = [CCBitmapFontAtlas bitmapFontAtlasWithString:@"go back"fntFile:@"bitmapFontTest3.fnt"];
CCMenuItemLabel *back = [CCMenuItemLabel itemWithLabel:label target:self selector:@selector(backCallback:)];
//构建菜单
CCMenu *menu = [CCMenu menuWithItems:
title1, title2,
item1, item2,
title3, title4,
item3, item4,
back, nil]; //共五组九个菜单项.
//对齐菜单项
//这是一个比较独特的对齐方法,即让菜单项目以分列方式居中对齐.
//第一组两列,会让前两个菜单项,将屏幕分为两列,在每列内居中.
//最后设置为了列,使一个菜单项单独占据一行
[menu alignItemsInColumns:
[NSNumber numberWithUnsignedInt:2],
[NSNumber numberWithUnsignedInt:2],
[NSNumber numberWithUnsignedInt:2],
[NSNumber numberWithUnsignedInt:2],
[NSNumber numberWithUnsignedInt:1],
nil
]; // 2 + 2 + 2 + 2 + 1 = 共9个.
[self addChild: menu];
return self;
}
-(void) menuCallback: (id) sender
{
//选中菜单项的回调,切换显示选中项目
NSLog(@"selected item: %@ index:%d", [sender selectedItem], [sender selectedIndex] );
}
//省略部分代码
@end