如何设计开发iPhone塔防游戏12-菜单来了

本文由eseedo(泰然骷髅会成员)翻译,泰然授权转载,转载请通知eseedo(http://blog.sina.com.cn/eseedo)。(-by Iven)

欢迎继续学习这个系列的教程,说实话,这个系列的教程稍微有点草率,还请大家多见谅。

所有的游戏都有始有终,而这一篇的教程主要是关于添加游戏菜单的。菜单的视觉效果是我花了5分钟搞定的(要知道哥可是一点美术天赋都没有),所以别指望它有多漂亮~





好吧,老习惯,还是先下载这篇教程完成后的源代码:

http://www.iphonegametutorials.com/usr/uploads/2012/03/TowerDefensePart9.zip

首先看看进入游戏时的菜单,在进入代码部分前先把相关的美术资源拷贝到项目中。然后创建一个新的类,将其命名为MenuLayer,并让其继承自CCLayer。

 

在Xcode中打开MenuLayer.h,并添加以下的代码:

 

#import "CCLayer.h"
#import "cocos2d.h"
#import "GameHUD.h"
#import "TutorialScene.h"
next open MenuLayer.m and add the following
-(id)init{
     if ((self = [super init])) {
          CGSize winSize = [[CCDirector sharedDirector] winSize];
          CCSprite *background = [CCSprite spriteWithFile:@"MenuScreen.png"];
          background.position = ccp(winSize.width/2, winSize.height/2);
          [self addChild:background];

          CCMenuItemImage *rangeButton = [CCMenuItemImage itemFromNormalImage:@"Buy.png" selectedImage:@"Buy.png" target:self selector:@selector(startGame)];
          CCMenu *menu = [CCMenu menuWithItems:rangeButton, nil];
          menu.position = ccp(winSize.width/2-65, winSize.height/2-15);
          [menu alignItemsVerticallyWithPadding: 20.0f];
          [self addChild:menu];
     }

     return self;
}

-(void) startGame
{
     [GameHUD resetGameHUD];
     [Tutorial resetGame];
     [self.parent removeChild:self cleanup:TRUE];
     [[CCDirector sharedDirector] resume];
}

-(void) dealloc{
     [super dealloc];
}

如果你看过之前的教程,对上面的代码应该不会陌生。这里添加了游戏菜单场景的背景图片,然后添加了菜单项(buy图片)。当玩家触碰图片时会调用startGame方法,该方法的作用是删除菜单层(游戏层和GUI在该层的下面隐藏)。

 

在TutorialScene.h的顶部添加一行代码:

 

#import "MenuLayer.h"

 

打开TutorialScene.m,并添加以下方法:

 

+(void) resetGame{
     reset = YES;
}

-(void) resetLayer{
     reset = NO;

     DataModel *m = [DataModel getModel];
     NSMutableArray *towersToDelete = [[NSMutableArray alloc] init];

     for (Tower *tower in m._towers) {
          [towersToDelete addObject:tower];
          [self removeChild:tower cleanup:YES];
     }

     for (Tower *tower in towersToDelete) {
          [self removeChild:tower cleanup:YES];
     }

     [towersToDelete release];

     for (Creep *target in m._targets) {
          [m._targets removeObject:target];
          [self removeChild:target.healthBar cleanup:YES];
          [self removeChild:target cleanup:YES];
     }

     NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
     for (Projectile *projectile in m._projectiles) {
          [projectilesToDelete addObject:projectile];
     }

     for (Projectile *projectile in projectilesToDelete) {
          [self removeChild:projectile cleanup:YES];
     }

     [projectilesToDelete release];
     [m._towers removeAllObjects];
     [m._targets removeAllObjects];
     [m._projectiles removeAllObjects];
     [m._waves removeAllObjects];
     [self addWaves];

     // Call game logic about every second
     [self schedule:@selector(update:)];
     [self schedule:@selector(gameLogic:) interval:0.2];! !
     self.currentLevel = 0;
     self.position = ccp(-258, -122);
     self.isTouchEnabled = YES;
}

然后在update循环的顶部添加下面的语句:

 

if (reset == YES) {
     [self resetLayer];
}

那么上面的代码是干吗用的呢?以上方法的作用是重新设置游戏层,所有的炮塔,怪物和炮弹都会被清除。接下来重新预定了游戏循环,并将场景设置到初始位置。

 

打开GameHUD.m,并添加以下代码:

 

+(void) resetGameHUD
{
     resetGameHUD = YES;
}

-(void) resetGameHUDLayer
{
     resetGameHUD = NO;
     [healthBar setSprite:[CCSprite
     spriteWithFile:@"health_bar_green.png"]];
     [healthBar setScale:0.5];

     baseHpPercentage = 99;
     [healthBar setPercentage:baseHpPercentage];
     [self updateBaseHp:+1];
     waveCount = 0;
     [waveCountLabel setString:[NSString stringWithFormat: @"Wave 1"]];
     resources = baseAttributes.baseStartingMoney;
     [resourceLabel setString:[NSString stringWithFormat: @"Money $%i",resources]];
}

然后在update方法的顶部添加以下代码:

if (resetGameHUD == YES) {
     [self resetGameHUDLayer];
}

大概解释一下:以上方法的作用是重新设置GameHUD。通过上面的代码,我们重新设置了血条,波次计数和可用金币。

当然如果现在编译运行游戏,不会看到有什么变化,这是因为我们还没有创建菜单层。

 

在Xcode中切换到TutorialScene.m,在+(id)scene这个类方法中,在添加GameHUD层的代码之后添加以下代码:

CCLayer *menuLayer =[[[MenuLayer alloc]init ]autorelease];
[scene addChild:menuLayer z:10];
[[CCDirector sharedDirector] pause];

以上代码的作用是加载菜单层,然后将其放置在游戏层和GameHUD层之上。然后我们暂停CCDirector,这样游戏就不会在背景中运行。

编译运行游戏,我们的游戏开始菜单应该OK了!

 

显示游戏结果

 

现在有两种方式来结束游戏,一种是基地的血条清零,那么玩家就输了,而另一种则是玩家抗过了多次进攻活到最后(这里是5波)。这样我们需要创建一个EndGame层,以便显示游戏结果。

在EndGame.h中添加以下代码:

 

#import "CCLayer.h"
#import "cocos2d.h"
#import "GameHUD.h"
#import "TutorialScene.h"
#import "MenuLayer.h"
@interface EndGame : CCLayerColor{
}

-(id)init:(bool)win;

@end

Then in EndGame.m add the following

-(id) init:(bool) win
{
     if( (self=[super initWithColor:ccc4(150,50,50,255)])) {
          CGSize winSize = [[CCDirector sharedDirector] winSize];
          self.isTouchEnabled = YES;
          GameHUD *gameHUD;
          gameHUD = [GameHUD sharedHUD];

          CCLabelTTF *endGameLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(150, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          endGameLabel.position = ccp((winSize.width/2-10), (winSize.height - 30));
          endGameLabel.color = ccc3(255,80,20);

          [self addChild:endGameLabel z:1];

          if (win == NO){
               [endGameLabel setString:[NSString stringWithFormat: @"Game Over!"]];
          }
          else {
               [endGameLabel setString:[NSString stringWithFormat: @"You Win!"]];
          }

          CCLabelTTF *welldoneLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          welldoneLabel.position = ccp((winSize.width/2-30), (winSize.height/2 + 75));
          welldoneLabel.color = ccc3(255,80,20);

          [self addChild:welldoneLabel z:1];

          [welldoneLabel setString:[NSString stringWithFormat: @"You reached wave %i",gameHUD.waveCount]];
          CCLabelTTF *restartLabel = [CCLabelTTF labelWithString:@"Restart?" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];

          restartLabel.position = ccp((winSize.width/2 - 150), (winSize.height/2));
          restartLabel.color = ccc3(255,80,20);

          [self addChild:restartLabel z:1];

          CCLabelTTF *menuLabel = [CCLabelTTF labelWithString:@"Menu" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          menuLabel.position = ccp((winSize.width/2 - 150), (winSize.height/2-50));
          menuLabel.color = ccc3(255,80,20);

          [self addChild:menuLabel z:1];

          CCMenuItemImage *replayButton;
          CCMenuItemImage *menuButton;
          CCMenu *menu;

          replayButton = [CCMenuItemImage
          itemFromNormalImage:@"Buy.png" selectedImage:@"Buy.png" target:self selector:@selector(replayLevel)];
          menuButton = [CCMenuItemImage itemFromNormalImage:@"Buy.png" selectedImage:@"Buy.png" target:self selector:@selector(returnToMenu)];
          menu = [CCMenu menuWithItems: replayButton, menuButton, nil];

          menu.position = ccp((winSize.width/2 + 50), (winSize.height/2-22));
          [menu alignItemsVerticallyWithPadding: 5.0f];

          [self addChild:menu];
          [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:10 swallowsTouches:YES];
     }

     return self;
}

-(void) replayLevel
{
     //Reset the game and start from the beginning.
     [GameHUD resetGameHUD];
     [Tutorial resetGame];
     [self.parent removeChild:self cleanup:TRUE];
     [[CCDirector sharedDirector] resume];
}

-(void) returnToMenu
{
     CCLayer *menuLayer =[[[MenuLayer alloc]init ]autorelease];
     [self.parent addChild:menuLayer z:10];
     [self.parent removeChild:self cleanup:TRUE];
}

-(void) dealloc{
     [super dealloc];
}

以上代码和GameHUD非常类似:我们使用标签添加文本,而不是使用背景图片。

 

在init方法中有一个布尔变量,其作用是根据它的值来设置屏幕上的文本,要吗是You Win,要吗是You Lose。我们在屏幕上添加了两个菜单按钮项,其中一个用于重新玩一遍当前关卡,而另一个则用于返回菜单场景。ReplayLever方法和MenuLayer中的StartGame方法几乎完全一样。

 

调用endGame:Win

 

打开TutorialScene.m,找到update方法,找到用于搜索当前波次结束的代码,并调整成下面的代码:

 

Wave *wave = [self getCurrentWave];
if ([m._targets count] ==0 && wave.redCreeps <= 0 && wave.greenCreeps <= 0 && wave.brownCreeps <= 0) {
     if (self.currentLevel == 5) {
          CCLayerColor *endGameLayer =[[[EndGame alloc]init:YES]autorelease];
          [self.parent addChild:endGameLayer z:10];
          [[CCDirector sharedDirector] pause];
     }
     else{
          [self schedule:@selector(waveWait) interval:3.0];
          [gameHUD newWaveApproaching];
     }
}

这样一来,我们添加了搜索最后一波的代码,而当选择了最后一波时会加载endGameLayer,并暂停游戏。

 

调用endGame:Lose方法

 

打开GameHUD.m,并将updateBaseHp方法的代码更改如下:

 

-(void) updateBaseHp:(int)amount{
     baseHpPercentage += amount;
     if (baseHpPercentage <= 25) {
          [self->healthBar setSprite:[CCSprite spriteWithFile:@"health_bar_red.png"]];
          [self->healthBar setScale:0.5];
     }

     if (baseHpPercentage <= 0) {
          //Game Over Scenario
          //printf("Game Over\n");
          CCLayerColor *endGameLayer =[[[EndGame alloc]init:NO ]autorelease];
          [self.parent addChild:endGameLayer z:10];
          [[CCDirector sharedDirector] pause];
     }

     [self->healthBar setPercentage:baseHpPercentage];
}

当baseHp <=0的时候会加载endGameLayer,并暂停游戏。

 

 

暂停游戏

 

创建一个PauseLayer类,并让其继承自CCLayerColor。

 

在PauseLayer.h中添加以下代码:

 

#import "CCLayer.h"
#import "cocos2d.h"
#import "GameHUD.h"
#import "EndGame.h"
#import "TutorialScene.h"
#import "MenuLayer.h"
@interface PauseLayer : CCLayerColor{
}

@end

Next add the following to PauseLayer.m :

-(id) init
{
     if( (self=[super initWithColor:ccc4(150,50,50,255)])) {
          CGSize winSize = [[CCDirector sharedDirector] winSize];
          self.isTouchEnabled = YES;

          GameHUD *gameHUD;
          gameHUD = [GameHUD sharedHUD];
          CCLabelTTF *endGameLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(150, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          endGameLabel.position = ccp((winSize.width/2-10), (winSize.height - 30));
          endGameLabel.color = ccc3(255,80,20);

          [self addChild:endGameLabel z:1];

          if (gameHUD.waveCount != 5){
               [endGameLabel setString:[NSString stringWithFormat: @"Paused"]];
          }

          CCLabelTTF *welldoneLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          welldoneLabel.position = ccp((winSize.width/2-30), (winSize.height/2 + 75));
          welldoneLabel.color = ccc3(255,80,20);

          [self addChild:welldoneLabel z:1];

          [welldoneLabel setString:[NSString stringWithFormat: @"You reached wave %i",gameHUD.waveCount]];
          CCLabelTTF *restartLabel = [CCLabelTTF labelWithString:@"Continue" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          restartLabel.position = ccp((winSize.width/2 - 150), (winSize.height/2));
          restartLabel.color = ccc3(255,80,20);

          [self addChild:restartLabel z:1];

          CCLabelTTF *menuLabel = [CCLabelTTF labelWithString:@"Quit" dimensions:CGSizeMake(300, 40) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:30];
          menuLabel.position = ccp((winSize.width/2 - 150), (winSize.height/2-50));
          menuLabel.color = ccc3(255,80,20);

          [self addChild:menuLabel z:1];

          CCMenuItemImage *replayButton;
          CCMenuItemImage *menuButton;
          CCMenu *menu;
          replayButton = [CCMenuItemImage itemFromNormalImage:@"Buy.png" selectedImage:@"Buy.png" target:self selector:@selector(continueLevel)];
          menuButton = [CCMenuItemImage itemFromNormalImage:@"Cancel.png" selectedImage:@"Cancel.png" target:self selector:@selector(returnToMenu)];
          menu = [CCMenu menuWithItems: replayButton, menuButton, nil];
          menu.position = ccp((winSize.width/2 + 50), (winSize.height/ 2-22));

          [menu alignItemsVerticallyWithPadding: 5.0f];
          [self addChild:menu];
          [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:10 swallowsTouches:YES];
     }

     return self;
}

-(void) continueLevel
{
     //Replay Game
     [self.parent removeChild:self cleanup:TRUE];
     [[CCDirector sharedDirector] resume];
}

-(void) returnToMenu
{
     //Return to menu
     CCLayer *menuLayer =[[[MenuLayer alloc]init ]autorelease];
     [self.parent addChild:menuLayer z:10];
     [self.parent removeChild:self cleanup:TRUE];
}

-(void)dealloc
{
     [super dealloc];
}

和EndGame类似,这里使用CCLayerColor,并使用标签来添加文本。然后添加了两个可选按钮,其中一个可以让玩家返回游戏,而另一个则是返回开始菜单。

 

接下来就可以在游戏中使用暂停菜单了。打开GameHUD.m,并在init方法的底部添加以下代码:

 

CCMenuItemImage *pauseButton = [CCMenuItemImage
itemFromNormalImage:@"Pause.png" selectedImage:@"Pause.png" target:self
selector:@selector(pauseGame)];
pauseButton.scale = 0.13;
CCMenu *menu = [CCMenu menuWithItems:pauseButton, nil];
menu.position = ccp(winSize.width -35, 35);
[menu alignItemsVerticallyWithPadding: 20.0f];
[self addChild:menu];

然后添加以下方法:

 

-(void) pauseGame
{
     CCLayerColor *pauseLayer =[[[PauseLayer alloc]init]autorelease];
     [self.parent addChild:pauseLayer z:10];
     [[CCDirector sharedDirector] pause];
}

 

好了,这一步完成后,我们就为游戏创建了三个非常简单的菜单。

 

原文在此:

http://www.iphonegametutorials.com/2012/03/06/how-to-build-a-tower-defense-game-for-the-iphone-part-9-menus/

标签: cocos2d教程, 塔防游戏教程

?>