Welcome to iPhoneGameTutorials.com! Check out our Tutorials page for a list of everything we've written
so far, and drop by the Forums to let us know what you would like to see written about on the site.

How to Build a Tower Defense Game for the iPhone – Part 7 – Money and Health

Hello, and welcome to part 7 of the Tower Defense Tutorial Series. – Money and Health!

In this tutorial we will incorporate “money” into the game so that towers cost the player, and the amount of money available will slowly increase over time. We shall also be incorporating base health bar into the game so we can ultimately have a game over. This particular tutorial focuses primarily on implementing dynamic labels and images, and the game logic behind them, as part of the gameHUD.

When adding labels to gameHUD.m the first thing we need to do is to decide upon what we want to display, in this case; Money count, abase health label and a base health bar, a wave count, and the prices of each tower.

Source code after the break…

First things first, if you want to just see everything in action, here is the source code: Tower Defense Part 7.

In gameHUD.m find the init function. Right before the line:

[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];

paste the following code:

// Set up Resources and Resource label
resources = 100;
self->resourceLabel = [CCLabelTTF labelWithString:@"Money $100" dimensions:CGSizeMake(150, 25) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:20]; 

resourceLabel.position = ccp(30, (winSize.height -15));
resourceLabel.color = ccc3(255,80,20);
[self addChild:resourceLabel z:1]; 

// Set up BaseHplabel
CCLabelTTF *baseHpLabel = [CCLabelTTF labelWithString:@"Base Health" dimensions:CGSizeMake(150, 25) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:20];

baseHpLabel.position = ccp((winSize.width -185), (winSize.height -15));
baseHpLabel.color = ccc3(255,80,20);
[self addChild:baseHpLabel z:1]; 

// Set up wavecount label
waveCount = 1;
self->waveCountLabel = [CCLabelTTF labelWithString:@"Wave 1" dimensions:CGSizeMake(150, 25) alignment:UITextAlignmentRight fontName:@"Marker Felt" fontSize:20]; 

waveCountLabel.position = ccp(((winSize.width/2) -80), (winSize.height -15));
waveCountLabel.color = ccc3(100,0,100);
[self addChild:waveCountLabel z:1];

The first three blocks of code, where we set up the wave count, resources (money) and base health labels should be fairly simple, however if you are unfamiliar with labels then feel free to take a few moments to understand the code above.
The interesting code comes next when implementing the health bar. First download the Red and Green health bar images here [insert link] and then import them to your resources folder in Xcode.

Next copy the following code and place it bellow the label setup shown above.

//Set up health Bar
baseHpPercentage = 100;
self->healthBar = [CCProgressTimer progressWithFile:@"health_bar_green.png"];
self->healthBar.type = kCCProgressTimerTypeHorizontalBarLR;
self->healthBar.percentage = baseHpPercentage;
[self->healthBar setScale:0.5];
self->healthBar.position = ccp(winSize.width -55, winSize.height -15);
[self addChild:healthBar z:1];

We are using a “CCProgresstimer” to show the health bar image, we chose this because a ProgressTimer is able to show percentages of an image. We chose the type horizontalBarLR because the health bar image is a horizontal bar, and we wish (when the base takes damage) to decrease the health bar starting from the right end (100% health), moving towards the left end (0% health). We then set the percentage to be 100, as we start with full health. We also scale the image by half for aesthetic reasons, and set the position of the bar.

The last thing that we need to do, is to set labels to show the “price” of each tower.

