上一篇文章分析了游戏的结构,并做了基本的文档工作。这一篇文章,将完成游戏菜单的创建和显示功能。
本篇文章对应的源代码下载:My_Flight_Control_2010-06-16.zip
添加游戏菜单
启动XCode,新建一个基于“cocos2d Application”模版的新应用。应用名称为“My Flight Control”,后续文章里面就称我们的山寨版Flight Control为MYFC,而原版则称为FC。新建的应用除了显示cocos2d的logo和一个hello,world外,什么功能也没有,我们就在此基础上逐步实现MYFC。
游戏菜单具备几个条目,例如“开始新游戏”、“查看积分榜”、“查看帮助信息”等。除非是极其简单的游戏,否则添加一个游戏菜单是很有必要的。MYFC的菜单很简单,只有“Start Game”和“Scores”两项。
虽然菜单很简单,但我们还是将菜单分为MenuScene和MenuLayer两个对象。MenuScene包含一个菜单背景图和MenuLayer对象。MenuLayer对象则显示菜单项目,并响应玩家对菜单项目的点击。
创建MenuScene和MenuLayer对象
在XCode中新建文件,并选择cocos2d模版“CCNode class”,Subclass则设定为“CCLayer”。文件名输入MenuScene。
新增的文件MenuScene.h和MenuScene.m会出现在Classes群组中:
接下来重复上述步骤,再建立一个MenuLayer对象。
显示菜单背景
iPhone的分辨率是480×320,所以我们要做一个同等尺寸的png图片作为菜单背景图。做好的图片名为MenuBackground.png,拖入XCode中的Resources群组即可。
注意:拖入Resources群组时,如果MenuBackground.png原本没有存放在MYFC的目录中,则导入时需要选中“Copy items into destination group’s folder(is needed)”选项。
有了资源,我们就可以进行编码了。
打开MenuScene.h文件,将:
@interface MenuScene : CCLayer
修改为:
@interface MenuScene : CCScene
然后再打开MenuScene.m文件,加入下面的代码:
02.
{
03.
self = [super init];
04.
if
(self) {
05.
// 取得屏幕大小
06.
CGSize winSize = [[CCDirector sharedDirector] winSize];
07.
// 将 MenuBackground.png 载入一个 CCSprite 对象实例
08.
CCSprite *bg = [CCSprite spriteWithFile:@
"MenuBackground.png"
];
09.
// 设定菜单背景的显示位置
10.
bg.position = ccp(winSize.width / 2, winSize.height / 2);
11.
// 将菜单背景添加到场景中
12.
[self addChild:bg];
13.
}
14.
return
self;
15.
}
CCDirector是cocos2d里面很重要的一个对象。这个对象充当游戏流程控制者的角色,让开发者可以根据需要切换不同的场景。由于CCDirector是一个单子模式实现,所以调用 [CCDirector sharedDirector] 可以获得CCDirector对象的唯一实例。
CCDirector除了控制流程,还有其他一些功能。此处我们用 [winSize] 消息来获得当前屏幕的大小。
接下来,使用CCSprite载入MenuBackground.png图片,并将这个Sprite添加到MenuScene的显示列表(显示列表的概念请参考本文后面部分)中。当MenuScene刷新画面时,MenuBackground.png的内容就会出现在屏幕上。
由于cocos2d载入一个图片后,会将图片的中心点做为原点。所以为了将图片显示在屏幕中央,需要设定图片的position属性。
如上图,CCSprite的中心点默认在图片的中间。所以要将某个图片显示在屏幕中央,代码就是:
sprite.position = ccp(winSize.width / 2, winSize.height / 2);
PS:ccp()是cocos2d定义的一个宏,是CGPointMake()的简写形式。
载入图片并调整好位置后,[self addChild:bg] 将包含背景图的CCSprite对象添加到MenuScene的显示列表中。
测试菜单背景
在编写MenuLayer的代码之前,我们先用MenuScene替换掉默认的hello,world。
打开My_Flight_ControlAppDelegate.m文件,将
#import "HelloWorldScene.h";
替换为:
#import "MenuScene.h";
再找到 applicationDidFinishLaunching 方法最后的
[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];
替换为:
[[CCDirector sharedDirector] runWithScene: [MenuScene node]];
确认无误后,运行模拟器即可看到菜单背景图了:
显示菜单项
菜单背景显示出来后,就该实现菜单项的显示了。这部分工作在MenuLayer对象中完成。这里我们使用cocos2d提供的CCMenuItem和CCMenu辅助类。这两个类可以创建一些简单的菜单,完全可以满足我们目前的需求。
打开MenuLayer.h文件,增加如下代码:
2.
- (
void
) scores: (id)sender;
打开MenuLayer.m文件,增加如下代码:
02.
{
03.
self = [super init];
04.
if
(self) {
05.
// 设定菜单项字体
06.
[CCMenuItemFont setFontName:@
"Helvetica"
];
07.
[CCMenuItemFont setFontSize:30];
08.
09.
// 创建菜单项
10.
CCMenuItem *start = [CCMenuItemFont itemFromString:@
"Start Game"
11.
target:self
12.
selector:@selector(startGame:)];
13.
CCMenuItem *scores = [CCMenuItemFont itemFromString:@
"Scores"
14.
target:self
15.
selector:@selector(scores:)];
16.
// 创建菜单
17.
CCMenu *menu = [CCMenu menuWithItems:start, scores, nil];
18.
// 设定菜单项为垂直对齐
19.
[menu alignItemsVertically];
20.
21.
// 添加菜单到显示列表中
22.
[self addChild:menu];
23.
}
24.
return
self;
25.
}
26.
27.
- (
void
) startGame: (id)sender {
28.
// 开始游戏
29.
}
30.
31.
- (
void
) scores: (id)sender {
32.
// 显示得分记录
33.
}
与添加背景一样的道理,最后需要把创建好的CCMenu对象添加到MenuLayer的显示列表中。
完成了对MenuLayer的修改,我们还要修改MenuScene对象,将MenuLayer包含的菜单项显示在背景之上。
打开MenuScene.m,在头部增加:
#import "MenuLayer.h"
修改init方法,在最后增加:
// 添加菜单层 [self addChild:[MenuLayer node]];
最终MenuScene.m中init方法的代码为:
02.
{
03.
self = [super init];
04.
if
(self) {
05.
// 取得屏幕大小
06.
CGSize winSize = [[CCDirector sharedDirector] winSize];
07.
// 将 MenuBackground.png 载入一个 CCSprite 对象实例
08.
CCSprite *bg = [CCSprite spriteWithFile:@
"MenuBackground.png"
];
09.
// 设定菜单背景的显示位置
10.
bg.position = ccp(winSize.width / 2, winSize.height / 2);
11.
// 将菜单背景添加到场景中
12.
[self addChild:bg];
13.
14.
// 添加菜单层
15.
[self addChild:[MenuLayer node]];
16.
}
17.
return
self;
18.
}
这时再编译运行,就可以看到菜单项了:
点击菜单项,还可以看到菜单项的放大缩小效果。
什么是显示列表?
虽然cocos2d没有明确提到“显示列表”这个概念,但实际上cocos2d是使用类似的技术来决定哪些对象应该显示在屏幕上。
在cocos2d中,所有出现在屏幕上的对象以一个树型结构来组织,例如MenuScene包含背景图和MenuLayer,而MenuLayer又包含几个菜单项目。这个树型结构中每一个节点都会维护一个列表。
例如MenuScene的显示列表中包含了背景图和MenuLayer对象。那么当MenuScene显示在屏幕上时,cocos2d就会把MenuScene显示列表中包含的对象也显示出来。如果MenuScene的显示列表中没有包含MenuLayer,则显示MenuScene时就不会显示MenuLayer对象。
当cocos2d刷新屏幕时,只有在显示列表中的对象会被绘制到屏幕上。因此如果我们有些对象不需要显示,则应该从显示列表中移除。
实质上,切换场景等操作就是对显示列表的调整。例如将场景从MenuScene切换为LeveScene,实际上就是从显示列表中移除MenuScene对象,再加入LevelScene对象。