如何设计开发iPhone塔防游戏8-冰封寒雪

欢迎再次回到iPhone塔防游戏系列教程!春日暖阳才刚刚几天,又要降温了。在之前的教程中,我们只有一种单调的炮塔,靠着无休止的火力才阻挡了怪物的入侵。而在这一部分教程中,我们会引入一种全新的炮塔,它会瞬间冰封一个怪物,并让它短时间内不得动弹。是啊,天寒地冻的,怪兽虽然不是人,也Hold不住呀。



 

当然,在学习了这部分教程之后,你还可以设计并实现自己的炮塔类型。



在学习之前,需要先下载美术资源:

http://www.iphonegametutorials.com/usr/uploads/2012/01/FreezeTurret.png

http://www.iphonegametutorials.com/usr/uploads/2012/01/IceProjectile.png

当然,还有上一部分教程完成后的项目源代码:

http://www.iphonegametutorials.com/usr/uploads/2012/01/TowerDefensePart5b.zip

在进入代码部分前,首先当然是要把美术资源图片拖到Resources里面,并选中”copy items into destination’s group(if needed)。

然后需要将新的炮塔图片添加到HUD中以便玩家可以选中,并将其放到关卡场景中。在Xcode中切换到GameHUD.m,并找到init方法中的以下代码:

