The Wizard - Part 4

Making a Sprite Shoot Fireballs

The Wizard - Part 4

Making a Sprite Shoot Fireballs

 

This is the fourth and final part in the Wizard series. We have covered making a sprite move, jump and duck. Now, we're moving onto creating projectiles and making your sprite shoot.

Starting the game project:

We are going to be building off everything that we did in the third part of the Wizard series  "The Wizard - Part 3: Making a Sprite Duck". Instead of creating a new XNA Game project, you can just download the source for that project and begin working from that game project.

Adding the images:

Fireball Now, let's add the new sprite image that we are going to be using in this tutorial to the game project. You can download the Fireball image used in the project from here. Once you have downloaded the image, add the Fireball image to the Content folder in your game. If you are having trouble remembering how to add an image to an XNA Game project, just follow the steps in this tutorial here and come back to this tutorial after you've refreshed your memory.

 

Enhancing the Sprite class:

We need to enhance the sprite class, but the change we're making is going to be a very small one. Some of the changes we will be making during this tutorial will require the ability for classes that inherit from our Sprite class to "override" the Draw method. When a method in an inherited class overrides a method in it's base class, that simply means the inherited classes version of the method will be called instead. This is typically done when an inherited class needs to either replace some functionality in a method or when it needs to do something before the functionality in the base class happens.

To indicate that an inherited class can override a method, the method must be marked as "virtual", this has nothing to do with the XNA framework, it's just basic object oriented programming and C# syntax. To make our Draw method virtual, modify the Draw method in the Sprite.cs class to look like the following.

        //Draw the sprite to the screen
        public virtual void Draw(SpriteBatch theSpriteBatch)
        {
            theSpriteBatch.Draw(mSpriteTexture, Position, Source,
                Color.White, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0);
        }

As you can see, nothing else changed, but the new keyword "virtual" was added to the the method signature.

Adding the Fireball class:

Our Wizard is going to shoot magic fireballs and we're going to make a "Fireball" class to represent those. The Fireball class is going to inherit from the Sprite class but add some fireballish type functionality. Let's get started. Right-click on the the game project in the Solution Explorer. Select, "Add", then pick "Class" from the add sub-men. This will open up the "Add New Item" dialog. Type in the name "Fireball" for your class and click the "Add" button.

Now we need to start adding some functionality to the Fireball class. Let's start by adding our XNA framework "using" statements to the top of the Fireball.cs class file.

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

Now, let's see the Fireball class up to inherit from the Sprite class. Change the class declaration for the Fireball to look like the following.

    class Fireball : Sprite

The Fireball class now inherits from the Sprite class and has access to all of it's public and protected members and methods.

To begin adding functionality to the Fireball class, we will start out by adding some constant and class level objects that we will be working with. Add the following code to the top of the Fireball.cs class.

        const int MAX_DISTANCE = 500;
 
        public bool Visible = false;
 
        Vector2 mStartPosition;
        Vector2 mSpeed;
        Vector2 mDirection;

MAX_DISTANCE is a constant that indicates how far our Fireball can travel before it disappears. Visible keeps track of whether the Fireball should be visible any longer, so when it's traveled it's maximum distance it wouldn't be Visible anymore.

We will use mStartPosition to store where the Fireball starts at so we can keep track of how far it has traveled. Speed and Direction will be used to move the Fireball.

Next, we want to loud our Fireball image. To do that, add the following "LoadContent" method to the Fireball class.

        public void LoadContent(ContentManager theContentManager)
        {
            base.LoadContent(theContentManager, "Fireball");
            Scale = 0.3f;
        }

We pass in the Asset name (Oh no! Refactor that "magic" string out! Did you see it? Remember, random strings and numbers in your code can lead to problems in the future.). That will load our Fireball image for us that we added earlier. Next, we scale the Fireball down from it's original size so that it looks appropriate for our Wizard sprite.

The Update logic is going to be a little different for the Fireball. Add the following Update method to the Fireball.cs class.

        public void Update(GameTime theGameTime)
        {
            if (Vector2.Distance(mStartPosition, Position) > MAX_DISTANCE)
            {
                Visible = false;
            }
 
            if (Visible == true)
            {
                base.Update(theGameTime, mSpeed, mDirection);
            }
        }

