Now that we have the ability to place down tower’s and have a basic targeting system working, we can move onto making our tower’s shoot so we can finally obliterate that little black dot smugly walking along our path!
We are going to start off by creating our first type of tower, the “Arrow Tower”, then move on to creating a “Bullet” class, and to top it all off, we will combine the two classes and create a targeting shooting tower.
Right, let’s begin! Before we create our new type of tower, there are a couple of changes we need to make to “Tower.cs”. Add the following field to the Tower class :
protected Texture2D bulletTexture;
Then modify the constructor so it looks like this :
public Tower(Texture2D texture, Texture2D bulletTexture, Vector2 position)
: base(texture, position)
{
this.bulletTexture = bulletTexture;
}
public class ArrowTower : Tower
{
public ArrowTower(Texture2D texture, Texture2D bulletTexture, Vector2 position)
: base(texture, bulletTexture, position)
{
this.damage = 15; // Set the damage
this.cost = 15; // Set the initial cost
this.radius = 80; // Set the radius
}
}
public class Bullet : Sprite
{
private int damage;
private int age;
private int speed;
public int Damage
{
get { return damage; }
}
public bool IsDead()
{
return age > 100;
}
public Bullet(Texture2D texture, Vector2 position, float rotation,
int speed, int damage) : base(texture, position)
{
this.rotation = rotation;
this.damage = damage;
this.speed = speed;
}
}
public void Kill()
{
this.age = 200;
}
I’m sure you are asking, how will this kill a bullet? Well, if you look up at the last bit of code we added in the “IsDead” property, you will see that if a bullet is older than 100, it is classed as dead, so by setting a bullet’s age to 200, we are essentially killing it.
One of the final things we are going to add is an update method for the bullet, without this, the bullets would just sit in the barrel of the tower doing nothing :
public override void Update(GameTime gameTime)
{
age++;
position += velocity;
base.Update(gameTime);
}
In the real world, when we shoot a bullet out of a gun, it is pretty much going to move linearly (in a straight line) until it hits something, but we aren't making a simulation! It would be frustration for the player if his towers never hit any enemies because they moved to fast and the bullets just zoomed behind the enemies! So we are going to cheat, we are going to “bend” our bullets, as if we were in the film “Wanted”. To do this, every frame we are going to making sure our bullet is moving towards our target :
public void SetRotation(float value)
{
rotation = value;
velocity = Vector2.Transform(new Vector2(0, -speed),
Matrix.CreateRotationZ(rotation));
}
For more information on this see Riemers Tutorial.
protected float bulletTimer; // How long ago was a bullet fired
protected List<Bullet> bulletList = new List<Bullet>();
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
bulletTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (target != null)
{
FaceTarget();
if (!IsInRange(target.Center))
{
target = null;
bulletTimer = 0;
}
}
}
bulletTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
This line just updates the timer, this will be set to zero when a bullet is fired.
if (!IsInRange(target.Center))
{
target = null;
bulletTimer = 0;
}
public bool IsInRange(Vector2 position)
{
return Vector2.Distance(center, position) <= radius;
}
IsInRange is a very simple method that just checks whether or not a point is within the range of the tower. Last but not least we will add in the method that will draw our bullets :
public override void Draw(SpriteBatch spriteBatch)
{
foreach (Bullet bullet in bulletList)
bullet.Draw(spriteBatch);
base.Draw(spriteBatch);
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
if (bulletTimer >= 0.75f && target != null)
{
Bullet bullet = new Bullet(bulletTexture, Vector2.Subtract(center,
new Vector2(bulletTexture.Width / 2)), rotation, 6, damage);
bulletList.Add(bullet);
bulletTimer = 0;
}
for (int i = 0; i < bulletList.Count; i++)
{
Bullet bullet = bulletList[i];
bullet.SetRotation(rotation);
bullet.Update(gameTime);
if (!IsInRange(bullet.Center))
bullet.Kill();
if (bullet.IsDead())
{
bulletList.Remove(bullet);
i--;
}
}
private Texture2D bulletTexture;
public Player(Level level, Texture2D towerTexture, Texture2D bulletTexture)
{
this.level = level;
this.towerTexture = towerTexture;
this.bulletTexture = bulletTexture;
}
Lastly find the following line in the Update method :
Tower tower = new Tower(towerTexture, new Vector2(tileX, tileY));
And replace it with this :
ArrowTower tower = new ArrowTower(towerTexture,
bulletTexture, new Vector2(tileX, tileY));
All that is left now is to go into Game1.cs and find the code where we create a new Player, and replace it with this :
Texture2D towerTexture = Content.Load<Texture2D>("arrow tower");
Texture2D bulletTexture = Content.Load<Texture2D>("bullet");
player = new Player(level, towerTexture, bulletTexture);
Then all we must do is add the following image to the Content Project :
That’s it for this tutorial, we now have working shooting towers!!!
Thanks thanks thanks. This is made it very easy to implement my own version of TowerDefence. Please keep posting :)
ReplyDeleteShouldn't you be reusing the bullet instances? It seems like the longer the game lasts, the more memory you are going to absorb creating bullet instances. Perhaps you could have a predefined number of bullets for each tower and if a new bullet needs to be fired, revive the first dead one and use it. Just a suggestion to cut down on memory use.
ReplyDeleteNo because once a bullet is used, it is removed from the list, and GC removes the instance.
DeleteYes I probably should, but that goes a bit beyond the scope of this tutorial.
ReplyDeleteThe most efficient way of handling bullets would probably be to use some sort of Resource Pool like you suggested.
If anyone is interested there is a great article on this here : http://swampthingtom.blogspot.com/2007/06/generic-pool-collection-class.html
Should the enemies be dieing at this point yet?
ReplyDeleteYes they should be, but make sure you check out the bug fixes I posted in the next tutorial!
ReplyDeleteHow exactly would we make it so the tower shoots faster or slower? -Thanks in Advance, Chicken21200
ReplyDeleteYou will need to find this line :
ReplyDeleteif (bulletTimer >= 0.75f && target != null)
in your custom tower class and change 0.75f to whatever number you want - a smaller number means a faster firing tower, a bigger number means a slower firing tower!
Hope that helps!
I can add tower and pretty much everything is working except for the bullets. I have them loaded in my project and etc. But there is no bullet firing and the enemy isn't dying. I looked into your files and there isnt a Draw() method in Bulet.cs. And I can't lauch it (your project, mine does lauch). Thanks for your help.
ReplyDeleteNevermind I fixed it, the draw was meant to be in the ArrowTower class.
ReplyDeleteI don't know if you're still reviewing this blog, hope so! :)
ReplyDeleteI'm still really enjoying it, but I've run into a conundrum.
The way you've set up the towers here, your player has a "BulletTexture" and a "TowerTexture", which means that each tower the player creates will look the same and fire the same bullet.
Is it possible (or mostly just feasible) to have a ContentManager in the tower or bullet class to load their own textures? That way, each type of tower has it's own texture, and you can (down the road) add different looking bullets.
Thanks!
I'm still here to answer comments! :)
ReplyDeleteAt the moment you are right that all the towers look the same, however in tutorial 12 you will start adding in different types of tower so this will be explained more.
But basically instead of storing just one bullet texture and one tower texture I store an array of different tower textures in the player class and pick the appropriate texture based on which tower is being placed.
Hopefully this will become more clear in later tutorials (It is the same with enemies as well).
can these codes be use for tower defense in win7 mobile?
ReplyDeleteI keep getting the error:
ReplyDelete"inconsistent accessibility field type 'system.collections.generic.list..." for the bulletlist. The class is public and the list is protected like it's supposed to be. I'm not sure what's wrong.
One thing to note here, in a previous tutorial you said to add the following to Tower.cs:
ReplyDeletepublic bool IsInRange(Vector2 position)
{
if (Vector2.Distance(center, position) <= radius)
return true;
return false;
}
But in this tutorial you're saying that we haven't implemented it yet and we should do it like this:
public bool IsInRange(Vector2 position)
{
return Vector2.Distance(center, position) <= radius;
}
Yes this can be adapted to the phone. You just have to go in and modify the player class where it reads the mousestate, and handle your touch there however you want. Already have it running on the phone just for fun...
ReplyDeleteHi
ReplyDeleteNot sure if I am to late to the party :)
I'm having this problem where my bullet, most of the time passes behind the enemy, at the corner of the enemy
Here is a pic that perhaps gives a better explanation: http://i.imgur.com/qKQ9y1o.png
Hmm that is odd, does increasing the bullet speed help?
ReplyDeleteWell it does work somewhat at really high speed but then the update wont catch all collides.. But I think its a problem due to the long range, it works perfect at close range like your game, but we are doing it in full screen mode, having a sniperTower with infinte range, they will miss 90% of the bullets unless the enemies are in a straight line with the tower.
ReplyDelete