如何设计开发iPhone塔防游戏10 –整装待发

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

在继续学习下面的教程之前,我们需要对之前的项目代码做一些调整。

而从下一部分的教程开始,我们将逐渐引入范围攻击,炮塔升级(实时)等内容。





以下是相关的代码变动:

1.有时在敌人被消灭后,炮塔会朝(0,0)的位置发射炮弹。这是因为我们已经清除了炮塔的目标,但还没有通知炮塔停止设计。在Xcode中切换到Tower.m,并找到finishFiring方法,修改如下:

 

-(void)finishFiring {
if (self.target != NULL) {
// Keep all pre-existing code
  }
}



2.我们希望炮塔每次只瞄准一个敌人,直到它被消灭或是冲出了火力范围。但此时炮塔只会朝着最近的敌人攻击。因此在每个Tower分类中添加以下方法:

 

-(void)checkTarget {
double curDistance = ccpDistance(self.position, self.target.position);
if (self.target.hp <= 0 || curDistance > self.range){
self.target = [self getClosestTarget]; } }



 

在+(id)tower方法中添加对该方法的调用:

[tower schedule:@selector(checkTarget) interval:0.5];

 

然后在towerLogic方法中更改代码如下:

 

+(id) tower methods. [tower schedule:@selector(checkTarget) interval:0.5];
and change the call to getClosestTarget in TowerUpdate methods
-(void)towerLogic:(ccTime)dt {
if (self.target == nil) {
self.target = [self getClosestTarget];
}
//All other code stays the same
}



3.增加炮塔旋转和子弹的速度。在towerLogic方法中更改rotateSpeed:

 

float rotateSpeed = 0.25 / M_PI; // 1/4 second to rotate 180 degrees and in finished



并修改delta值:

 

ccTime delta = 0.5;

 

4.此时我们可以在同一位置修建两个炮塔,这显然是不合理的。在TutorialScne.m中更改canBuildOnTilePosition方法如下:

 

-(BOOL) canBuildOnTilePosition:(CGPoint) pos
{
     CGPoint towerLoc = [self tileCoordForPosition: pos];
     int tileGid = [self.background tileGIDAt:towerLoc];
     NSDictionary *props = [self.tileMap propertiesForGID:tileGid];
     NSString *type = [props valueForKey:@"buildable"];

     bool occupied = NO;
     DataModel *m = [DataModel getModel];

     for (Tower *tower in m._towers) {
          CGRect towerRect = CGRectMake(tower.position.x - (tower.contentSize.width/2), tower.position.y - (tower.contentSize.height/2), tower.contentSize.width, tower.contentSize.height);
          if (CGRectContainsPoint(towerRect, pos)) {
               occupied = YES;
          }
     } 

     if([type isEqualToString: @"1"] && occupied == NO) {
          return YES;
     }
     return NO;
}



类似的,在addTower方法中switch语句前添加以下代码:

