本节书摘来异步社区《Cocos2d 跨平台游戏开发指南(第2版)》一书中的第2章,第2.8节,作者: 【印度】Siddharth Shekar(谢卡)译者: 武传海 责编: 胡俊英,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.8 添加难度选择场景
在本部分中,我们将学习如何添加难度选择场景,其中包含多种难度选择按钮,当你按下某个按钮时,相应难度水平的游戏就会被加载进来。
2.8.1 准备工作
为了创建难度级别选择场景,你需要一个自定义精灵,用来显示按钮背景图片以及表示难度级别的数字。首先,我们要创建这些按钮。
在创建好按钮精灵之后,接下来我们要创建一个新场景,用来存放背景图像、场景名称、按钮数组,以及变换场景到指定游戏难度的逻辑。
2.8.2 操作步骤
首先,我们创建一个新的 Cocoa Touch 类,命名为LevelSelectionBtn,它以CCSprite类作为父类。
然后,打开LevelSelectionBtn.h文件,在其中添加如下代码:
#import "CCSprite.h"
@interface LevelSelectionBtn : CCSprite
-(id)initWithFilename:(NSString *) filename
StartlevelNumber:(int)lvlNum;
@end
上面代码中,我们创建了一个自定义的init函数,它带有两个参数,一个是图像文件名,用来指定按钮背景图像,另一个是整数,显示在按钮背景图像之上,表示难度级别。
这就是LevelSelectionBtn.h文件中的所有代码。在LevelSelectionBtn.m文件中,添加如下代码:
#import "LevelSelectionBtn.h"
@implementation LevelSelectionBtn
-(id)initWithFilename:(NSString *) filename StartlevelNumber:
(int)lvlNum;
{
if (self = [super initWithImageNamed:filename]) {
CCLOG(@"Filename: %@ and levelNUmber: %d",
filename,
lvlNum);
CCLabelTTF *textLabel =
[CCLabelTTF labelWithString:[NSString
stringWithFormat:@"%d",lvlNum ]
fontName:@"AmericanTypewriter-Bold"
fontSize: 12.0f];
textLabel.position =
ccp(self.contentSize.width / 2,
self.contentSize.height / 2);
textLabel.color = [CCColor colorWithRed:0.1f
green:0.45f
blue:0.73f];
[self addChild:textLabel];
}
return self;
}
@end
在自定义的init函数中,首先把参数传入的文件名与游戏难度级别数字在控制台中输出,而后创建一个文本标签,在把整数转换为字符串之后传递给它。
随后,把文本标签放置到当前精灵背景图像的中央,通过把图像的宽度与高度分别除以2得到图像中心点坐标。
由于图像背景与文本都是白色,所以需要把文本颜色修改为蓝色,以便把文本显示出来。
最后,我们把文本添加到当前的类中。
以上就是LevelSelectionBtn类的所有代码。接下来,我们将创建LevelSelectionScene类,并向其中添加精灵按钮与按下按钮所要执行的逻辑。
创建好LevelSelectionScene类之后,在头文件(LevelSelectionScene.h)中添加如下代码:
#import "CCScene.h"
@interface LevelSelectionScene : CCScene{
NSMutableArray *buttonSpritesArray;
}
+(CCScene*)scene;
@end
请注意,在LevelSelectionScene.h中,除了常见代码之外,我们还创建了一个NSMutableArray类型的变量buttonsSpritesArray,在后面的代码中将会用到它。
接着,在LevelSelectionScene.m文件中,添加如下代码:
#import "LevelSelectionScene.h"
#import "LevelSelectionBtn.h"
#import "GameplayScene.h"
@implementation LevelSelectionScene
+(CCScene*)scene{
return[[self alloc]init];
}
-(id)init{
if(self = [super init]){
CGSize winSize = [[CCDirector sharedDirector]viewSize];
//Add Background Image
CCSprite* backgroundImage = [CCSprite spriteWithImageNamed:@
"Bg.png"];
backgroundImage.position = CGPointMake(winSize.width/2,
winSize.height/2);
[self addChild:backgroundImage];
//add text heading for file
CCLabelTTF *mainmenuLabel = [CCLabelTTF labelWithString:@
"LevelSelectionScene" fontName:@"AmericanTypewriter-Bold"
fontSize:
36.0f];
mainmenuLabel.position = CGPointMake(winSize.width/2, winSize.
height
* 0.8);
[self addChild:mainmenuLabel];
//initialize array
buttonSpritesArray = [NSMutableArray array];
int widthCount = 5;
int heightCount = 5;
float spacing = 35.0f;
float halfWidth =
winSize.width/2 - (widthCount-1) * spacing * 0.5f;
float halfHeight =
winSize.height/2 + (heightCount-1) * spacing * 0.5f;
int levelNum = 1;
for(int i = 0; i < heightCount; ++i){
float y = halfHeight - i * spacing;
for(int j = 0; j < widthCount; ++j){
float x = halfWidth + j * spacing;
LevelSelectionBtn* lvlBtn =
[[LevelSelectionBtnalloc]
initWithFilename:@"btnBG.png"
StartlevelNumber:levelNum];
lvlBtn.position = CGPointMake(x,y);
lvlBtn.name =
[NSString stringWithFormat:@"%d",levelNum];
[self addChild:lvlBtn];
[buttonSpritesArray addObject: lvlBtn];
levelNum++;
}
}
}
return self;
}
在上述代码中,我们先添加了背景图像、场景标题文本,并对NSMutableArray进行了初始化。
然后,我们创建了6个变量,如下所示(见图2-10)。
- widthCount:列数。
- heightCount:行数。
- spacing:精灵按钮之间的距离,防止它们重叠在一起。
- halfWidth:指从屏幕中心到第一个精灵按钮左上角的距离在x轴上的投影而得到的距离。
- halfHeight:指从屏幕中心到第一个精灵按钮左上角的距离在y轴上的投影而得到的距离。
- lvlNum:指显示在精灵按钮上表示难度级别的数字,默认值为1,每次创建一个按钮,其值就会增加1。
在双重循环中,我们将获取每个按钮精灵的x与y坐标。首先,为了获取y值,我们要用halfHeight减去spacing与循环变量i的乘积。由于i的初始值为0,所以最顶行的y值为halfHeigh。
随后,计算按钮位置的x值,计算时我们使用halfWidth加上spacing与j的乘积。每次x值都会被spacing增大。
在获取x与y值之后,创建一个新的LevelSelectionBtn精灵,传入btnBG.png图像,以及lvlNum值,生成按钮精灵。
然后,把之前计算得到的x与y值赋给按钮精灵的position属性。
为了通过数字引用按钮,我们先把levelNum转换为字符串,而后将其赋给按钮精灵的name属性,也就是说引用按钮的数字与其上显示的代表难度级别的数字是一致的。
接下来,把按钮添加到场景之中,同时按钮也会被添加到之前创建的全局按钮精灵数组之中,这是因为后面我们需要对这些图像进行循环。
最后,把levelNum值增加1。
然而,目前我们还没有向精灵按钮添加任何交互行为,当添加交互行为后,每次按下按钮,就会加载相应难度级别的游戏场景。
为了添加触摸交互行为,我们将使用Cocos2d内置的touchBegan函数。在本书的后面章节中,我们将创建更复杂的游戏界面。而这里,我们只使用基本的touchBegan函数。
在同一个文件中,在init函数与@end之间添加如下代码:
-(void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event{
CGPoint location = [touch locationInNode:self];
for (CCSprite *sprite in buttonSpritesArray)
{
if (CGRectContainsPoint(sprite.boundingBox, location)){
CCLOG(@" you have pressed: %@", sprite.name);
CCTransition *transition =
[CCTransition transitionCrossFadeWithDuration:0.20];
[[CCDirector sharedDirector]replaceScene:[[GameplayScene
alloc]initWithLevel:sprite.name] withTransition:transition];
self.userInteractionEnabled = false;
}
}
}
每次当我们触碰屏幕时,touchBegan函数都会被调用执行。
因此,当我们触碰屏幕时,应用程序就会获取触碰的位置,并将其保存到location变量中。
而后,使用for in循环遍历添加到buttonSpritesArray数组中的所有按钮精灵。
并且,调用RectContainsPoint函数,检测我们触碰的位置是否位于任一个按钮精灵的矩形框之内。
若是,则在控制台中输出相关信息,告知用户单击了哪个按钮,这样一来,我们就能知道是否加载了正确难度级别的场景。
接着,我们创建了一个淡入淡出过渡效果,并且从当前场景切换到GameplayScene,并使用所单击的按钮精灵名称进行初始化。
最后,我们需要把Boolean型变量userInteractionEnabled设置为false,禁止当前类监听用户的触屏行为。
当然,这需要我们在init函数开始的时候先把userInteractionEnabled设置为TRUE,即在init函数中添加如下粗体代码。
if(self = [super init]){
self.userInteractionEnabled = TRUE;
CGSize winSize = [[CCDirector sharedDirector]viewSize];
2.8.3 工作原理
至此,我们已经编写好了LevelSelectionScene类。但是,我们还需要向MainScene添加一个按钮以便打开LevelSelectionScene。
在MainScene的init函数中添加如下粗体代码。我们主要添加了menuBtn按钮,以及单击它时要调用的函数。
CCButton *playBtn =
[CCButton buttonWithTitle:nil
spriteFrame:[CCSpriteFrame frameWithImageNamed:@
"playBtn_normal.png"]
highlightedSpriteFrame:[CCSpriteFrame frameWithImageNamed:@
"playBtn_pressed.png"]
disabledSpriteFrame:nil];
[playBtn setTarget:self
selector:@selector(playBtnPressed:)];
CCButton *menuBtn = [CCButton buttonWithTitle:nil
spriteFrame:[CCSpriteFrame
frameWithImageNamed:@"menuBtn.png"]
highlightedSpriteFrame:[CCSpriteFrame
frameWithImageNamed:@"menuBtn.png"]
disabledSpriteFrame:nil];
[menuBtn setTarget:self selector:@selector(menuBtnPressed:)];
CCLayoutBox * btnMenu;
btnMenu = [[CCLayoutBox alloc] init];
btnMenu.anchorPoint = ccp(0.5f, 0.5f);
btnMenu.position =
CGPointMake(winSize.width/2, winSize.height * 0.5);
btnMenu.direction = CCLayoutBoxDirectionVertical;
btnMenu.spacing = 10.0f;
[btnMenu addChild:menuBtn];
[btnMenu addChild:playBtn];
[self addChild:btnMenu];
请不要忘记把menuBtn.png文件从本章的资源文件夹复制到项目中,否则会出现编译错误。
接下来,添加menuBtnPressed函数,一旦menuBtn按钮按下且被释放,它就会被调用执行,代码如下:
-(void)menuBtnPressed:(id)sender{
CCLOG(@"menu button pressed");
CCTransition *transition = [CCTransition transitionCrossFadeWith
Duration:0.20];
[[CCDirector sharedDirector]replaceScene:[[LevelSelectionScene
alloc]init] withTransition:transition];
}
现在,MainScene场景如图2-11所示。
单击play按钮之下的菜单按钮,你将看到如下LevelSelectScreen场景,里面罗列出了所有的难度级别,如图2-12所示。
此时,单击任意一个按钮,即可切换到GameplayScene场景,并且把你所单击的代表难度级别的数字一同显示出来。
由于我单击了18号按钮,所以在切换到GameplayScene场景后,显示出的数字为18,如图2-13所示。