The Wizard - Part 3
Making a Sprite Duck
This is the third in a four part series to show you how to add some movement and action to your sprite. This first part was covered in "The Wizard - Part 1: Moving a Sprite on the Screen". The second part was covered in "The Wizard - Part 2: Making a Sprite Jump". Part three covers adding the ability to make your sprite have a little "ducking" animation.
Starting the game project:
We are going to be building off everything that we did in the second part of the Wizard series "The Wizard - Part 2: Making a Sprite Jump". 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:
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 Wizard ducking image used in the project from here. Once you have downloaded the image, add the new Wizard image to the Content folder in your game which should replace the existing Wizard image there. 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:
To start out, we are going to make some more enhancements to the Sprite class.We need to enhance the Sprite class to be able to switch between different images on a "sprite sheet". A sprite sheet is a single image file with multiple images in it. Often these images will be used to build frames or an animation or maybe represent different tiles in a game. It is a wise idea when possible to add as many images as you can to a single image file, but always keep the size of the file in mind. You don't want the dimensions of the image file to get to large and you don't want it's file size to grow to big. So keep a balance of making separate images and combining as often as possible.
When drawing from a sprite sheet, you don't want to draw every image in the sheet. Instead, what you want to do is specify which area in the sprite sheet represents the image that you want to draw. Imagine cutting a rectangular image out of the sprite sheet that you want to draw and giving that the the SpriteBatch. So you will want to know just how to define that rectangular area. You will need to know where the top left corner of the rectangle is and just how wide and high the rectangle is. That "source rectangle" now defines the area you want to be drawn from the original image.
That is the first enhancement we need to make to the Sprite class.
Add the following Source object and property to the top of the Sprite.cs class.
//The Rectangular area from the original image that
//defines the Sprite.
Rectangle mSource;
public Rectangle Source
{
get { return mSource; }
set
{
mSource = value;
Size = new Rectangle(0, 0, (int)(mSource.Width * Scale), (int)(mSource.Height * Scale));
}
}
The property re-calculates the Size anytime the Source is redefined. That way the size always takes into account that you might not be drawing the entire original image, but just a portion of that image.
Next, we need to change the Scale property to recalculate Size using source, it originally was using the height and width or the original image. Modify the Scale property in the Sprite.cs class to look like the following.
//When the scale is modified through he property, the Size of the
//sprite is recalculated with the new scale applied.
public float Scale
{
get { return mScale; }
set
{
mScale = value;
//Recalculate the Size of the Sprite with the new scale
Size = new Rectangle(0, 0, (int)(Source.Width * Scale), (int)(Source.Height * Scale));
}
}
The LoadContent method should now be modified to calculate Source and then use Source when calculating Size. Modify the LoadContent method to look like the following.
//Load the texture for the sprite using the Content Pipeline
public void LoadContent(ContentManager theContentManager, string theAssetName)
{
mSpriteTexture = theContentManager.Load<Texture2D>(theAssetName);
AssetName = theAssetName;
Source = new Rectangle(0, 0, mSpriteTexture.Width, mSpriteTexture.Height);
Size = new Rectangle(0, 0, (int)(mSpriteTexture.Width * Scale), (int)(mSpriteTexture.Height * Scale));
}
Finally, we need to change the Draw method to now use our Source rectangle when drawing instead of drawing the entire image. Modify the Draw method of the Sprite.cs class to look like the following.
//Draw the sprite to the screen
public void Draw(SpriteBatch theSpriteBatch)
{
theSpriteBatch.Draw(mSpriteTexture, Position, Source,
Color.White, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0);
}
Enhancing the Wizard class:
With the changes done to the base sprite class, it's time to begin modifying the Wizard class to give it the ability to "duck". To start things off we need to modify the State enum in the Wizard.cs class to look like the following.
enum State
{
Walking,
Jumping,
Ducking
}
State mCurrentState = State.Walking;
We have added a new state for our Wizard called "Ducking", we will use this to indicate when he is ducking since we don't want him to be able to jump or walk around when he is in that state.
Next we need to modify the LoadContent method. Modify the Load Content method of the Wizard.cs class to look like the following.
public void LoadContent(ContentManager theContentManager)
{
Position = new Vector2(START_POSITION_X, START_POSITION_Y);
base.LoadContent(theContentManager, WIZARD_ASSETNAME);
Source = new Rectangle(0, 0, 200, Source.Height);
}
We added a line of code here to set the Source rectangles for our Wizard. Remember, the Sprite class now uses Source to only draw a smaller portion of the original image. In our case, we want only the left side of our image drawn to start with. That's the portion of our image with the Wizard standing. So we defined a rectangle that started in the upper left corner of the image and is only 200 pixels wide.
You'll notice we set the SourceRectangle after LoadContent in the base sprite class is called. We need to do it in that order since the base Sprite class also sets Source and we don't want it to override ours.
Now, we need to begin make some changes to the Update method so that the actual action of "ducking" can begin. 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);
mPreviousKeyboardState = aCurrentKeyboardState;
base.Update(theGameTime, mSpeed, mDirection);
}
We just added a single line of code to the Update method. A call to a method named "UpdateDuck". We haven't created this method yet, so obviously that along is not going to be enough to make our Wizard start ducking (if only it were that simple!).
Now that we have our method call, let's go ahead and add the UpdateDuck method. Add the following method to the Wizard.cs class.
private void UpdateDuck(KeyboardState aCurrentKeyboardState)
{
if (aCurrentKeyboardState.IsKeyDown(Keys.RightShift) == true)
{
Duck();
}
else
{
StopDucking();
}
}
The UpdateDuck method checks to see if the right shift key is currently being pressed. If it is, then it calls the "Duck" method. If it isn't, then it calls the "StopDucking" method. So what this means is that our Wizard sprits is only going to "duck" while the right shift key is being pressed and when it's released, the Wizard will stop ducking immediately.
We called two new methods that we haven't written yet. Let's add the "Duck" method first. Add the following method to the Wizard.cs class.
private void Duck()
{
if (mCurrentState == State.Walking)
{
mSpeed = Vector2.Zero;
mDirection = Vector2.Zero;
Source = new Rectangle(200, 0, 200, Source.Height);
mCurrentState = State.Ducking;
}
}
The Duck method does the actual work of making the Wizard appear to be ducking. First, the Wizard can only duck if he's currently walking. We don't want him to be able to duck if he's in any other state like jumping. So if he is walking, then we first stop him from moving. We do that by zeroing out his Speed and Direction. Next, we change the "frame" of the Wizard image. This is just a way of saying that we change what Source image is now going to be drawn for the Wizard. We want the image on the right of our original image to be drawn, the one where the Wizard is hiding in his hate. So now we define a Source rectangle that starts at 200 pixels into the image and moves to the end of the image. Finally, we change the state of our Wizard to ducking.
Now that we have our Wizard ducking, we need him to stop ducking when we release the right shift key. Add the following StopDucking method to the Wizard.cs class.
private void StopDucking()
{
if (mCurrentState == State.Ducking)
{
Source = new Rectangle(0, 0, 200, Source.Height);
mCurrentState = State.Walking;
}
}
That's it! Go ahead and do a Build and move and jump and now duck your Wizard. You shouldn't be able to move or jump when the Wizard is ducking, and you shouldn't be able to duck when the Wizard is jumping.
Congratulations! You have successfully figured out how to get a sprite to duck. More importantly, you have been introduced to how to use sprite sheets and switch frames for a sprite. What modifications can you make? Could you change the code so that the Wizard could duck while Jumping? Could you change it so he could move around while ducking? What about using a different key for jumping?
Play with it, experiment and ask questions. Most of all make sure you’re having fun!