We start out by checking to see just how far this Fireball has traveled. We do that by using one of the methods of the Vector2 object called "Distance". With distance, you can pass in a starting vector2 and an ending vector2 and it will calculate just how far apart they are. If the fireball has traveled further then it's maximum distance, then we say it should no longer be visible.

Next, we check to see if the Fireball is still visible. If it is, then we pass it's current speed and direction (and the time that has elapsed) to the base Sprite class so that the Position for the Fireball sprite will be adjusted properly.

The Draw code will have some similar functionality. Add the following Draw method to the Fireball.cs class.

        public override void Draw(SpriteBatch theSpriteBatch)
        {
            if (Visible == true)
            {
                base.Draw(theSpriteBatch);
            }
        }

You can see that we used the keyword "override" in our Draw method. This is why we had to mark the Draw method in the base Sprite class as "virtual". Our Fireball doesn't want the base Draw functionality to happen automatically. When some class calls Draw on a Fireball, it first wants to make sure it's Visible, then if it is, it will go ahead and call the base Draw functionality. Overriding methods is very useful functionality!

Just one more method we need to add to our Fireball class. We want something to start the Fireball in motion. To do that, add the following "Fire" method to the Fireball.cs class.

        public void Fire(Vector2 theStartPosition, Vector2 theSpeed, Vector2 theDirection)
        {
            Position = theStartPosition;            
            mStartPosition = theStartPosition;
            mSpeed = theSpeed;
            mDirection = theDirection;
            Visible = true;
        }

The Fire method takes in a starting position, a speed and a direction object. It uses these to set it's own objects so that when Update is called the speed and direction will be set. It also sets the Visible on the Fireball to true indicating the Fireball can now be seen.

Now that we have our Fireball class created. Let's see how we're going to interact with it.

Enhancing the Wizard class:

Our Wizard is going to gain the ability to shot Fireballs, but to do that we're going to have to start writing some code. Let's start off first by creating some object to keep track of the fireballs. Add the following objects to the top of the Wizard.cs class.

        List<Fireball> mFireballs = new List<Fireball>();
 
        ContentManager mContentManager;

The Content Manager object will be used to create new Fireballs when we need them. The LoadContent method of the Fireball class needs a reference to the Content Manager so we need to have a reference hanging around to pass to it.

The "List" object gives us a way of creating multiple types of objects and keeping track of them. One of the neat things about the List object is you can tell it what type of object you are going to be keeping in it. In our case, we're going to be keeping track of Fireball classes so we use that in our declaration. (for more information on this type of thing, start reading up on C# generics. They're great!).

Next, we need to modify the LoadContent method. The LoadContent method is called every time the graphics device is reset. That means that all the content will need to be reloaded so not only our Wizard image, but all the Fireballs as well. Modify the LoadContent method in the Wizard.cs class to look like the following.

        public void LoadContent(ContentManager theContentManager)
        {
            mContentManager = theContentManager;
 
            foreach (Fireball aFireball in mFireballs)
            {
                aFireball.LoadContent(theContentManager);
            }
 
            Position = new Vector2(START_POSITION_X, START_POSITION_Y);
            base.LoadContent(theContentManager, WIZARD_ASSETNAME);
            Source = new Rectangle(0, 0, 200, Source.Height);
        }

We start by storing a reference to the ContentManager passed into the Wizard's LoadContent method. We want that reference hanging around so that when we have to add a new Fireball, it's available for us to pass to the Fireball's LoadContent method.

Next, we cycle through all the current Fireballs we have in our list and call LoadContent for them. The rest of the code was there from the previous tutorials in the series.

The Update method now needs to start Updating our Fireballs as well. Modify the Update method in the Wizard.cs class to look like the following.

        public void Update(GameTime theGameTime)
        {
            KeyboardState aCurrentKeyboardState = Keyboard.GetState();
 
            UpdateMovement(aCurrentKeyboardState);
            UpdateJump(aCurrentKeyboardState);
            UpdateDuck(aCurrentKeyboardState);
            UpdateFireball(theGameTime, aCurrentKeyboardState);
 
            mPreviousKeyboardState = aCurrentKeyboardState;
 
            base.Update(theGameTime, mSpeed, mDirection);
        }

