Tuesday, 18 January 2011

Tutorial 12 : Adding a New Tower Type

Sorry for the delay, I have had a very busy at the month! In this tutorial I will show you how to add in two type of cannon, the Spike tower. This tower will be able to shoot 8 bullets in eight different directions at a time.

This should be pretty straight forward seeing as we have designed the Tower class to be as extensible as possible, although we will have to make several adjustments to some of the other classes.

So let’s get started, the first thing we will do is add four new textures to our project :

The “spike tower” texture needs to be added to the main Content Project, whereas the other 3 textures need to be added to a new folder called “Spike Tower” that needs to be added in the GUI folder. Confused yet? When you have done this step you should end up with something like this :

Hopefully your still with me after that stage! Right, we can move onto creating a new class for our tower. Add a new class into the Tower folder and call it “SpikeTower.cs”. Next, make our new class inherit from the Tower class :

publicclass SpikeTower : Tower

Now we are going to add some new fields and properties to the class :

// A list of directions that the tower can shoot in.private Vector2[] directions = new Vector2[8];// All the enimes that are in range of the tower.private List<Enemy> targets = new List<Enemy>();

The first field will store of all the directions that the Spike Tower can shoot in (North, East, South… etc.). The next field will contain a list of all the enemies that are in range of the tower. You may be asking why not just have one target like the last tower? We need to store all these enemies because we aren’t just targeting one enemy, we are shooting bullets off in all directions and hoping one hits! We need to know all of the enemies that might be hit!

Instead of taking in a rotation and then converting it into a velocity, we directly pass in a velocity to use (Well technically we pass in a direction and use that and the speed to calculate the velocity).

With that added we can go back to “SpikeTower.cs” and add our update method :

publicoverridevoid Update(GameTime gameTime){

}

We will build this method up step by step. The first thing we will add to this method is some code to create our bullets and make sure the travel in the right direction :

bulletTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;

// Decide if it is time to shoot.if (bulletTimer >= 1.0f && targets.Count != 0){// For every direction the tower can shoot,for (int i = 0; i < directions.Length; i++){// create a new bullet that moves in that direction. Bullet bullet = new Bullet(bulletTexture, Vector2.Subtract(center,new Vector2(bulletTexture.Width / 2)), directions[i], 6, damage);

bulletList.Add(bullet);}

bulletTimer = 0;}

First we update the timer that we use to decide if it is time to shoot again. We then check if it is time to shoot, and if it is, we loop through all of the directions that our tower can shoot, and we create a bullet that travels in that direction.

// Remove the bullet if it is dead.if (bullet.IsDead()){ bulletList.Remove(bullet); i--;}}

This code is basically the same as the update code in “ArrowTower.cs” however notice that we don’t just check if our bullets have hit just one target, we test it against all of the nearby enemies until either we hit one or we run out of enemies to test against.

Our Spike Tower is nearly finished however there is one more property that needs to be added, however first we need to go back to “Tower.cs” and add the following property :

I will explain the significance of this property later. While we are still in “Tower.cs” there is one more change that needs to be made, find the GetClosestEnemy method and make it a virtual method, so change this :

publicvoid GetClosestEnemy(List<Enemy> enemies)

to this :

publicvirtualvoid GetClosestEnemy(List<Enemy> enemies)

Now then, go back to “SpikeTower.cs” and add the following property :

publicoverridebool HasTarget{// The tower will never have just one target.get { returnfalse; }}

Again I will explain this in more detail later. Before we can move onto to adding a button for this tower, there is a few more changes to be made (yes I know I said we only had one more property to add…). We need to override the GetClosestEnemy method, this is because our now Spike Tower handles enemies differently than the normal method, so let’s override it and add in some new logic :

publicoverridevoid GetClosestEnemy(List<Enemy> enemies){// Do a fresh search for targets.targets.Clear();

// Loop over all the enemies.foreach (Enemy enemy in enemies){// Check wether this enemy is in shooting distance.if (IsInRange(enemy.Center)) {// Make it a target. targets.Add(enemy); }}}

As you can probably see, instead of trying to find the closest enemy, we are just finding all of the enemies that are in range of the tower (see above for the explanation why).

Before we can add a new button for this tower, we need to make a small method to the Players Update method, so go ahead and find the Update method in “Player.cs” and more specifically this line :

if (tower.Target == null)

and replace it with the following :

// Make sure the tower has no targets.if (tower.HasTarget == false)

This is where the property we added earlier comes into play. Normally this property will just return whether or not a tower has a single target, but in the case of the Spike Tower, it will always return false. This is because the Spike Tower never really has a single target, it can have many, and it always needs to check whether new enemies have come into range or old enemies are no longer in range.

While we are in the player class, go to the top of the class and find the towerTexture field, and change it to :

// The textures used to draw our tower.private Texture2D[] towerTextures;

It is no longer possible to only store one texture to use for all of the Towers, we will needs a separate texture for each tower. We need to modify the constructor to accommodate this change :

Now that we have access to different textures to use for different types of tower, we can go ahead and find the AddTower method and change it so it can handle the SpikeTower. You need to change the switch statement so it looks like this :

As you can see the code is pretty much the same, the major difference is that the arrow tower uses the first texture stored in our texture array, and the spike tower uses the second texture stored in the array. We must remember this order when we pass our texture array to the player.

Now all that’s left to do is to add a new button for our tower, so go back to “Game1.cs” and at the top add the following field :

Button spikeButton;

Next, find the LoadContent method. We need to adjust the code that initializes the player so that we pass in a texture array instead of a single texture :

You can see an explanation of this in the previous tutorial. All that is left to do now is Update and draw our buttons!

Fnd the Update method and just after where we update the arrowButton, update the spikeButton :

//Update the spike button.spikeButton.Update(gameTime);

And then go down to the Draw method and just after where we draw the arrowButton, draw the spikeButton :

spikeButton.Draw(spriteBatch);

And where finished! Who knew it would take so much to just add in a new tower . I’m still undecided on whether to write another tutorial about creating a custom tower class, or just write a “finishing off” tutorial. Let me know what you think!

- A Button to upgrade the tower would be nice. So he does more damage or has a longer shooting range.- The player sould get some money when he kills an enemy or survives a round.- Multiplayer would be nice too, but i think its nothing easy to do.

So you think for every bullet that is fired, the bullet should store some kind of reference to the tower that fired it so that the bullet knows when it is out of the range of the tower? Should the bullet also be aware of all the enemy's as well so it knows when it hits one...?

I think my method of just letting the tower handle its own bullets would be more efficient than that.

Of course there are better ways to handle the bullets more efficiently, i.e using some kind of resource pool.

But the tower will still have to remove the dead bullet from it's list of bullets, and for your idea to work each bullet would need to keep track of the towers range, I just don't see the benefit of the bullet keeping track of when it should expire.

Sorry for taking so long to reply I was away on vacation. I have worked through the tutorial and didn't have a problem till I tested the game and added a few arrow towers. Also I was wondering, if you aren't too busy, could you write a tutorial about making a tower the does splash damage or help point me in the right direction of how I should go about doing it? I've tried figuring it out by myself but I've only ended up screwing other things up lol

Hi firefly, im having this problem which whenever i place my spike tower on the map it show null reference from the line if (IsCellClear() == true && towerToAdd.Cost <= money) i have gone through and through with this tutorial and the source code and have absolutely no idea what the problem im facing, would you kindly help me? =D