Find the for statement which places the tower images onto the gameHUD, then replace it with the following.

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; printf("tag %i", sprite.tag);
      [self addChild:sprite];
      [movableSprites addObject:sprite]; 

      //Set up and place towerCost labels
      CCLabelTTF *towerCost = [CCLabelTTF labelWithString:@"$" fontName:@"Marker Felt" fontSize:10];
      towerCost.position = ccp(winSize.width*offsetFraction, 15);
      towerCost.color = ccc3(0, 0, 0);
      [self addChild:towerCost z:1]; 

      //Set cost values
      switch (i) {
            case 0:
                  [towerCost setString:[NSString stringWithFormat:@"$ 25]];
            break; 

            case 1:
                  [towerCost setString:[NSString stringWithFormat:@"$ 35”]];
            break; 

            case 2:
                  [towerCost setString:[NSString stringWithFormat:@"$ 25]];
            break; 

            case 3:
                  [towerCost setString:[NSString stringWithFormat:@"$ 25]];
            break; 

            default:
            break;
      }
}

This simply places a price label slightly bellow the tower image, with a separate price for each tower image.

One final thing to do is to set up the variables involved in the dynamic labels. Add the following variables into the declaration of gameHUD.h:

int resources;

      CCLabelTTF *resourceLabel;
      CCLabelTTF *waveCountLabel;
      float baseHpPercentage;
      CCProgressTimer *healthBar;
} 

@property (nonatomic, assign) int resources;
@property (nonatomic, assign) float baseHpPercentage;

and in gameHUD.m add the following:

@synthesize resources = resources;
@synthesize baseHpPercentage = baseHpPercentage; 

int waveCount;

Next we will add the logic behind these labels to make them informative to the player and meaningful within the game.

Adding Towers: Removing Resources.
Go into gameHUD.m and add the following functions (remember to add the headers too).

-(void) updateResources:(int)amount {
      resources += amount;
      [self->resourceLabel setString:[NSString stringWithFormat: @"Money $ %i",resources]];
} 

-(void) updateResourcesNom {
      resources += 1;
      [self->resourceLabel setString:[NSString stringWithFormat: @"Money $ %i",resources]];
}

We can now use this function to update the amount of resources, then place the new number on screen. Next go into TutorialScene.m and find the addTower function. Replace the switch statement (towerTag) with the following:

switch (towerTag) {
      case 1:
            if (gameHUD.resources >= 25) {
                  target = [MachineGunTower tower];
                  [gameHUD updateResources:-25];
            } else return;
      break; 

      case 2:
            if (gameHUD.resources >= 35) {
                  target = [FreezeTower tower];
                  [gameHUD updateResources:-35];
             } else return;
      break; 

      case 3:
            if (gameHUD.resources >= 25) {
                  target = [MachineGunTower tower];
                  [gameHUD updateResources:-25];
             } else return;
      break; 

      case 4:
            if (gameHUD.resources >= 25) {
                  target = [MachineGunTower tower];
                  [gameHUD updateResources:-25];
            } else return;
      break; 

      default:
      break; 

}

What this does, is to check to see if there is enough resources available, if there is then it creates the tower selected and removes the cost of the tower from resources.

Next, Increasing the Resources available. There are two ways which the player can increase their money.
1: Money increases over time.
2: Gain money from killing creeps. We will implement both methods, starting with gaining money from killing creeps, go to -update and find where our creep collisions are being detected. When a creep’s health drops below 0 (a creep dies) we add one to the resources total eg:

(creep.hp <= 0) {
      [targetsToDelete addObject:target];
      [gameHUD updateResources:1];
}

Next, to increase money over time find the init function of TutorialScene and add the following after the declaration of gameHUD

[gameHUD schedule:@selector(updateResourcesNom) interval: 2.0];

This will add 1 resource ever two seconds.

Run and test this, and we should have fully operational resources/money functionality.

Next, we need to change the base health when a creep reaches the end of its path. Add the following function to gameHUD.m (& the header).

-(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"); 

            //Implement Game Over Scenario
      }

      [self->healthBar setPercentage:baseHpPercentage]; }

So, what this does is to update the baseHpPercentage by a given amount. If the baseHp falls below 25% then we change the image to the red health bar, then in the baseHp falls below (or equal to) 0 we have reached game over (at the moment just a printf message but we shall work on this next tutorial). Then after these checks we update the image bar to reveal the baseHpPercentage. Now, open Creep.m and find the getNextWaypoint function. In this function we check to see if the creep is at the last waypoint, if it is we delete the creep. After we delete the creep add the following code.

gameHUD = [GameHUD sharedHUD]; 

if (gameHUD.baseHpPercentage > 0) {
      [gameHUD updateBaseHp:-10];
}

This means that the baseHp will decrease by 10 for each creep that “hits” it. Run and test this code, we should see the health bar be affected by the creeps and change color when low on health.

Finally we wish to update the WaveCount, so that players can get a feeling for how far through the game they are, and have something to aim for (ie. a previous highest level).

In gameHUd.com add the following function (and header):

-(void) updateWaveCount {
      waveCount++;
      [self->waveCountLabel setString:[NSString stringWithFormat: @"Wave %i",waveCount]];
}

next go into TutorialScene and the update function, find the if statement which detects the end of the current wave and add a call to the updateWaveCount function.

Wave *wave = [self getCurrentWave];
if ([m._targets count] ==0 && wave.redCreeps <= 0 && wave.greenCreeps <= 0) {
      [self getNextWave];
      [gameHUD updateWaveCount];
}

And there we have it! We now have 3 dynamic labels, with corresponding logic, and also have prices for each tower. The actual prices, resource spawn rates and starting resources are all nominal, feel free to change these settings to get the balance of challenge and enjoyment that you wish.

Yours,
Aiden Fry
iPhone Game Tutorials Contributor

Aiden Fry is a recent graduate, working to get into the games industry. He is a games programmer with a special interest in audio programming. Please check out his website aidenfry.tk to see some of his work.

Aiden Fry is a recent graduate, working to get into the games industry. He is a games programmer with a special interest in audio programming. Please check out his website aidenfry.tk to see some of his work.
Aiden Fry
View all posts by Aiden Fry
Aidens website
Share this:
Share this page via Email Share this page via Stumble Upon Share this page via Digg this Share this page via Facebook Share this page via Twitter

28 Responses to “How to Build a Tower Defense Game for the iPhone – Part 7 – Money and Health”


  • Hi

    Amazing work dude, very much appreciated all the time and effort you and all your contributors put into this series.

    One minor thing I noticed when running this on my test phone (3gs) is that the map flickers, It’s like I can see the actual grid when I pan around the screen. Doesn’t happen on the Simulator, just wondering does this happen on anyone’s devices?
    I’ll try it on iPhone and iPad and see if anything similar happens

  • Hello,
    great tutorials! Now what I’ve experienced is, that a turret jusst shoots at the nearest enemy. But in the original tower defense games, the turret locks on to a specific enemy, and shoots til the enemy is dead or til the enemy is out of range. I experienced this, when there are too many enemies directly in front of a turret.
    :D

  • @Lars,

    Right now, every time towerLogic is called the tower calls
    [self getClosestTarget];
    To achieve what you want, you would need to add an if statement so that the tower only calls
    [self getClosestTarget];
    when they currently do not have a current target. (ill leave you to figure out exactly how to do this…. its good homework :) )

    @Dan,
    Yes this seems to be an ongoing issue (look at the comments section for part 6 for some possible fix’s)
    Updates will be coming as I am getting my Dev license next week, so I can test and create a stable release. Sorry for the inconvenience.

    Aiden

    • @Aiden

      No need to apologize mate, you’ve created an amazing tutorial that must have taken a LOT of time and will help countless people
      People who get stuff for free and complain its not 100% perfect are just ungrateful, I just wanted to see if its an ongoing issue (and yes I skipped step 6 to see how it’s coming along :p)
      I’ll check the fixes and see if I can contribute anything else

      Thanks again

  • I tried to do what you said, but I have a problem with it ;)
    I created an if statement in the tower logic of the MachineGunTower, which calls getClosestTarget, if there is no current target(target == nil). In the finishFiring method, I give every new projectile self as its userData:
    self.nextProjectile.userData = self;
    In tutorialScene, in the update: method, i call:
    [(MachineGunTower *)projectile.userData setTarget:nil];
    when an enemy is dead.

    This works well with one tower shooting at an enemy. But if multiple towers shoot at one enemy, only the tower who shot the bullet that actually killed the enemy gets notified that the enemy is dead and it should search for a new target. Any idea how i can fix that?
    Thanks,
    Lars
    btw: Great tutorials on this page! I can definetly recommend it to anybody who wants to learn game programming :D

    • Hey lars,
      I like your thinking! You are fairly close, however you are going on quite a long journey (through the projectile class) and you will end up with the errors that you have found using this method.

      What I did was to create a new method in tower, which check’s the current targets hp. when this is <=0 then we re call get nearest target.

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

      //new method
      -(void)checkTarget {
      double curDistance = ccpDistance(self.position, self.target.position);
      if (self.target.hp self.range){
      self.target = [self getClosestTarget];
      }
      }

      //towerlogic
      if (self.target == nil) {
      self.target = [self getClosestTarget];
      }

      there you go, that should solve the problem for you! :D

  • Thank you Aiden!
    Now it works fine! thank you very much! :)

  • There was another problem that i had, but it was pretty easy to fix:
    Whenever a tower had no target, it shot to (0|0) once. To fix this, just add an if statement into the finishFiring: method which checks if the target is not nil.
    :)

  • Great tutorials. Now that you have shown us how to add some nice overlays could we maybe try adding those nifty range overlays that I see on a lot of tower defense games? I’ve been curious on how to implement them. Should we use a circular sprite that we simply scale to the range of a given turret or procedurally draw them? Should they be on another layer?

    • Good idea,
      if you look in gameHUD ccTouchesBegan you will find this section of code

      selSpriteRange = [CCSprite spriteWithFile:@"Range.png"];
      selSpriteRange.scale = 4;
      [self addChild:selSpriteRange z:-1];
      selSpriteRange.position = sprite.position;

      this adds the image Range as an overlay. The image Range is 100 pixels in diameter, and therefore shows a range of 50.
      We then will scale the image x4 in the instance of a mgTower. For other towers add in a switch statement for differing scales.

      However this range image only is shown when placing a tower, after it is placed it is removed.

      Is this what you meant?

  • Once again another great tutorial! Thanks!

  • Please add the function of a change in speed.

    • Could you expand on that please, not sure what u mean :)

      • Sorry , my english is bad,
        Reading is not a problem:)

        I mean to change game speed.

        Such as:
        1x speed 2x speed

        • My guess would be to take all the unit and fire speeds out of the objects classes and make another NSObject called SpeedClass or something and set a BOOL value called speedOn, when active:
          fastCritter.speed = SpeedClass.megaSpeed (7 in SpeedClass)
          else{
          fatCritter.speed = SpeedClass.normalSpeed (14 in SpeedClass)

          It’s messy tho, maybe someone can offer anther solution as this would have to be in every monster and turret object

  • Aha well in the next tutorial we will take those variable and add them to their own class. I was not going I imPlement double speed but why not! Dan you are about right. Coming soon guys.

  • All thanks go to the developers and people who write code and not just throwing out ideas like I do :p
    Since everything has to have varying speeds it might be easier to make a global multiplyer , so standard would be speed x 1 and standard and pass in a multiply value if the game state has been altered eg button and refresh game values. It’s a tough one :)

  • TowerDefenseTutorialPart7[1356:11003] *** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]‘
    *** First throw call stack:
    (0x1a1d052 0x1dc9d0a 0x1a09db8 0×4008 0×6618 0x7c428 0×84700 0xaa12a 0xad0b4 0x83c2db 0x83c1af 0x19f1966 0x19f1407 0x19547c0 0x1953db4 0x1953ccb 0x23fb879 0x23fb93e 0x8f1a9b 0x2ba1 0x2b25)

    • I think You have reached the end of the game (you have played the 5 levels) at this point, you will get an error. I am currently working on the next tutorial which will solve this problem.

  • Hey, great tutorial Aiden. I just have one question, and that is whenever I try to modify the .tmx file from your source code by creating my own maps, the program doesn’t compile and crashes at this line of the code,

    NSAssert( !compression || [compression isEqualToString:@"gzip"], @”TMX: unsupported compression method” );

    Do you know why? Thanks

    • Ok, so i’m no expert on this, from what I see it looks like you are exporting your tmx file using the wrong compression format. Try opening preferences and choosing 64bit zlib compressed then re-exporting.
      I did come across this error myself, what I ended up doing is duplicating the original TMX file and editing the copy. However this does not get to the bottom of the problem just avoids it.
      Hope this helps, if you are still having problems shout again and I will have a proper look at it.

    • I had a similar problem, it seems to happen when you alter it and the compression changes (ie. no longer compressed)
      Use the file editor and copy the this into I think its line 20, you’ll see the compression line anyway:

      If that doesn’t work change it to “gzip”

      as Aiden said it doesn’t get to the root of the problem but it happened to me months ago, so this may not be the solution as I fixed mine the same way Aiden did

  • Ah. I have made multiple modifications to the game, adding new towers, sprites, tiles etc and everything runs smoothly on the iPhone simulator. However, whenever I try to run the app on my personal iPhone, it builds but then ends up with 8 errors that tell me it cannot find the location of the new sprites I added, even though it is clearly in the resources folder. Did anyone else have this problem?

  • Off the top of my brain it may be because the iPhone is case sensitive whilst the simulator is not. Make sure ur sprite names are exact. Hope this helps

Leave a Reply

Notify me of followup comments via e-mail. You can also subscribe without commenting.

Tip: When posting code, put the following around your code for a better view:
<pre class="brush:c++">your code here</pre>




Switch to our mobile site