Texture Atlases - Part 2

Sections > 1 - 2 - 3

Creating an AnimatedSprite Class

In this part of the tutorial, we are going to create a class that will handle the texture atlas, and take care of the drawing for us.

Creating the Class

To create a new class, right-click on your project in the Solution pad and choose Add > New File from the popup menu. The New File dialog will appear. Select the General category on the left and choose the Empty Class template. Change the name (near the bottom) to AnimatedSprite.cs. Click on the New button when you have done this. The new class file will open up in the main editor window.

The first change we will want to make is to make the class public, so near the top of the file, find the line that says:

using Directives

Another thing we will need to do is include references to a couple of other assemblies, so that we can use the MonoGame libraries. Add the following two statements to the very top of the file with the other using statements:

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

Adding Instance Variables

Next, we will want to add the necessary instance variables, so add the following lines, just inside of the class declaration:

public Texture2D Texture { get; set; }
public int Rows { get; set; }
public int Columns { get; set; }
private int currentFrame;
private int totalFrames;

The variable Texture stores the texture atlas for our animation. Rows is the number of rows in the texture atlas. Columns is the number of columns in the atlas. Additionally, we need to keep track of which frame of the animation we are currently on, and how many frames there are total. These are stored in the currentFrame and totalFrames variables.

Adding a Constructor

The next thing to add to the class is a constructor, that we can use later to make a new AnimatedSprite object. We can do that with the following code:

public AnimatedSprite(Texture2D texture, int rows, int columns)
{
    Texture = texture;
    Rows = rows;
    Columns = columns;
    currentFrame = 0;
    totalFrames = Rows * Columns;
}

This constructor requires the user to give us a texture, along with the number of rows and columns in the texture atlas. We then assign these values to the appropriate instance variables. Additionally, we set the current frame to be 0, and we calculate the total number of frames which is simply the rows in the texture atlas multiplied by the columns.

The Update() Method

Next we will add an Update() method to our class which will change the current frame to the next frame. Add the following code to your class:

public void Update()
{
    currentFrame++;
    if (currentFrame == totalFrames)
        currentFrame = 0;
}

This simply increments the frame, and if it needs to start back over at the beginning, it does.

The Draw() Method

This method will probably be the most interesting part of this class. This is also where we will do some new stuff with 2D graphics. Add the following code as a method in your class:

public void Draw(SpriteBatch spriteBatch, Vector2 location)
{
    int width = Texture.Width / Columns;
    int height = Texture.Height / Rows;
    int row = (int)((float)currentFrame / (float)Columns);
    int column = currentFrame % Columns;
 
    Rectangle sourceRectangle = new Rectangle(width * column, height * row, width, height);
    Rectangle destinationRectangle = new Rectangle((int)location.X, (int)location.Y, width, height);
 
    spriteBatch.Begin();
    spriteBatch.Draw(Texture, destinationRectangle, sourceRectangle, Color.White);
    spriteBatch.End();
}

In this method, the first thing we need to do is determine which part of the texture we are going to draw to draw only the current frame. So we start off by calculating the width and height of the frame. We then need to calculate which row and column the current frame is located at.

In the second section, we calculate a "source rectangle", which is a rectangle within the texture (the source) that we want to draw. At this point, we also calculate a "destination rectangle" which is a rectangle that represents where the texture will be drawn.

Finally, we draw the correct part of the texture on the screen with a call one of the SpriteBatch.Draw() methods. This is a version of this method that we haven't seen yet, but takes a texture, a source rectangle, a destination rectangle, and a color. This will draw only the part of the texture that is used in the current frame.

The entire code for the AnimatedSprite class is below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
 
namespace TextureAtlas
{
    public class AnimatedSprite
    {
        public Texture2D Texture { get; set; }
        public int Rows { get; set; }
        public int Columns { get; set; }
        private int currentFrame;
        private int totalFrames;
 
        public AnimatedSprite(Texture2D texture, int rows, int columns)
        {
            Texture = texture;
            Rows = rows;
            Columns = columns;
            currentFrame = 0;
            totalFrames = Rows * Columns;
        }
 
        public void Update()
        {
            currentFrame++;
            if (currentFrame == totalFrames)
                currentFrame = 0;
        }
 
        public void Draw(SpriteBatch spriteBatch, Vector2 location)
        {
            int width = Texture.Width / Columns;
            int height = Texture.Height / Rows;
            int row = (int)((float)currentFrame / (float)Columns);
            int column = currentFrame % Columns;
 
            Rectangle sourceRectangle = new Rectangle(width * column, height * row, width, height);
            Rectangle destinationRectangle = new Rectangle((int)location.X, (int)location.Y, width, height);
 
            spriteBatch.Begin();
            spriteBatch.Draw(Texture, destinationRectangle, sourceRectangle, Color.White);
            spriteBatch.End();
        }
    }
}

Continue on to Part 3

Sections > 1 - 2 - 3


Troubleshooting.png Having problems with this tutorial? Try the troubleshooting page!