Saturday 25 September 2010

Tutorial 3 : The Enemy Class

To start with, we will be creating a new class called "Enemy.cs", this will be a base class for all our enemy's, whether they are bosses or fast units or just plain cannon fodder.

So to start with add a new class called "Enemy.cs", then add the following using statements :

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

From now on these 2 namespaces will need to be added to nearly every class, so I am just going to assume that you will remember to add these.

The first thing that we need to do is make our new class inherit from the "Sprite" class. To do this replace this line :

public class Enemy

with this :

public class Enemy : Sprite

Now we can access all of the methods and fields that we exposed in the Sprite class last time.


Every enemy is going to share some basic properties, so we will add these first, add these lines of code to the top of Entity.cs :

protected float startHealth;
protected float currentHealth;
 
protected bool alive = true;
 
protected float speed = 0.5f;
protected int bountyGiven;


These fields should be pretty self explanatory so I won't insult you by explaining them to you, however one thing to notice is these fields are marked as "protected", this means that any class that inherits from this class will be able to access them.

Next we will add some properties to expose some of these feilds, so that other classes will be able "check up" on our enemy's. Add these just under the feilds we just added :

public float CurrentHealth
{    
    get { return currentHealth; }    
    set { currentHealth = value; }
}

public bool IsDead
{    
    get { return currentHealth <= 0; }
}

public int BountyGiven
{    
    get { return bountyGiven; }
}


Now it's time to add a constructor to the class :

public Enemy(Texture2D texture, Vector2 position, float health, int bountyGiven, float speed)
    : base(texture, position)
{
    this.startHealth = health;
    this.currentHealth = startHealth;
    this.bountyGiven = bountyGiven;
    this.speed = speed;
}


We now have the basic outline of our enemy class, so lets get drawing us some cannon fodder. Go back into "Game1.cs" and at the top, just under where we defined our level add the following :

Enemy enemy1;

Now before we can initialize our enemy, we need to add in a new sprite for the to use, so add the following image to the content project :

Note : This texture should be called "enemy.png"

With that added, we can move onto initializing and drawing our enemy!

So in the "LoadContent()" Method, add the following :

Texture2D enemyTexture = Content.Load<Texture2D>("enemy");
enemy1 = new Enemy(enemyTexture, Vector2.Zero, 100, 10, 0.5f);

This code loads in the texture for the enemy, and then passes it to our "Enemy" in the enemy's constructor. The enemy will be created in the top left corner (0, 0) with 100 health, 10 gold in his pocket, and with a slow walk.

Now to draw our enemy, add the following code in the draw method after level.Draw(spriteBatch) but before SpriteBatch.End() :

enemy1.Draw(spriteBatch);

Now Run the project, and you should see our level, with a little white circle in the top left corner, and their we have a very simple enemy class.

Before we move onto the next tutorial, we just need to add in a couple more things to the "Enemy" class. So go to the enemy class and add in the following code :

public override void Update(GameTime gameTime)
{
    base.Update(gameTime);

    if (currentHealth <= 0)
        alive = false;
}


This code overrides the Sprites "Update()" method. The first line just tell the Sprite base class to update itself, then in the last two lines we just check if the enemy is dead.

In the next method, we are going to override the Sprites "Draw()" method, so that we can draw our enemy a little bit differently. But first we must add a new method to "Sprite.cs". Go to "Spirte.cs" and add the following method :

public virtual void Draw(SpriteBatch spriteBatch, Color color)
{
    spriteBatch.Draw(texture, center, null, color, rotation, 
        origin, 1.0f, SpriteEffects.None, 0);
}

This overload method allows us to "tint" a sprite with a certain colour.

Now, back to "Enemy.cs", add the following code just underneath the Update() method :

public override void Draw(SpriteBatch spriteBatch)
{
 
}

Hopefully there is no explanation needed here. First we are going to check if our enemy is still alive :

if (alive)
{

Next we are going to work out what percentage of health our enemy has lost :

float healthPercentage = (float)currentHealth / (float)startHealth;

Then we will create a colour based off this information, so that our player can see how much damage he had done to his enemy :

Color color = new Color(new Vector3(1 - healthPercentage,
    1 - healthPercentage, 1 - healthPercentage));
 
base.Draw(spriteBatch, color);

And that's it for our enemy class. Your draw method should now look like this :

public override void Draw(SpriteBatch spriteBatch)
{
    if (alive)
    {
        float healthPercentage = (float)currentHealth / (float)startHealth;

        Color color = new Color(new Vector3(1 - healthPercentage,
            1 - healthPercentage, 1 - healthPercentage));
 
        base.Draw(spriteBatch, color);
    }
}

So let's see if what we just did made any difference to our enemy ;). Go to "Game1.cs" and in the Update() method add the following :

enemy1.CurrentHealth -= 1;
enemy1.Update(gameTime);

This will knock off one health point from our enemy each frame. If you run the game now, you should see the enemy start off black, the gradually get whiter, and eventually disappears (He disappears because he is dead), and their we have it, our completed enemy class.

Here is the working code for this tutorial.

23 comments:

  1. I think the line

    Texture2D enemyTexture = Content.Load("enemy");

    needs to be

    Texture2D enemyTexture = Content.Load("enemy");

    also since the file name when you download it is apckuo perhaps it should be...
    Texture2D enemyTexture = Content.Load("apckuo");

    ReplyDelete
  2. Also, the instructions should say to go back to the Enemy class before you add this code...

    public override void Draw(SpriteBatch spriteBatch)
    {

    }

    ReplyDelete
  3. Thanks for pointing that out - Fixed :)

    ReplyDelete
  4. Just wanted to say that these tutorials are great! one problem though. You do not need to override the Draw method as you are overloading the actual method and this provides a seperate method signature and will yield an error if it does not find a method with that exact same signature. better to do public void Draw and call Draw(spriteBatch, color) in the method.

    ReplyDelete
  5. Thanks for the kind words! I'm not sure I understand your suggestion though, the Draw method simply overrides one of the Sprite classes draw methods :).

    ReplyDelete
  6. hey i get error: 'Tower_Defence.Enemy.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch)': no suitable method found to override

    dont know whats wrong..

    ReplyDelete
  7. Hey,

    Are you sure that your Enemy class is inheriting from the sprite class (see the start of this tutorial) and if so are you sure you added all the correct methods to the sprite class? (See previous tutorial).

    ReplyDelete
  8. I think the confusion with the 'Tower_Defence.Enemy.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch)': no suitable method found to override' error is from editing the sprite draw method.

    This tutorial does not edit the Sprite Draw method from the previous tutorial, it adds a second one. When finished with this tutorial, their should be one draw method in the Enemy class and two in the Sprite class.

    ReplyDelete
  9. ok i got it, now working

    ReplyDelete
  10. Hi,

    there is an error in the texture loading.
    game1 -> LoadContent() ->
    Texture2D enemyTexture = Content.Load("enemy");
    has to be:
    Texture2D enemyTexture = Content.Load("enemy");


    If you want to fade your enemy from green to red:
    Color color = new Color(new Vector3(1 - healthPercentage, healthPercentage, 0));

    anyway nice tutorials :D

    ReplyDelete
  11. The Comments delete spiky brackets...
    Content.Load
    (spiky bracket here) <
    Texture2D
    > (spiky bracket here)
    ("enemy");

    ReplyDelete
  12. Hey thanks for pointing that out, it turns out that you are not allowed to use the Inequality signs :S I have fixed it though.

    ReplyDelete
  13. There isn't a enemy texture in your tutorial :S

    ReplyDelete
  14. Agreed. Where's my ugly creep texture!?

    ReplyDelete
  15. It is there, it is just really hard to see, it is just above the line in italic.

    ReplyDelete
  16. I draw my enemy but he doesnt move anywhere, think you know what the problem is? feels like i dont give him any direction to move in.

    ReplyDelete
  17. The name 'enemy1' does not exist in the current context

    Not sure what to fix... Thanks

    ReplyDelete
  18. Error 1 Inconsistent accessibility: base class 'Tower_Defence_game.Sprite' is less accessible than class 'Tower_Defence_game.Enemy' C:\Users\Lewis\Documents\Visual Studio 2010\Projects\Tower Defence game\Tower Defence game\Tower Defence game\Enemy.cs 10 18 Tower Defence game

    It keeps saying this?

    ReplyDelete
  19. You need to make sure each class has the word public in front of it e.g :

    public class Enemy
    {

    }

    public class Sprite
    {

    }

    ReplyDelete
  20. when i try to change the enemy texture all i get is a black box. is there some speciel conditions the images has to meet?

    ex. transparancy, .png. speciel size for them to be drawn?

    ReplyDelete
  21. Give please link to enemy.png It's broken

    ReplyDelete
  22. Hi Roman,

    I will try to upload it later, but for now you can get it from the zip file linked to at the bottom of the tutorial!

    ReplyDelete
  23. Very thank to you for useful tutorial!!!

    ReplyDelete