在继续学习下面的教程之前,我们需要对之前的项目代码做一些调整。
而从下一部分的教程开始,我们将逐渐引入范围攻击,炮塔升级(实时)等内容。
以下是相关的代码变动:
1.有时在敌人被消灭后,炮塔会朝(0,0)的位置发射炮弹。这是因为我们已经清除了炮塔的目标,但还没有通知炮塔停止设计。在Xcode中切换到Tower.m,并找到finishFiring方法,修改如下:
-(void)finishFiring {
2
if (self.target != NULL) {
3
// 保持原有代码不变Keep all pre-existing code
4
} }
2.我们希望炮塔每次只瞄准一个敌人,直到它被消灭或是冲出了火力范围。但此时炮塔只会朝着最近的敌人攻击。因此在每个Tower分类中添加以下方法:
-(void)checkTarget {
2
double curDistance = ccpDistance(self.position, self.target.position);
3
if (self.target.hp <= 0 || curDistance > self.range){
4
self.target = [self getClosestTarget]; } }
在+(id)tower方法中添加对该方法的调用:
[tower schedule:@selector(checkTarget) interval:0.5];
然后在towerLogic方法中更改代码如下:
-(void)towerLogic:(ccTime)dt {
4
if (self.target == nil) {
5
self.target = [self getClosestTarget];
6
}
7
//其它代码保持不变:All other code stays the same
8
}
float rotateSpeed = 0.25 / M_PI; // 1/4 second to rotate 180 degrees and in finished
3.增加炮塔旋转和子弹的速度。在towerLogic方法中更改rotateSpeed:
并ccTime delta =0.5;修改delta值:
4.此时我们可以在同一位置修建两个炮塔,这显然是不合理的。在TutorialScne.m中更改canBuildOnTilePosition方法如下:
-(BOOL) canBuildOnTilePosition:(CGPoint) pos
02
{
03
CGPoint towerLoc = [self tileCoordForPosition: pos];
04
int tileGid = [self.background tileGIDAt:towerLoc];
05
NSDictionary *props = [self.tileMap propertiesForGID:tileGid];
06
NSString *type = [props valueForKey:@"buildable"];
07
08
bool occupied = NO;
09
DataModel *m = [DataModel getModel];
10
11
for (Tower *tower in m._towers) {
12
CGRect towerRect = CGRectMake(tower.position.x - (tower.contentSize.width/2), tower.position.y - (tower.contentSize.height/2), tower.contentSize.width, tower.contentSize.height);
13
if (CGRectContainsPoint(towerRect, pos)) {
14
occupied = YES;
15
}
16
}
17
18
if([type isEqualToString: @"1"] && occupied == NO) {
19
return YES;
20
}
21
return NO;
22
}
类似的,在addTower方法中switch语句前添加以下代码:
-(void)addTower: (CGPoint)pos: (int)towerTag {
02
DataModel *m = [DataModel getModel];
03
Tower *target = nil;
04
CGPoint towerLoc = [self tileCoordForPosition: pos];
05
06
int tileGid = [self.background tileGIDAt:towerLoc];
07
NSDictionary *props = [self.tileMap propertiesForGID:tileGid];
08
NSString *type = [props valueForKey:@"buildable"];
09
10
bool occupied = NO;
11
12
for (Tower *tower in m._towers) {
13
CGRect towerRect = CGRectMake(tower.position.x - (tower.contentSize.width/2), tower.position.y - (tower.contentSize.height/2), tower.contentSize.width, tower.contentSize.height);
14
if (CGRectContainsPoint(towerRect, pos)) {
15
occupied = YES;
16
}
17
}
18
19
if([type isEqualToString: @"1"] && occupied == NO) {
20
switch (towerTag) {
21
//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 {
02
DataModel *m = [DataModel getModel];
03
04
WayPoint *waypoint0 = (WayPoint *) [m._waypoints objectAtIndex:0];
05
WayPoint *waypoint1 = (WayPoint *) [m._waypoints objectAtIndex:1];
06
07
firstDistance = ccpDistance(waypoint0.position, waypoint1.position);
08
WayPoint *waypoint2 = (WayPoint *) [m._waypoints objectAtIndex:(self.curWaypoint-1)];
09
WayPoint *waypoint3 = (WayPoint *) [m._waypoints objectAtIndex:(self.curWaypoint)];
10
11
float thisDistance = ccpDistance(waypoint2.position, waypoint3.position);
12
float moveScale = thisDistance/firstDistance; return (self.moveDuration * moveScale);
13
}
14
15
//Add to Creep.h interface float firstDistance;
16
(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];
2
换为:
float moveDuration = durScale * distFraction;
7. 添加Boss 如果每一波敌人都平淡无奇,就有点无趣了,我们也可以加点Boss。
在Xcode中切换到Creep.m,并添加以下类:
@implementation BossBrownCreep
02
03
+ (id)creep {
04
BossBrownCreep *creep = nil;
05
06
if ((creep = [[[super alloc] initWithFile:@"Enemy3.png"] autorelease])) {
07
creep.hp = creep.initHp = 500;
08
creep.moveDuration = 10;
09
creep.curWaypoint = 0; [creep schedule:@selector(creepLogic:) interval:0.2];
10
11
[creep schedule:@selector(healthBarLogic:)];
12
}
13
14
return creep;
15
}
16
@end
wave = [[Wave alloc] initWithCreep:[FastRedCreep creep] SpawnRate:1.0 RedCreeps:5
2
GreenCreeps:0 BrownCreeps:0];
接下来替换addTarget方法的内容如下:
-(void)addTarget {
02
DataModel *m = [DataModel getModel];
03
Wave * wave = [self getCurrentWave];
04
05
if (wave.redCreeps <= 0 && wave.greenCreeps <= 0 && wave.brownCreeps <= 0) {
06
return; //
07
}
08
09
//wave.totalCreeps--;
10
Creep *target = nil;
11
int creepChoice = (arc4random() % 3);
12
13
int layer;
14
switch (creepChoice) {
15
case 0:
16
if (wave.redCreeps > 0) {
17
target = [FastRedCreep creep];
18
target.tag = 1;
19
wave.redCreeps--;
20
layer = 1;
21
}
22
else {
23
[self addTarget];
24
return;
25
}
26
break;
27
28
case 1:
29
if (wave.greenCreeps >0) {
30
target = [StrongGreenCreep creep];
31
target.tag = 2;
32
wave.greenCreeps--;
33
layer = 1;
34
}
35
else {
36
[self addTarget];
37
return;
38
}
39
break;
40
41
case 2:
42
if (wave.brownCreeps >0) {
43
target = [BossBrownCreep creep];
44
target.tag = 3;
45
wave.brownCreeps--;
46
layer = 2;
47
}
48
else{
49
[self addTarget];
50
return;
51
}
52
break;
53
54
default:
55
break;
56
}
57
58
WayPoint *waypoint = [target getCurrentWaypoint];
59
60
target.position = waypoint.position;
61
waypoint = [target getNextWaypoint ];
62
63
[self addChild:target z:layer];
64
float moveDuration = target.moveDuration;
65
id actionMove = [CCMoveTo actionWithDuration:moveDuration position:waypoint.position];
66
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(FollowPath:)];
67
68
[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]]; // Add to targets array
69
[m._targets addObject:target];
70
return;
71
}
最后找到update方法,并使用以下代码替代if判断语句:
if ([m._targets count] ==0 && wave.redCreeps <= 0 && wave.greenCreeps <= 0 && wave.brownCreeps <= 0)
8.关于敌人的血条。如果可以在敌人的头上显示血条,那么就可以看到每个敌人受到的伤害值是多少。在Creep.h中添加新的实例变量声明:
CCProgressTimer *healthBar;
2
int _totalHp;
别忘了添加相关的@property和@synthesize语句。
接下来切换到Creep.m,在creepLogic方法下添加以下方法:
-(void)healthBarLogic:(ccTime)dt {
02
03
//Update health bar pos and percentage.
04
healthBar.position = ccp(self.position.x, (self.position.y+20));
05
healthBar.percentage = ((float)self.hp/(float)self.totalHp) *100;
06
07
if (healthBar.percentage <= 0) {
08
[self removeChild:healthBar cleanup:YES];
09
}
10
}
以上方法的作用是在敌人头上放置血条,并根据敌人的hp值来更新bar.percentage。此外在+(id)crep方法中添加对该方法的调用:
creep.hp = creep.totalHp = setRedHp;
2
[creep schedule:@selector(healthBarLogic:)];
最后在TutorialScene.m中的addTarget方法中添加以下代码:
//Under [self addChild:target z:1];
2
target.healthBar = [CCProgressTimer progressWithFile:@"health_bar_red.png"];
3
target.healthBar.type = kCCProgressTimerTypeHorizontalBarLR;
4
target.healthBar.percentage = 100;
5
[target.healthBar setScale:0.1];
6
target.healthBar.position = ccp(target.position.x,(target.position.y+20));
7
8
[self addChild:target.healthBar z:3];
然后找到upgrade方法,并更改以下代码
if (creep.hp <= 0) {
2
[targetsToDelete addObject:target];
3
[gameHUD updateResources:1];
4
[self removeChild:creep.healthBar cleanup:YES];//添加本行代码Add this
5
}
这样一来,敌人的头上也有血条了。
9.波次速度调整:
在TutorialScene.m中添加以下方法:
-(void)waveWait {
2
[self unschedule:@selector(waveWait)];
3
[self getNextWave]; [gameHUD updateWaveCount];
4
}
然后在更新方法中将[self getNextWave]和[gameHUD updateWaveCount]语句更改如下:
[self schedule:@selector(waveWait) interval:3.0];
现在每个波次间就会存在一定的间隔时间了。
10.告诉玩家波次的来临。
切换到GameHUD.h,并添加一个实例变量声明:
CCLabelTTF *newWaveLabel;
然后切换到GameHUD.m,并添加以下代码:
//Add to init
02
// Set up new Wave label
03
newWaveLabel = [CCLabelTTF labelWithString:@"" dimensions:CGSizeMake(300, 50)
04
alignment:UITextAlignmentRight fontName:@"TrebuchetMS-Bold" fontSize:30];
05
newWaveLabel.position = ccp((winSize.width/2)-20, (winSize.height/2)+30);
06
newWaveLabel.color = ccc3(255,50,50); [self addChild:newWaveLabel z:1];
07
08
//add new methods & define them in the header file
09
-(void) newWaveApproaching {
10
[newWaveLabel setString:[NSString stringWithFormat: @"HERE THEY COME!"]];
11
}
12
13
-(void) newWaveApproachingEnd {
14
[newWaveLabel setString:[NSString stringWithFormat: @" "]];
15
}
11.最后的最后,当游戏开始时,敌人立马就出现了,这个我可不太喜欢。因此这里也需要使用waveWait方法。
切换到TutorialScene.m,找到addWaves方法,并在开始处添加一个空的波次。
Wave *wave = nil;
2
wave = [[Wave alloc] initWithCreep:[FastRedCreep creep] SpawnRate:1.0 RedCreeps:0 GreenCreeps:0];
3
[m._waves addObject:wave];
在getNextWave中将最大波次修改为6,然后切换到GameHUD.m,在init方法中更改waveCount:
waveCount =0;
好了,以上就是所有的优化调整工作。
从下一部分开始,我们将添加新的炮塔类型,还会添加一些新的特征。