-(void)addTower: (CGPoint)pos: (int)towerTag {
     DataModel *m = [DataModel getModel];
     Tower *target = nil;
     CGPoint towerLoc = [self tileCoordForPosition: pos];

     int tileGid = [self.background tileGIDAt:towerLoc];
     NSDictionary *props = [self.tileMap propertiesForGID:tileGid];
     NSString *type = [props valueForKey:@"buildable"];

     bool occupied = NO; 

     for (Tower *tower in m._towers) {
          CGRect towerRect = CGRectMake(tower.position.x - (tower.contentSize.width/2), tower.position.y - (tower.contentSize.height/2), tower.contentSize.width, tower.contentSize.height);
          if (CGRectContainsPoint(towerRect, pos)) {
               occupied = YES;
          }
     } 

     if([type isEqualToString: @"1"] && occupied == NO) {
          switch (towerTag) {
               //Leave all other code the same.

5.当放置炮塔时,因为手指的遮挡,玩家没法看到炮塔。因此我们需要将炮塔向上移动一点,这样在放置炮塔时就可以看到了。首先切换到GameHUD.m,找到ccTouchBegan方法,修改设置newSprite.position的代码如下:

newSprite.position = ccpAdd(sprite.position, ccp(0, 50));

 

然后切换到TutorialScene.m,找到canBuildOnTilePosition方法,并在实现代码的开始添加以下代码:

pos = ccpAdd(pos, ccp(0, 50));

 

类似的,还可以在addTower方法的开始处添加这行代码。编译运行游戏,看看是否正常。

 

6. 此前,敌人会根据路点的间距来加速或减速。实际上我们希望看到的是敌人以相同的速度在地图上前进。在Xcode中切换到Creeps.m,然后添加以下方法:

 

(float) moveDurScale {
     DataModel *m = [DataModel getModel]; 

     WayPoint *waypoint0 = (WayPoint *) [m._waypoints objectAtIndex:0];
     WayPoint *waypoint1 = (WayPoint *) [m._waypoints objectAtIndex:1]; 

     firstDistance = ccpDistance(waypoint0.position, waypoint1.position);
     WayPoint *waypoint2 = (WayPoint *) [m._waypoints objectAtIndex:(self.curWaypoint-1)];
     WayPoint *waypoint3 = (WayPoint *) [m._waypoints objectAtIndex:(self.curWaypoint)]; 

     float thisDistance = ccpDistance(waypoint2.position, waypoint3.position);
     float moveScale = thisDistance/firstDistance; return (self.moveDuration * moveScale);
}

//Add to Creep.h interface float firstDistance;
(float) moveDurScale;

在以上的代码中,将当前路点间距作为前一个路点间距的f分数。然后使用该分数来调整moveDuration。

接下来切换到TutorialScene.m,并找到FollowPath方法,然后将

int moveDuration = creep.moveDuration;

替换为:

 

float moveDuration = [creep moveDurScale];



然后找到ResumePath方法,将

 

float moveDuration = creep.moveDuration * distFraction;



替换为:

 

float durScale = [creep moveDurScale];
float moveDuration = durScale * distFraction;



7. 添加Boss 如果每一波敌人都平淡无奇,就有点无趣了,我们也可以加点Boss。

在Xcode中切换到Creep.m,并添加以下类:

@implementation BossBrownCreep

+ (id)creep {
     BossBrownCreep *creep = nil;

     if ((creep = [[[super alloc] initWithFile:@"Enemy3.png"] autorelease])) {
          creep.hp = creep.initHp = 500;
          creep.moveDuration = 10;
          creep.curWaypoint = 0; [creep schedule:@selector(creepLogic:) interval:0.2];

          [creep schedule:@selector(healthBarLogic:)];
     } 

     return creep;
}
@end

然后在Creep.h中添加该类的定义。

 

在wave.m中使用BossCreep的方法和添加普通敌人完全相同。不过别忘了在wave.h中更新该方法,并添加相关的实例变量。

切换到TutorialScene.m,并更改addWaves方法的代码:

wave = [[Wave alloc] initWithCreep:[FastRedCreep creep] SpawnRate:1.0 RedCreeps:5
GreenCreeps:0 BrownCreeps:0];

 

 

接下来替换addTarget方法的内容如下:

 

-(void)addTarget {
     DataModel *m = [DataModel getModel];
     Wave * wave = [self getCurrentWave];

     if (wave.redCreeps <= 0 && wave.greenCreeps <= 0 && wave.brownCreeps <= 0) {
          return; //
     } 

     //wave.totalCreeps--;
     Creep *target = nil;
     int creepChoice = (arc4random() % 3);

     int layer;
     switch (creepChoice) {
          case 0:
               if (wave.redCreeps > 0) {
                    target = [FastRedCreep creep];
                    target.tag = 1;
                    wave.redCreeps--;
                    layer = 1;
               }
               else {
                    [self addTarget];
                    return;
               }
          break;

          case 1:
               if (wave.greenCreeps >0) {
                    target = [StrongGreenCreep creep];
                    target.tag = 2;
                    wave.greenCreeps--;
                    layer = 1;
               }
               else {
                    [self addTarget];
                    return;
               }
          break;

          case 2:
               if (wave.brownCreeps >0) {
                    target = [BossBrownCreep creep];
                    target.tag = 3;
                    wave.brownCreeps--;
                    layer = 2;
               }
               else{
                    [self addTarget];
                    return;
               }
          break;

          default:
          break;
     } 

     WayPoint *waypoint = [target getCurrentWaypoint];

     target.position = waypoint.position;
     waypoint = [target getNextWaypoint ];

     [self addChild:target z:layer];
     float moveDuration = target.moveDuration;
     id actionMove = [CCMoveTo actionWithDuration:moveDuration position:waypoint.position];
     id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(FollowPath:)];

     [target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]]; // Add to targets array
     [m._targets addObject:target];
     return;
}

最后找到update方法,并使用以下代码替代if判断语句:

 

if ([m._targets count] ==0 && wave.redCreeps <= 0 && wave.greenCreeps <= 0 && wave.brownCreeps <= 0) {



8.关于敌人的血条。如果可以在敌人的头上显示血条,那么就可以看到每个敌人受到的伤害值是多少。在Creep.h中添加新的实例变量声明:

 

 

CCProgressTimer *healthBar;
int _totalHp;



 

别忘了添加相关的@property和@synthesize语句。

接下来切换到Creep.m,在creepLogic方法下添加以下方法:

-(void)healthBarLogic:(ccTime)dt {

     //Update health bar pos and percentage.
     healthBar.position = ccp(self.position.x, (self.position.y+20));
     healthBar.percentage = ((float)self.hp/(float)self.totalHp) *100;

     if (healthBar.percentage <= 0) {
          [self removeChild:healthBar cleanup:YES];
     }
}

以上方法的作用是在敌人头上放置血条,并根据敌人的hp值来更新bar.percentage。此外在+(id)crep方法中添加对该方法的调用:

 

creep.hp = creep.totalHp = setRedHp;
[creep schedule:@selector(healthBarLogic:)];



最后在TutorialScene.m中的addTarget方法中添加以下代码:

//Under [self addChild:target z:1];
target.healthBar = [CCProgressTimer progressWithFile:@"health_bar_red.png"];
target.healthBar.type = kCCProgressTimerTypeHorizontalBarLR;
target.healthBar.percentage = 100;
[target.healthBar setScale:0.1];
target.healthBar.position = ccp(target.position.x,(target.position.y+20));

[self addChild:target.healthBar z:3];

然后找到upgrade方法,并更改以下代码:

 

if (creep.hp <= 0) {
     [targetsToDelete addObject:target];
     [gameHUD updateResources:1];
     [self removeChild:creep.healthBar cleanup:YES];//Add this
}



这样一来,敌人的头上也有血条了。

 

9.波次速度调整:

在TutorialScene.m中添加以下方法:

-(void)waveWait {
     [self unschedule:@selector(waveWait)];
     [self getNextWave]; [gameHUD updateWaveCount];
}

然后在更新方法中将[self getNextWave]和[gameHUD updateWaveCount]语句更改如下:

 

[self schedule:@selector(waveWait) interval:3.0];



现在每个波次间就会存在一定的间隔时间了。

 

10.告诉玩家波次的来临。

切换到GameHUD.h,并添加一个实例变量声明:

 

CCLabelTTF *newWaveLabel;



然后切换到GameHUD.m,并添加以下代码:

/Add to init
// Set up new Wave label
newWaveLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(300, 50)
alignment:UITextAlignmentRight fontName:@"TrebuchetMS-Bold" fontSize:30];
newWaveLabel.position = ccp((winSize.width/2)-20, (winSize.height/2)+30);
newWaveLabel.color = ccc3(255,50,50); [self addChild:newWaveLabel z:1];

//add new methods & define them in the header file
-(void) newWaveApproaching {
     [newWaveLabel setString:[NSString stringWithFormat: @"HERE THEY COME!"]];
}

-(void) newWaveApproachingEnd {
     [newWaveLabel setString:[NSString stringWithFormat: @" "]];
}

11.最后的最后,当游戏开始时,敌人立马就出现了,这个我可不太喜欢。因此这里也需要使用waveWait方法。

切换到TutorialScene.m,找到addWaves方法,并在开始处添加一个空的波次。

 

Wave *wave = nil;
wave = [[Wave alloc] initWithCreep:[FastRedCreep creep] SpawnRate:1.0 RedCreeps:0 GreenCreeps:0];
[m._waves addObject:wave];

 

在getNextWave中将最大波次修改为6,然后切换到GameHUD.m,在init方法中更改waveCount:

 

waveCount = 0;



好了,以上就是所有的优化调整工作。

从下一部分开始,我们将添加新的炮塔类型,还会添加一些新的特征。

 

原文在此:

 

http://www.iphonegametutorials.com/2012/02/12/how-to-build-a-tower-defense-game-for-the-iphone-part-8-some-early-spring-cleaning/

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

?>