We added a new line of code to call a method "UpdateFireball". So let's go ahead and add that new method. now. Add the following "UpdateFireball" method to the Wizard.cs class.

        private void UpdateFireball(GameTime theGameTime, KeyboardState aCurrentKeyboardState)
        {
            foreach (Fireball aFireball in mFireballs)
            {
                aFireball.Update(theGameTime);
            }
 
            if (aCurrentKeyboardState.IsKeyDown(Keys.RightControl) == true && mPreviousKeyboardState.IsKeyDown(Keys.RightControl) == false)
            {
                ShootFireball();
            }
        }

The UpdateFireball method does the leg work for making sure that each Fireball currently in our list gets Updated (so it can move). It does this by cycling through all the Fireballs currently in the list and calling their Update method. Next, it checks to see if the right control key has been pressed (and wasn't pressed previously). If that's true, then the Wizard will shoot a fireball.

Add the following "ShootFireball" method to the Wizard.cs class.

        private void ShootFireball()
        {
            if (mCurrentState == State.Walking)
            {
                bool aCreateNew = true;
                foreach (Fireball aFireball in mFireballs)
                {
                    if (aFireball.Visible == false)
                    {
                        aCreateNew = false;
                        aFireball.Fire(Position + new Vector2(Size.Width / 2, Size.Height / 2), 
                            new Vector2(200, 0), new Vector2(1, 0));
                        break;
                    }
                }
 
                if (aCreateNew == true)
                {
                    Fireball aFireball = new Fireball();
                    aFireball.LoadContent(mContentManager);
                    aFireball.Fire(Position + new Vector2(Size.Width / 2, Size.Height / 2), 
                        new Vector2(200, 200), new Vector2(1, 0));
                    mFireballs.Add(aFireball);
                }
            }
        }

Ok, there's a lot going on in this method. First, nothing happens unless the Wizards current state is Walking. At this point in time (unless you start making some changes to the code as you're playing), our Wizard can't shoot when in any other state (like ducking or jumping).

The next bit of code is probably overkill for this little sample, but I thought it was a good thing to go over so you have this little trick available for future projects. Creating and destroying objects is expensive. A much better technique is to keep them around, but have them in an inactive, dead or invisible state. Then when you need to create a new object of that type, you just check your list to see if there are any not being used and you re-use them.

So first we start out by setting a flag called "aCreateNew". We set this to true initially to indicate we have to create a new Fireball. Next, we cycle through all the fireballs in our list checking to see if any aren't currently being used. We know they're not being used because they won't be Visible any longer. When we find one that isn't visible, we change our flag to say we don't need to create a new one because we found one we are going to reuse instead.

Then, we call the "Fire" method on the Fireball to reset all it's own internal objects and start it on it's own merry little magic fireball way again.

The second part of the ShootFireball method covers the case that we couldn't reuse one of the existing Fireballs. For those cases, we have to create a new one. So if our flag still indicates we need to create a new one, create a new one we will do. We declare a new Fireball object, call LoadContent passing in our reference to the Content Manager and then we Fire the Fireball. Finally, we add taht Fireball to our list.

Now that we're shooting Fireballs, we need to start drawing them to the screen. Add the following Draw method to the Wizard.cs class.

        public override void Draw(SpriteBatch theSpriteBatch)
        {
            foreach (Fireball aFireball in mFireballs)
            {
                aFireball.Draw(theSpriteBatch);
            }
            base.Draw(theSpriteBatch);
        }

Again, we're overriding the base Sprite classes Draw method so we can draw our Fireballs first. Once we're done with that, we call the base classes Draw method so the Wizard will then draw.

That's it! Go ahead and do a Build CropperCapture26324_thumb44 and move and jump, ducks and set the screen ablaze with your mighty magic fireballs!

CropperCapture[272]

 

Congratulations! You have successfully added the ability for your Wizard to shot fireballs. You also gotten a taste for how to handle projectiles and multiple sprites on the screen. Can you think of some things in the project you could change? Would you make the Wizard shoot out other Wizards instead of fireballs? Could you make the size of the Fireballs larger and smaller? Could you make them move faster and slower? Could you make them look blue or green?

Play with it, experiment and ask questions. Most of all make sure you’re having fun!

Technorati Tags: