SpriteSheet Animation in MonoGame for Windows 8

August 31st, 2012 Leave a comment Go to comments

Using SpriteSheets in your 2D game is a recommended way to help performance while at the same time managing your assets. There’s a delicate balance between the number of SpriteSheets used for the assets in your game and the amount of data needed to be loaded in memory and sent to the graphics card. A SpriteSheet is simply a single texture (graphic) file containing a series of sprites that represent the various visual assets of your game. A common practice is to use a SpriteSheet for an animation sequence within your game.

An animation is just a series of frames of static images. which played over a specific time line, tricks the eye into thinking that there is fluid movement.

To better understand the concept, let’s take a SpriteSheet from the Platformer XNA Sample.

image

In this SpriteSheet, our Hero is celebrating by thrusting his fist into the air. A simple way to think how playing this animation works is to think of an animation flipbook. In this case you would cut out each sprite image, stack them one on top of the other, and flip through them quickly to show our Hero in motion.

Creating an Animation Sequence in MonoGame for Windows 8

Let’s walk through how to create this animation for a Windows 8 game. I could create my game using HTML5/JS or C++ but I’m more efficient in C# and XNA. Thankfully  MonoGame gives the capability to use my existing C# skills to create a game that I can sell in the Windows 8 Store. (If you want to see how to do this in HTML5, David Rousset has a great article on sprite animation in HTML5).

First, read through Bob Familiar’s series on getting MonoGame up and running on Windows 8. Also, review the change I had to make when I countered a problem with the ContentPipeline in my MonoGame project.

Once you’ve gone through the steps to get the MonoGame code and project templates on your machine, you’re ready to create your Windows 8 game.

If you already know the steps to create a MonoGame project and importing the ContentPipeline, feel free to jump to Step 5.

1.) In Visual Studio 2012, Create a new Project, Open the C# Project Template and select ‘Create a MonoGame Metro Application for Windows’

image

2.) Add the MonoGame.Framework.Windows8 project to your Solution and add a reference from your MonoGame project to the MonoGame.Framework.Windows8 project. (this will change once the binaries become available for MonoGame Framework for Windows 8).

image

3) Right-Click your game project and select Add|New Folder and give it the name ‘Content’

NOTE: MonoGame doesn’t have a ContentPipeline project type at this time. You need to jump out of Visual Studio 2012 and load up Visual Studio 2010 with XNA Game Studio 2010 and create a blank XNA project and fill your ContentPipeline with the assets you need for your MonoGame project. This process is outlined in Part 3 of Bob’s blog series.

For this example, I copied the Celebrate.png SpriteSheet from the Platformer sample (Platformer\Content\Sprites\Player\Celebrate.png) and added it to the ContentPipeline project in Visual Studio 2010.

Here is my XNA Game Studio project from Visual Studio 2010:

image

 

4) Compiling the XNA Game will produce a .XNB file. This is your ContentPipeline all compiled up. Add this .XNB file to your ‘Content’ folder in your MonoGame project.

In your MonoGame project in Visual Studio 2012, right-click on the ‘Content’ folder, select Add|Existing Item, and browse to your .XNB file from your XNA Game project (ie. C:\Users\{You}\Documents\Visual Studio 2010\WindowsPhoneGame1\WindowsPhoneGame1\bin\Windows Phone\Debug\Content\Celebrate.XNB).

image

Now on to the animation code…

5) First, we need to define some class-level variables to maintain some state information about our animation sequence. Add the following prior to the Game1() class constructor:

// the spritesheet containing our animation frames
Texture2D spriteSheet;
// the elapsed amount of time the frame has been shown for
float time;
// duration of time to show each frame
float frameTime = 0.1f;
// an index of the current frame being shown
int frameIndex;
// total number of frames in our spritesheet
const int totalFrames = 10;
// define the size of our animation frame
int frameHeight = 64;
int frameWidth = 64;
 
We know some information about our SpriteSheet such as the height and width of each of our frames (they should all be the same size) and the number of frames contained in our sequence. In this case, we have 10 separate sprites that make up our animation sequence.

6) In the LoadContent() method, load the SpriteSheet into the Texture2D spriteSheet variable:

protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);

spriteSheet = Content.Load<Texture2D>("Celebrate");
}

 

The Update() and Draw() methods are called in a constant loop, called the Game Loop. Update() handles any user input and game logic that needs to be processed since the last time the method was called (enemy movement, collisions, scoring, etc). The Draw() method is responsible for sending any graphical bits to the screen. The ideal situation is to have these two methods execute no slower than your target framerate (ie. 30fps – 60fps). Framerate is measured by how fast can I write the graphical bits (my Draw() method) to the screen per second.

7) In the Draw() method, we will place our logic to draw out our animation sequence over a period of time:

First we calculate the time its taken since we last drew one of our animation frames to the screen. frameTime contains our target time (.1 milliseconds) to draw our next frame in the sequence. When we’ve exceeded that time, we set our index to the next frame in the SpriteSheet and reset our time.

// Process elapsed time
time += (float)gameTime.ElapsedGameTime.TotalSeconds;
while (time > frameTime)
{
// Play the next frame in the SpriteSheet
     frameIndex++;
  
// reset elapsed time
     time = 0f;
}

To draw a loop sequence, we determine if our frameIndex has exceeded the number of frames in our SpriteSheet. If it has, we reset it back to frame 1.

if (frameIndex > totalFrames) frameIndex = 1;

The SpriteSheet is a single texture image that contains a sequence of images that are the same size. We use the known size and count information to calculate how big our frame is, and where in the sequence we need to pull the image out of the SpriteSheet. Calculating the position in the SpriteSheet is easily handled by creating a Rectangle based on our known information: frameWidth, frameHeight and frameIndex. Multiply the frameIndex by frameWidth to reference the appropriate frame in the sequence.

// Calculate the source rectangle of the current frame.
Rectangle source = new Rectangle(frameIndex * frameWidth,

0, frameWidth, frameHeight);

Next we calculate the position on the screen in which we want to draw our sprite. In this case, we’re just drawing out to the center of the screen:

// Calculate position and origin to draw in the center of the screen
Vector2 position = new Vector2(this.Window.ClientBounds.Width / 2,
this.Window.ClientBounds.Height / 2);
Vector2 origin = new Vector2(frameWidth / 2.0f, frameHeight);

The spriteBatch.Draw method outputs the spriteSheet based on all of our instructions. The ‘position’ is the location where to draw our sprite and ‘source’ contains the Rectangle coordinates for the frame contained within spriteSheet that we want to draw.

spriteBatch.Begin();



// Draw the current frame.
spriteBatch.Draw(spriteSheet, position, source, Color.White, 0.0f,
origin, 1.0f, SpriteEffects.None, 0.0f);



spriteBatch.End();

After following the delicate balance to get MonoGame up and running, the Content Pipeline loaded into your MonoGame project, and following the above steps, you should be able to hit F5 to run your Windows Store Game and watch our Hero throw his hands up in the air in celebration! (this is where I’d paste in an animated gif of the final result if I only knew how to do that with a Windows Store app)

Here is my final project if you get lost somewhere along the line. This solution does not include the source code for the MonoGame.FrameWork.Windows8 project. You’ll want to add your local copy to the solution to allow it to compile. Hopefully we can remove this step once the MonoGame Windows 8 binaries come available.

Source code: MonoGameSpriteSheetDemo.zip