NSArray *images = [NSArray arrayWithObjects:@"MachineGunTurret.png", @"MachineGunTurret.png", @"MachineGunTurret.png", @"MachineGunTurret.png", nil];
        for(int i = 0; i < images.count; ++i) {
            NSString *image = [images objectAtIndex:i];
            CCSprite *sprite = [CCSprite spriteWithFile:image];
            floatoffsetFraction = ((float)(i+1))/(images.count+1);
            sprite.position = ccp(winSize.width*offsetFraction, 35);
            [self addChild:sprite];
            [movableSprites addObject:sprite];

将以上代码替换为下面的代码:

NSArray *images = [NSArray arrayWithObjects:@"MachineGunTurret.png", @"FreezeTurret.png", @"MachineGunTurret.png", @"MachineGunTurret.png", nil];
for(int i = 0; i < images.count; ++i) {
     NSString *image = [images objectAtIndex:i];
     CCSprite *sprite = [CCSprite spriteWithFile:image];
     float offsetFraction = ((float)(i+1))/(images.count+1);
     sprite.position = ccp(winSize.width*offsetFraction, 35);
     sprite.tag = i+1;//individual tag for each Turret.
     [self addChild:sprite];
     [movableSprites addObject:sprite];

大概说以下以上代码的变化:

首先,在NSArray *images这行代码中,我们将第二个图片”MachieGunTurret.png”更改成了”FreezeTurret.png“。然后,在for 循环中,我们给每个炮塔都添加了一个tag标识。当然,现在炮塔3和炮塔4还是标准的机枪炮。不过在你看完这部分教程后,大可以把它们换成自己需要的炮塔类型。

在GameHud.m中找到ccTouchesBegan方法,并在[self addChild:newSprite]这行代码的前面添加以下代码:

selSprite.tag = sprite.tag;

然后找到ccTouchesEnded方法,并将if判断语句

if (!CGRectContainsPoint(backgroundRect, touchLocation))

的相关代码替换成以下代码:

if (!CGRectContainsPoint(backgroundRect, touchLocation)) {
     CGPoint touchLocationInGameLayer = [m._gameLayer convertTouchToNodeSpace:touch];
     [m._gameLayer addTower: touchLocationInGameLayer: selSprite.tag];
}

以上代码中,我们获取当前所选炮塔的类型,并将其传递给TutorialScene类中的addTower方法。当然,此时还应对addTower方法做一些小的调整。

切换到TutorialScene.h,并替换addTower方法的定义如下:

- (void)addTower: (CGPoint)pos: (int)towerTag;

然后还需要切换到TutorialScene.m,并替换addTower方法(方法的实现代码内容不变):

-(void)addTower: (CGPoint)pos: (int)towerTag{
     //Keep the same function content!
}

现在如果编译运行游戏,会发现在HUD部分的确出现了寒冰炮,可惜当我们把它放到地图上后,就会自动变成机关炮。很显然,这是因为我们还没有创建FreezeTower这个类。

在Xcode中切换到Tower.m,在@end的后面添加以下代码,以创建FreezeTower类:

@implementation FreezeTower
+ (id)tower {
     FreezeTower *tower = nil;
     if ((tower = [[[super alloc] initWithFile:@"FreezeTurret.png"] autorelease])) {
          tower.range = 200;
          tower.target = nil;
          [tower schedule:@selector(towerLogic:) interval:2];
     }
     return tower;
}
-(id) init
{
     if ((self=[super init]) ) {
          //[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
     }
     return self;
}
-(void)setClosestTarget:(Creep *)closestTarget {
     self.target = closestTarget;
}
-(void)towerLogic:(ccTime)dt {
     self.target = [self getClosestTarget];

     if (self.target != nil) {
          //rotate the tower to face the nearest creep

		CGPoint shootVector = ccpSub(self.target.position, self.position);

		CGFloat shootAngle = ccpToAngle(shootVector);

		CGFloat cocosAngle = CC_RADIANS_TO_DEGREES(-1 * shootAngle);

		float rotateSpeed = 0.5 / M_PI; // 1/2 second to roate 180 degrees

		float rotateDuration = fabs(shootAngle * rotateSpeed);

		[self runAction:[CCSequence actions: [CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle], [CCCallFunc actionWithTarget:self selector:@selector(finishFiring)], nil]];

	}

-(void)creepMoveFinished:(id)sender {

   DataModel * m = [DataModel getModel];

   CCSprite *sprite = (CCSprite *)sender; [self.parent removeChild:sprite cleanup:YES];

   [m._projectiles removeObject:sprite];

}

- (void)finishFiring {

   DataModel *m = [DataModel getModel];

   self.nextProjectile = [Projectile projectile];

   self.nextProjectile.position = self.position;

   [self.parent addChild:self.nextProjectile z:1];

   [m._projectiles addObject:self.nextProjectile];

   ccTime delta = 0.5;

   CGPoint shootVector = ccpSub(self.target.position, self.position);

   CGPoint normalizedShootVector = ccpNormalize(shootVector);

   CGPoint overshotVector = ccpMult(normalizedShootVector, 320);

   CGPoint offscreenPoint = ccpAdd(self.position, overshotVector);

   [self.nextProjectile runAction:[CCSequence actions: [CCMoveTo actionWithDuration:delta position:offscreenPoint], [CCCallFuncN actionWithTarget:self selector:@selector(creepMoveFinished:)], nil]];

   self.nextProjectile.tag = 2;!!

   self.nextProjectile = nil;

}
@end

然后切换到Tower.h,并在@end的后面添加以下代码:

@interface FreezeTower : Tower {
}
+ (id)tower;
- (void)setClosestTarget:(Creep *)closestTarget;
- (void)towerLogic:(ccTime)dt;
- (void)creepMoveFinished:(id)sender;
- (void)finishFiring;
@end

好吧,这里稍微解释下这个类的实现代码。实际上,FreezeTower这个类直接继承自Tower类,它的很多方法和MachineGunTurret类非常类似,copy&paste之后,只更改了一些参数而已。例如:图片,炮弹的开火速度,炮弹运行速度(稍微快了一点)。

完成这一步之后,就可以通过addTower方法来添加寒冰炮了。

在Xcode中切换到TutorialScene.m,找到addTower方法,找到最后的这段代码:

NSLog(@"Buildable: %@", type);
  if([type isEqualToString: @"1"]) {
     target = [MachineGunTower tower];
     target.position = ccp((towerLoc.x * 32) + 16, self.tileMap.contentSize.height - (towerLoc.y * 32) - 16);
     [self addChild:target z:1];

     target.tag = 1;
     [m._towers addObject:target];

  } else {
     NSLog(@"Tile Not Buildable");
  }

将其替换为如下代码:

 NSLog(@"Buildable: %@", type);
  if([type isEqualToString: @"1"]) {
        //printf("tower tag %i", towerTag);
        switch (towerTag) {
            case 1:
                target = [MachineGunTower tower];
                break;
            case 2:
                target = [FreezeTower tower];
                break;
            case 3:
                target = [MachineGunTower tower];
                break;
            case 4:
                target = [MachineGunTower tower];
                break;
            default:
                break;
        }

     target.position = ccp((towerLoc.x * 32) + 16, self.tileMap.contentSize.height - (towerLoc.y * 32) - 16);
     [self addChild:target z:1];

     target.tag = 1;
     [m._towers addObject:target];

  } else {
     NSLog(@"Tile Not Buildable");
  }

以上代码中,我们根据从GameHud类中所获取的towerTag标识来判断玩家所选择的炮塔类型,并将其添加到场景中。

编译运行游戏,这个时候寒冰炮看起来更像是一个缓慢的机关炮,这是因为我们还没有添加冰冻机制。

首先要做的就是切换到Tower.m,并确保来自机关炮MachineGunTower的标识为1,而来自寒冰炮的炮弹标识为2。具体来说,finishFiring方法中self.nextProjectile.tag需要设置不同的值。

然后打开TutorialScene.m,找到update方法,并找到实现碰撞检测的代码部分,并使用以下代码替代整个if判断语句:

if (CGRectIntersectsRect(projectileRect, targetRect)) {
   [projectilesToDelete addObject:projectile];
   Creep *creep = (Creep *)target;

   if (projectile.tag ==1) { //MachineGun Projectile
        creep.hp--;
        if (creep.hp

当炮弹和敌人发生碰撞时,首先要判断炮弹的类型是哪一个,如果是1,那么就是来自机关炮,我们什么也不需要修改。而如果炮弹类型标识是2,说明炮弹来自寒冰炮,这时就得做点不同的事情了。首先需要将敌人在原地冰冻1秒钟,然后调用一个新的方法ResumePath以便让敌人继续沿着路径前进。

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

-(void)ResumePath:(id)sender {

   Creep *creep = (Creep *)sender;

   WayPoint * cWaypoint = [creep getCurrentWaypoint];//destination

   WayPoint * lWaypoint = [creep getLastWaypoint];//startpoint

   float waypointDist = fabsf(cWaypoint.position.x -lWaypoint.position.x);

   float playerDist = fabsf(creep.position.x - cWaypoint.position.x);

   float distFraction = playerDist / waypointDist;

   float moveDuration = creep.moveDuration * (distFraction); //Time it takes to go from one way point to another * the fraction of how far is left to go (meaning it will move at the correct speed)

   id actionMove = [CCMoveTo actionWithDuration:moveDuration position:cWaypoint.position];

   id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(FollowPath:)];

   [creep stopAllActions];

   [creep runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];

}

然后切换到Creep.m,并添加以下方法:

- (WayPoint *)getLastWaypoint{

     DataModel *m = [DataModel getModel];
     //int lastWaypoint = m._waypoints.count;
     self.lastWaypoint = self.curWaypoint -1;
     WayPoint *waypoint = (WayPoint *) [m._waypoints objectAtIndex:self.lastWaypoint];
     return waypoint;
}

当然,别忘了在头文件中添加对方法的声明。

以上方法的作用是什么呢?首先我们确定前一个路点和当前路点间的距离,然后计算出敌人和当前路点间的距离。然后将敌人和当前路点间的距离作为分子,将两个路点间的距离作为分母来表示距离。接下来使用这个分数乘以creep.moveDuration,这样,当敌人继续朝当前路点移动时,其运动速度会和其同伙保持一致。而当其抵达当前路点后,就会和之前一样继续正常前进了。

当然,这里还需要对getNextWaypoint方法做一些修正。

切换到Creep.m,并使用以下代码替换getNextWaypoint方法:

- (WayPoint *)getNextWaypoint{
	DataModel *m = [DataModel getModel];
	self.curWaypoint++;

	if (self.curWaypoint >= m._waypoints.count){

		  self.curWaypoint--;

		  gameHUD = [GameHUD sharedHUD];

		  if (gameHUD.baseHpPercentage > 0) {

			  [gameHUD updateBaseHp:-10];

		  }

		  Creep *target = (Creep *) self;

		  NSMutableArray *endtargetsToDelete = [[NSMutableArray alloc] init];

		  [endtargetsToDelete addObject:target];

		  for (CCSprite *target in endtargetsToDelete) {

			  [m._targets removeObject:target];

			  [self.parent removeChild:target cleanup:YES];

		  }

		  return NULL;

	  }

	  WayPoint *waypoint = (WayPoint *) [m._waypoints objectAtIndex:self.curWaypoint];
	  return waypoint;
}

此时编译运行游戏,当我们使用寒冰炮时,敌人会被冻住一秒钟,然后继续以正常速度前进。

当然,还有一件事情要做,就是给寒冰炮添加特有的炮弹,而不是普通的炮弹。切换到Projectile.m,并在@end之后添加以下代码:

@implementation IceProjectile
+ (id)projectile {
     IceProjectile *projectile = nil;
     if ((projectile = [[[super alloc] initWithFile:@"IceProjectile.png"] autorelease])) {
     }
     return projectile;
}

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

@end

然后切换到Projectile.h,并在@end之后添加以下代码:

@interface IceProjectile : CCSprite {
}

+ (id)projectile;
@end

以上代码很简单,只是使用另一种图片创建了不同的炮弹。

切换到Tower.h,找到FreezeTower类,在finishedFiring方法中找到以下代码:

self.nextProjectile = [Projectile projectile];

将其替换成:

self.nextProjectile = [IceProjectile projectile];

好了,就这么简单。

编译运行游戏,一个新的炮塔和新的攻击方式就出现了。

将尽快补上后续教程

原文地址:

http://www.iphonegametutorials.com/2012/01/18/cocos2d-game-tutorial-–-how-to-build-a-tower-defense-game-for-the-iphone-part-6-tower-powers/

源代码:

http://www.iphonegametutorials.com/usr/uploads/2012/01/TowerDefensePart6.zip

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

?>