Subscribe to So you think you can code?        RSS Feed
-----

Finally!

Icon Leave Comment
Well, I logged in this morning to find that my tutorial on animated sprites in XNA had finally been approved after something like 3 weeks. Glad someone finally got around to looking at it. Here's a direct link to the tutorial, feel free to flood the thread with any questions.

http://www.dreaminco...nimated-sprite/

And here's the tutorial itself posted on here just because I wanted it here:

Animated Sprite Tutorial:

This is actually the first tutorial I’ve ever written, so I hope I make this as clear as possible. This is going to be a simple tutorial on the basics of looping through the “frames” of a spritesheet to create the illusion of a walking character. Generally when I think of animations pulled from spritesheets, I think of the old school 8bit and 16bit RPG’s that were the glory days of gaming, so I’m going to use a simple 16 “frame” walk cycle for a character that I took about five minutes of my time to create using some online sprite sheet generator that I found several months ago. I can’t remember the site, and unfortunately, I didn’t bookmark it.

I’ll upload the file that I used just to make things simpler than having you find your own file that matches mine close enough to avoid any confusion but I take no credit for the creation of the image and unfortunately, as I mentioned above, I do not remember the site that is responsible for it.

The image I have is a 512x48 pixel PNG file. Each image is 32 pixels wide and 48 pixels tall. There are 4 images walking to the left, 4 images walking to the right, 4 images walking down, and 4 images walking up. With these 16 images, we can create a reasonably realistic walk cycle for any cardinal direction.

Once we have our sprite sheet, we’ll want to start an XNA project and import the image file into the content folder of the project, and I’m assuming you already know how to do this. If not, then check around other places because I’m not going to be covering that particular topic.

Once the file has been added to the project, we’ll start by adding a new class to the project by right clicking the name of the project in the Solution Explorer on the right side of the screen, choose Add->New Item, then choose Class from the window that pops up. We’ll name this class AnimatedSprite.cs. This will be the class that handles the majority of the logic behind creating an animated sprite. Once the class opens up, it’ll be mostly empty. For starters, at the top of the page, add some using statements for the XNA framework namespaces that we will be using in this file. Without these using statements, you’ll run into a lot of errors.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;



Now that we have that simple step out of the way we can start the fun stuff. First thing we’ll do is create some variables inside the class.
Texture2D spriteTexture;
float timer = 0f;
float interval = 200f;
int currentFrame = 0;
int spriteWidth = 32;
int spriteHeight = 48;
int spriteSpeed = 2;
Rectangle sourceRect;
Vector2 position;
Vector2 origin;



Let’s take a minute to explain these variables so that we’re completely clear on what they are and what they’ll be used for. Texture2D spriteTexture; is pretty self explanatory. It’s an 2d texture object that we are going to use as our sprite. float timer; is a floating point number that will be used for creating a certain amount of time that is required to pass before the sprite will move to the next “frame”. float interval = 200f; is a floating point number that we will use to determine how often we want to step to the next frame in the animation.int currentFrame; is used to keep track of the current frame of the animation. int spriteWidth and int spriteHeight are just used to keep track of the width and height of our sprite as their names suggest. int spriteSpeed; is used to set the speed that the sprite actually moves across the screen. Rectangle sourceRect; is the rectangle in which our sprite will be drawn.Vector2 position; and Vector2 origin; are 2d vectors that will be used to denote where the sprite is on the screen and the center of our drawn sprite.

Alright, now that those variables have been explained, let’s move on to the part where they’re actually being put to use. The following code just sets up the properties for getting and setting the above variables.
public Vector2 Position
{
	get { return position; }
	set { position = value; }
}

public Vector2 Origin
{
	get { return origin; }
	set { origin = value; }
}

public Texture2D Texture
{
	get { return spriteTexture; }
	set { spriteTexture = value; }
}

public Rectangle SourceRect
{
	get { return sourceRect; }
	set { sourceRect = value; }
}



To keep this tutorial from being too long, I’m not going to explain everything about this code. If you have questions about how these work, feel free to ask me, or do a little research and you’ll find plenty of sources that explain properties in depth.

The next bit we need to tackle is the constructor for this class. Following is the code for the constructor.
public AnimatedSprite(Texture2D texture, int currentFrame, int spriteWidth, int spriteHeight)
{
	this.spriteTexture = texture;
	this.currentFrame = currentFrame;
	this.spriteWidth = spriteWidth;
	this.spriteHeight = spriteHeight;
}



The constructor is basically just a method that initializes and creates instances of the data types that we create by creating a class. Each class that you create can be viewed as a new data type. The constructor’s parameters are often used as shortcuts for setting the values of the properties that you defined above the constructor.

In order to allow our sprite to move, we’ll want to check keyboard states to see which keys, if any, are currently pressed. At the top of the class, before the properties, and just before the variables that we declared earlier, we need two variables of type KeyboardState.

KeyboardState currentKBState;
KeyboardState previousKBState;



Now that we have variables created for storing keyboard states, lets utilize them and create a method to handle the movement of our sprite.

public void HandleSpriteMovement(GameTime gameTime)
{
	previousKBState = currentKBState;
	currentKBState = Keyboard.GetState();

	sourceRect = new Rectangle(currentFrame * spriteWidth, 0, spriteWidth, spriteHeight);

	if (currentKBState.GetPressedKeys().Length == 0)
	{
		if (currentFrame > 0 && currentFrame < 4)
		{
			currentFrame = 0;
		}
		if (currentFrame > 4 && currentFrame < 8)
		{
			currentFrame = 4;
		}
		if (currentFrame > 8 && currentFrame < 12)
		{
			currentFrame = 8;
		}
		if (currentFrame > 12 && currentFrame < 16)
		{
			currentFrame = 12;
		}
	}

	// This check is a little bit I threw in there to allow the character to sprint.
	if (currentKBState.IsKeyDown(Keys.Space))
	{
		spriteSpeed = 3;
		interval = 100;
	}
	else
	{
		spriteSpeed = 2;
		interval = 200;
	}

	if (currentKBState.IsKeyDown(Keys.Right) == true)
	{
		AnimateRight(gameTime);
		if (position.X < 780)
		{
			position.X += spriteSpeed;
		}
	}

	if (currentKBState.IsKeyDown(Keys.Left) == true)
	{
		AnimateLeft(gameTime);
		if (position.X > 20)
		{
			position.X -= spriteSpeed;
		}
	}

	if (currentKBState.IsKeyDown(Keys.Down) == true)
	{
		AnimateDown(gameTime);
		if (position.Y < 575)
		{
			position.Y += spriteSpeed;
		}
	}

	if (currentKBState.IsKeyDown(Keys.Up) == true)
	{
		AnimateUp(gameTime);
		if (position.Y > 25)
		{
			position.Y -= spriteSpeed;
		}
	}

	origin = new Vector2(sourceRect.Width / 2, sourceRect.Height / 2);
}



The code above might look like a lot, but it isn’t. It first checks to see if any keys are currently pressed. If no keys are pressed, then it checks to see what the current frame of the animation is and sets the frame to the start of that particular walk cycle so that it looks like the character stopped walking and returned to a resting position. Then we check to see which keys are pressed, the sprint section is completely optional and it’s just something I threw in there because I like to have the option to be able to move faster when I feel like it, if up, down, left, or right are pressed then we animate the sprite by calling the appropriate method that we will get to in just a few minutes, and as long as our sprite isn’t hitting the boundaries of the screen, which is where those magic numbers in the ifchecks came from, then we apply the speed to the position of the sprite moving in the appropriate direction.

Alright, it’s time to move to the actual methods that create the animation of the sprite and moves from one frame to another. To accomplish this, we use the following code:
public void AnimateRight(GameTime gameTime)
{
	if (currentKBState != previousKBState)
	{
		currentFrame = 9;
	}

	timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

	if (timer > interval)
	{
		currentFrame++;
		
		if (currentFrame > 11)
		{
			currentFrame = 8;
		}
		timer = 0f;
	}
}



Above is the code for the AnimateRight() method. The other three methods that we will create in a minute are just the same, except they deal with the proper frames inside our sprite sheet. After we change the current frame, the timer is reset and continues to increment. Following is the code for the [ilAnimateLeft()[/il], AnimateUp(), and AnimateDown() methods. I’m not going to go through and explain each one, since you should be able to figure out what each of them do just by reading the explanation of the AnimateRight() method from above.

public void AnimateUp(GameTime gameTime)
        {
            if (currentKBState != previousKBState)
            {
                currentFrame = 13;
            }

            timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            if (timer > interval)
            {
                currentFrame++;

                if (currentFrame > 15)
                {
                    currentFrame = 12;
                }
                timer = 0f;
            }
        }

        public void AnimateDown(GameTime gameTime)
        {
            if (currentKBState != previousKBState)
            {
                currentFrame = 1;
            }

            timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            if (timer > interval)
            {
                currentFrame++;

                if (currentFrame > 3)
                {
                    currentFrame = 0;
                }
                timer = 0f;
            }
        }

        public void AnimateLeft(GameTime gameTime)
        {
            if (currentKBState != previousKBState)
            {
                currentFrame = 5;
            }

            timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            if (timer > interval)
            {
                currentFrame++;

                if (currentFrame > 7)
                {
                    currentFrame = 4;
                }
                timer = 0f;
            }
        }



That is the end of our AnimatedSprite class. Don’t forget to add 2 closing brackets to the end of the file to close out our class to prevent errors.

From here we’ll open the Game1.cs class. At the top of the class, we’ll need to add a sprite batch declaration: SpriteBatch spriteBatch;. After declaring the sprite batch object, we also need to declare a variable of the type we just created. AimatedSprite sprite;. Now move down to the LoadContent() method and create an instance for our spriteBatch; spriteBatch = new SpriteBatch(GraphicsDevice);. We also need to create an instance for our sprite, sprite = new AnimatedSprite(Content.Load<Texture2D>(“Sprite_Sheet”), 1, 32, 48);. We also need to determine the starting position of our sprite, sprite.Position = new Vector2(400, 300);. I chose to start the sprite in the middle of the screen. 400 x 300 is the center of the screen as long as XNA is rendering to a window with the default size of 800 x 600. You can change the values inside the parenthesis if you want your sprite to start elsewhere. Following is the entire code for the LoadContent method just incase I was a bit confusing with the above paragraph:
protected override void LoadContent()
{

	spriteBatch = new SpriteBatch(GraphicsDevice);

	sprite = new AnimatedSprite(Content.Load<Texture2D>(“Sprite_Sheet”), 1, 32, 48);

	sprite.Position = new Vector2(400, 300);
}



The next place we need to add code is the Update method of the Game1 class. This will just be a single line, which can be placed at the top of the method just inside the brackets. sprite.HandleSpriteMovement(gameTime);. This call in the update method will call our HandleSpriteMovement method from the AnimatedSprite class that we wrote earlier. Each time the game is updated, this method will be called and the logic inside it will be performed. If you only want the sprite to animate at certain times, for instance when the game is not paused, then you can throw that call inside an ifcheck inside the update method.

The only real thing left to do is the add a few lines to our draw method to draw the sprite to the screen. Following is the entire code for the draw method:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.DarkOliveGreen);

            spriteBatch.Begin();
            spriteBatch.Draw(sprite.Texture, sprite.Position, sprite.SourceRect, Color.White, 0f, sprite.Origin, 1.0f, SpriteEffects.None, 0);
            spriteBatch.End();

            base.Draw(gameTime);
}



The code within the draw method is pretty simple, we tell the spritebatch to start and then we tell it to draw our sprite using the texture, position, rectangle, color, rotation, origin, scale, sprite effects, and layer depth. I won’t go into detail about sprite effects or layer depth because that’s beyond what this tutorial is supposed to cover. Finally, we tell the spritebatch to stop.

And that’s it! That’s all it takes to animate a simple spritesheet. If you have any questions, or want me to explain anything a little better, feel free to let me know. I actually enjoyed writing this, so I think I’ll do a few more over the next month or so when I find time. I’m open to suggestions.

Attached image(s)

  • Attached Image

0 Comments On This Entry

 

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

July 2020

S M T W T F S
   12 3 4
567891011
12131415161718
19202122232425
262728293031 

Tags

    Recent Entries

    Recent Comments

    Search My Blog

    2 user(s) viewing

    2 Guests
    0 member(s)
    0 anonymous member(s)