Monday 31 January 2011

Tutorial 14 : Polishing the Game

In this, the last tutorial in the series, I will show you how to add a bit of polish to the game by adding a few key features to our game :

  1. Giving the player money when he kills an enemy.
  2. Removing a life when one of the enemies reaches the end of the path.

Both of these things will be implemented in the wave class, however before we make any changes to that we must first make some of the player’s properties settable!

So go to the Player class and find the Money and Lives properties and a set statement to both of them :

public int Money
{
get { return money; }
set { money = value; }
}
public int Lives
{
get { return lives; }
set { lives = value; }
}

This will allow us to make changes to these properties outside of the player class. We are now ready to add our two key features! Go to the Wave class and somewhere with the fields add a Player field :

private Player player; // A reference to the player.

Now we need to pass a reference to the player via the constructor, so change the Wave constructor to accept a player reference :

public Wave(int waveNumber, int numOfEnemies,
Player player, Level level, Texture2D enemyTexture)
{
this.waveNumber = waveNumber;
this.numOfEnemies = numOfEnemies;

this.player = player; // Reference the player.
this.level = level;

this.enemyTexture = enemyTexture;
}

Now that we have a stored reference to the player, find the Update method and in particular the if statement that check’s if an enemy is dead but still has health (ergo the enemy is at the end), and in this if statement add the code to remove a life from the player :

if (enemy.IsDead)
{
if (enemy.CurrentHealth > 0) // Enemy is at the end
{
enemyAtEnd = true;
player.Lives -= 1;
}

Then just under that we are going to add an else statement which will be triggered if the enemy has been killed and has no health (ergo the player has killed the enemy) :

else
{
player.Money += enemy.BountyGiven;
}

Simple huh? I can’t beleive I left it until the end to add these features in that actually make our game playable! With those changes made, that if statement should look like this :

if (enemy.IsDead)
{
if (enemy.CurrentHealth > 0) // Enemy is at the end
{
enemyAtEnd = true;
player.Lives -= 1;
}

else
{
player.Money += enemy.BountyGiven;
}

enemies.Remove(enemy);
i--;
}

All that is left now is to pass the player reference to the Wave class. So go to the WaveManager class and modify the constructor so that it takes in a reference to the Player class and then passes it to the Wave class :

public WaveManager(Player player, Level level, int numberOfWaves, Texture2D enemyTexture)
{
this.numberOfWaves = numberOfWaves;
this.enemyTexture = enemyTexture;

this.level = level;

for (int i = 0; i < numberOfWaves; i++)
{
int initialNumerOfEnemies = 6;
int numberModifier = (i / 6) + 1;

// Pass the reference to the player, to the wave class.
Wave wave = new Wave(i, initialNumerOfEnemies *
numberModifier, player, level, enemyTexture);

waves.Enqueue(wave);
}

StartNextWave();
}

Now go back to Game1 and find the LoadContent method, and in it find where we initialize the WaveManager.We need to move this initialization code to underneath where we initialize the Player and then pass in the Player to the WaveManager, like so :

Texture2D bulletTexture = Content.Load<Texture2D>("bullet");

Texture2D[] towerTextures = new Texture2D[]
{
Content.Load<Texture2D>("arrow tower"),
Content.Load<Texture2D>("spike tower"),
Content.Load<Texture2D>("slow tower"),
};

player = new Player(level, towerTextures, bulletTexture);

Texture2D enemyTexture = Content.Load<Texture2D>("enemy");
waveManager = new WaveManager(player, level, 24, enemyTexture);

And we’re done!! That is the end of this series (unless people are in need of another tutorial??). So now you have a pretty solid base to work off of if you want to create your own tower defence! But don’t be tempted to think this is finished game, there is still a lot more that could be done!!
  • The art is only placeholder, be creative and make your own!

  • Currently there is only 1 type of enemy, how dull is that? Be creative and add new types, it is basically the same process as adding a new tower!

  • Wouldn’t it be nice if enemies didn’t have to just follow a path? Let A-Star guide you...

  • Add a timer that appears in between levels to let the player know how long he has before the next wave.

  • Add in bonus rounds for extra gold.

  • The current UI is so dull, wouldn’t it be nice if you could see some more information about each tower?

Well that’s it for me, thank you so much for the kind words, reporting bugs, and for just reading this blog!

Happy Coding!

46 comments:

  1. "Wouldn’t it be nice if enemies didn’t have to just follow a path? Let A-Star guide you."

    what do you mean about A-Star guiding us?

    ReplyDelete
  2. A-Star is a pathfinding algorithm that would be perfect in this type of game.

    ReplyDelete
  3. Sweet man!! Thanks for making this, I still found it impossible lol (I commented on your other tutorial), great :)

    "Now we need to pass a reference to the player via the constructor" .. that was the thing catching me out! :(

    ReplyDelete
  4. I followed this as closely as I could but still get the same error as before, unfortunately.

    In the Wave class, when trying to add money or lose a life, I get:

    NullReferenceException was unhandled
    Object reference not set to an instance of an object.

    I have the reference to the player class etc, and there are no build errors.. I still have no idea why it gives me this because your tutorial fixed the build error I was getting! :( Any ideas?

    ReplyDelete
  5. Argh, very sorry to have spammed your page but woot! I don't know what happened but it works now, must have been early morning fail syndrome. Sorry, best tutorial series ever and just what I've been looking for ;)

    If you're looking for some more I'd suggest:

    - How to add a circle/square around the towers placed, so they have a 'damage zone' people can see (which is their radius)

    - When you click a button, it gives you a picture of the tower wherever you move your cursor, (looking like drag and drop).

    Just some suggestions, sorry for spamming again :P

    ReplyDelete
  6. Just an hint to everyone, Switch (waveNumber % 10) returns an value between 1 and 10, depending on the wave between steps of 10, so 1 will be 1, 10 will be 0 and 11 will be 1 again, I used this to make 10 different enemies, so if you add case 1= easy enemy, case 2 = normal enemy, case 0 = boss, etc. It's really handy.

    ~Arjan.

    ReplyDelete
  7. Could you explain how I would go about making my towers shoot the enemy which is closest to the end instead of closest to the tower itself, I've tried for a while but I'm getting stuck with the incompatibility of floats and vector2's..

    Any advice would be great, thanks in advance :)

    ReplyDelete
  8. Hey,

    The first thing you would need to do is make a property in the enemy class that gets the enemy's waypoints.

    Then in your towers GetClosestEnemy method instead of finding the smallest distance to an enemy, find the enemy that has the lowest number of waypoints left and that has the smallest DistanceToDestination.

    I hope that helps, feel free to ask if you need more help!

    ReplyDelete
  9. FireFly, do you want to concider a tutorial about making it multiplayer? That would be really awesome. I followed your tutorials since the beginning and I love them. I made my own version of it which is starting to look like a great game. But the art of game design is being different I guess. Thanks for everything, you already have a neat basis here for a full game! Thanks man.

    ~Arjan.

    ReplyDelete
  10. Hey Firefly, Thanks for this really sweet tutorial. It was just the start I needed with XNA.

    A lot of TD games are also available on smartphones. Is it possible to do a tutorial part on the scaling part of the game? How nice would it be to see your own perfect sprite upclose. ;-)

    Cheers, Tim

    ReplyDelete
  11. Thank you both for your kind words!

    Unfortunately I don't have enough experience with networking to write a tutorial on making the game multi-player :(.

    Tim, i'm not sure what you mean when you say the scaling part? And yes TDs are awesome on when played on portable devices (watch this space ;)! )

    ReplyDelete
  12. Hi FireFly,

    Sorry, I meant "2D zooming". My focus is a small device platform e.d. smartphone.

    Cheers, Tim

    ReplyDelete
  13. Great tutorials! Theyre very helpful to me. In the first tutorials, you said you would make a map editor for at the end of all the tutorials? I think this would help our everyone loads because, well, Ive never seen a tutorial for a map editor before. I think it would be nice. Could you possibly consider making a map editor tutorial?

    Thanks,
    Davis

    ReplyDelete
  14. "Anonymous said...

    I followed this as closely as I could but still get the same error as before, unfortunately.

    In the Wave class, when trying to add money or lose a life, I get:

    NullReferenceException was unhandled
    Object reference not set to an instance of an object.

    I have the reference to the player class etc, and there are no build errors.. I still have no idea why it gives me this because your tutorial fixed the build error I was getting! :( Any ideas?"

    Hi,
    i got the same error, please can anyone tell me what to do because everything i've done is not working.
    by the way,FireFly, your tutorials are simply the best, thanks a lot doing such a good work.

    ReplyDelete
  15. I figured it out, now it's working and no bugs at all !

    Cheers,
    Adrian

    ReplyDelete
  16. Thanks for the kind words Adrian, I'm glad you got it working! Just out of interest was it something missing in my code, or had you missed something out?

    ReplyDelete
  17. Hi FireFly, there was my couple mistakes but i'll fixed. I tried to change your code a little bit and missed something in the game class. Your code works perfectly...thanks again for your kind to share it with rest of as.
    Best regards,
    Adrian

    ReplyDelete
  18. FireFly, it would be great if you'll post a tutorial of how to upgrade towers and especially how to drag and drop a tower. These things are hard from my point of view.
    Your work is great.
    Thanks.

    ReplyDelete
  19. Hey Firefly, i want to have a healthbar or some sort of marker near the enemy of how much health it has. (maybe even X%). How would i go about doing this? I have tried multiple ways, but no result.

    ReplyDelete
  20. Hey I am considering writing a tutorial on how to do this because it is pretty simple to do.

    But until I post that you should have a read of this : http://www.xnadevelopment.com/tutorials/notsohealthy/NotSoHealthy.shtml

    I would put the health bar drawing code in the Wave.cs draw method inside of the foreach loop.

    Let me know if this helps!

    ReplyDelete
  21. i have been following that tutorial, but i have not tried putting it in the foreach loop. ill try that

    ReplyDelete
  22. I'm a total TD addict, having created some in StarCraft and Warcraft III.

    I've dedicated my entire working day to completing these tutorials (don't tell my chief), and as a .NET developer, I found it very easy to follow. You've really inspired me to create a few TD's of my own.

    I'm hoping the A-Star stuff doesn't blow my head off, as it's essential to getting my idea(s) off the ground.

    Thanks a lot for taking the time to write these tutorials.

    ReplyDelete
  23. "Anonymous said...

    I followed this as closely as I could but still get the same error as before, unfortunately.

    In the Wave class, when trying to add money or lose a life, I get:

    NullReferenceException was unhandled
    Object reference not set to an instance of an object.

    I have the reference to the player class etc, and there are no build errors.. I still have no idea why it gives me this because your tutorial fixed the build error I was getting! :( Any ideas?"

    I got the same error :(

    ReplyDelete
  24. Excellent tutorials!

    However, I'm still having issues with this error:

    "In the Wave class, when trying to add money or lose a life, I get:

    NullReferenceException was unhandled
    Object reference not set to an instance of an object."

    To those of you who fixed this, what needs to be done?

    ReplyDelete
  25. Same problem as the anno above, can anyone please enligthen me? :]

    Also Thanks to you FireFly for posting this great Tutorial! has helped me out alot :]

    ReplyDelete
  26. For all the people who are getting the NullReferenceException: On which line of code are you getting this exception? Have you tried putting a break point on this line to see what is actually null?

    Are you sure you are actually setting a reference to the player class and not just defining the field ie. have you got something like :

    this.player = player; (The THIS is very important)

    Let me know if this helps!

    ReplyDelete
  27. Hi there,

    great Tut, totaly loved it :)

    I have made my own TD from it and it works like a charm! Only have one little Issue left:

    Once the last wave is through, the game crashes. VS highlights the get { return CurrentWave.Peek();} in the CurrentWave Property. Stating: There is no more Element left in the Queue - NullReference

    Is there anyway to fix this somehow? I tried adding some if clause to the get{ } but that only helped until like wave 9 and then it crahsed again with some other Null-Reference :(

    My code looked like:

    public Wave CurrentWave
    {
    get
    {
    if(waves.Count > 0){
    return waves.Peek();
    }else{
    return null;
    }
    }
    }

    But as mentioned, it didn't work :(

    Any ideas on your end maybe?

    Cheers
    Hiro

    ReplyDelete
  28. Hello, this is a rather old guide but I'm getting the same error everyone else was getting, I've checked my code against the tutorial many times and its identical, yet I'm getting the error:

    NullReferenceException was unhandled
    Object reference not set to an instance of an object.

    ReplyDelete
  29. Are you sure you are calling:
    player = new Player(level, towerTextures, bulletTexture);
    before:
    waveManager = new WaveManager(player, level, 24, enemyTexture);

    ReplyDelete
  30. Hello thanks for the reply, I got it working now :)
    I was wondering if you could help me.
    I'm trying to make it so after every round, the game pauses, and the user has to press a button to start the next round.

    ReplyDelete
  31. The first thing I would do is add an event to the top of the WaveManager class:

    public event EventHandler OnWaveFinished;

    Next I would go to the update method and in the conditional that checks if the current wave is finished I would replace:

    waveFinished = true;

    with:

    if (OnWaveFinished != null)
    {
    OnWaveFinished(CurrentWave, EventArgs.Empty);
    }

    This will fire the OnFinished event. I would then move:

    waves.Dequeue(); to the top of the StartNextWave method and make the method public.

    Then just remove the other code from the update method that starts waves based on a timer if you don't want that.

    In the game class when the OnWaveFinished event gets fired I would show the start next wave button, and when that is clicked simply call the waveManager.StartNextWave method.

    Hope that helps!

    ReplyDelete
  32. So what exactly should I write in the game class? :S Sorry I'm kinda new with this.

    ReplyDelete
  33. Ok nevermind I got it with the way I was trying to do at the start... I was really frustrated yesterday when I couldnt get it to work thinking I was doing it majorly wrong, when I just realised right now, when I copy pasted the previous Clicked EventHandler from the last button I made, I didnt actually change the name.. >_______<
    Anyway thanks! :p

    ReplyDelete
  34. If you were to use my method then in the game class you could use the wave event the same way you would use a button event.

    And to start a wave you would do something like:

    Button button = new Button(...);
    button.Click += new EventHandler(StartNextWave_Clicked);

    void StartNextWave_Clicked(object sender, EventArgs e)
    {
    waveManager.StartNextWave
    }

    I'm glad you got it sorted!

    ReplyDelete
  35. Hey again, now to my next problem.
    I'm trying to make it when i hover over my towers that have been placed, a circle is drawn to show their firing radius. I have everything set up and working as follows:
    Basically its the same as the button hover thing, the texture changes when i hover over the tower, but the texture for the radius is 160x160 and it seems to cram itself into the 32*32 square.. not really sure whats making it do that. Any enlightenment? :)
    Do you have MSN / Skype etc because I'm sure it would be easier to contact on that! :P I know I'm gonna have more problems because I'm trying to make my game really unique from yours.
    Thanks in advanced!

    ReplyDelete
  36. I wish you continued these tutorials because they were helping me so much! Now I just need to know how to show the radius on the towers when mouseover and also when placing them, also was wanting to add upgrades to towers also, but I havent really put much thought into that. Praying for a new tutorial <3 But I understand if you can't :p

    ReplyDelete
  37. How are you drawing the circle texture? At I guess you are doing:

    spriteBatch.Draw(circleTex, new Rectangle(posX, posY, 32, 32), Color.White);

    You need to change the width of the rectangle from 32 to the texture size, or even better use the other spriteBatch overload:

    spriteBatch.Draw(circleTex, new Vector2(posX, posY) - new Vector2(circleRadius), Color.White);

    Hope that helps!

    ReplyDelete
  38. Hey there, thanks again! Its working good, only small problem when I subtract the circle radius vector it doesnt center the circle around the tower, it seems lopsided somehow... Not sure whats wrong with the maths

    ReplyDelete
  39. Nvm I got it :)
    Thanks again for all your help!

    ReplyDelete
  40. Ok now I have a new problem >_> Never ending problems I know..
    Its working great for my arrow tower, but for any of my other towers the hoverTexture doesnt seem to show up and I'm not sure why..

    ReplyDelete
  41. Hi, I've just noticed that my waves arent actually going higher than the 2nd wave, nothing happens after the 2nd wave finishes.. even though I have 20 waves in the game1.cs

    ReplyDelete
  42. Nvm fixed the wave problem.

    ReplyDelete
  43. Meh been trying to make the hoverTextures show up on my slow and aoe towers but they just dont, I really dont get it... any idea? :S

    ReplyDelete
  44. I want to show the number of waves i've finished, how do i do that?

    ReplyDelete
  45. @iSekz

    in the ToolBar.cs class, when drawing the font and string of text,

    just add another value

    string.Format("Gold: {0}, Lives: {1}, Wave Number: {2}",player.Money, player.Lives, waveManager.RoundNumber);

    something like that

    ReplyDelete