Starting Small: XNA Framework Guide


In this instalment of the Starting Small series, we look at XNA (XNA is Not an Acronym) – a set of tools from Microsoft for developing games. The reasons this tutorial is focussing on XNA are simple: it is easy to use (when you know a bit about it), and it can be used to develop for Windows, Xbox and Windows 7 Phone. With the arrival of Xbox LIVE in South Africa, I’m sure that many developers will want to try their hand at developing for these platforms.

This tutorial assumes you are familiar with basic programming in C#.

First of all, you should download the tools required: XNA and Visual C# 2010, if you haven’t already.

The Structure of an XNA Project

Let’s take a look at the basic XNA structure. XNA splits game actions into five main groups: initialisation; content loading; content unloading; updating; and drawing. Each of these is implemented as methods for each game object class, and these methods are generally called automatically by XNA. A class need only implement those methods that it requires. These are fairly straightforward and easy to understand.

LoadContent Method

In general you can just load content in the LoadContent method given to you, and get away with it, but with larger projects you may find it a more elegant solution to write yourself a decent load screen system and load the content only when it is needed. We won’t cover that in detail here. XNA automatically calls the LoadContent method at the start of your game at the appropriate time to load all your content.

UnloadContent Method

For unloading content, I generally found that I could ignore the UnloadContent method, as the content manager in XNA handles unloading of all content that you load automatically. So unless you create custom content and import it using custom code, you won’t even need to do anything in there. XNA will automatically run this code at the end of the game when the player closes the game.

Update Method

The Update method is where all your game logic should reside. It takes a GameTime object as parameters, and it is a good practice to get into the habit of using it to make all your code work independent of frame rate. This will ensure that your game runs smoothly, and that physics and animations will have predictable behaviour at all frame rates. XNA automatically calls this method every update cycle.

Draw Method

The Draw method is where everything in the game is drawn. This method is only called 30 times per second unless you change it. XNA automatically calls the Draw method at a fixed interval (if possible) for a fixed FPS.

The next few sections will deal with specific aspects of XNA and how to do certain things using it. After going through this tutorial you will be able to apply the techniques without much difficulty and begin working on your own games.

The Main class

To begin with, we need a class that will contain our code. In this tutorial, we will only define a single class. In real games you will have dozens, possibly hundreds.

We will call the class Game1. It will extend the XNA class Game. Note that a Program class is automcatically created. It has the main entry point, which instantiates the Game1 class, and calls its run method.

Loading Content

XNA does not like working with content that is in hundreds of different formats, so the general rule of thumb is for you to add it to you project under Content, so that it can be converted to a single XNA format. For this tutorial, we will need an image to draw, so add it to the Content project, like this:

  1. Go to your Solution Explorer.
  2. Right click on the Content project (typically named <yourprojectname>Content).
  3. Select Add, then Existing Item.
  4. Navigate to your sprite and select it.
  5. Click on Add on the dialog.

You will now be able to load that image from code.

XNA converts all content types (images, sounds, models, etc.) to its own format (XNB files). Therefore you cannot have two filenames that differ only by extension in the same folder. The extension is not used when referring to the asset in code: to load ‘image1.png’ you will only use ‘image1’ as the name.

The other thing you need to know is that you have to declare a variable for the content you are loading. The type for images is Texture2D, so if you go up to the top of the Game1 class, under the declaration of spriteBatch, declare a variable to hold your image.

Finally, in the LoadContent method, use somevar = Content.Load<Type>(“myimage”) to load your image; somevar is the variable you made, Type is the content type (Texture2D in this case), and myimage is the name of your image without the file type.

public class Game1 : Game
{
   Texture2D someTexture;
 
   protected override void LoadContent()
   {
       someTexture = Content.Load("img1");
   }
}

2D Image Drawing

Being able to load content is all fine and well, but that knowledge isn’t much use without being able to draw it. Before drawing anything, we need to clear the screen. This is done with a call to GraphicsDevice.Clear. We also make a call to the Draw method of the base class at the end of the method. Our own drawing commands come in between these two calls.

Thankfully, drawing sprites is quite simple. We use spriteBatch to draw various things in 2D in XNA, and this is no exception for Texture2Ds.

The spriteBatch variable should be declared in the class, and initialized in the LoadContent method.

Then, in the Draw method, you need to call spriteBatch.Begin and spriteBatch.End in that order. This tells XNA you want to use spriteBatch to show things on the screen. In between these two lines of code you can use spriteBatch.Draw(…) and spriteBatch.DrawString(…).

Right now, all we are interested in is spriteBatch.Draw. We will only look at one overload of this method, which is the one that takes (Texture2D, Vector2, Color).

  • For the first argument, we will use the container variable we made.
  • The second argument takes a Vector2, and all this is just a point in 2D space to say where the upper left hand corner of the Texture2D will go. Use new Vector2(123,456) to see how it works. We will return to this parameter later.
  • The last argument is a Color, and you can change this if you want to change the colours of the texture being drawn. For now use Color.White.

If you run your game now, you should see your sprite displayed on a point on the screen. As an added exercise, take a look at the overloads for spriteBatch.Draw and you will see that you can rotate the Texture2D, draw only a section of it, and much more.

protected override void LoadContent()
{
   spriteBatch = new SpriteBatch(GraphicsDevice);
   someTexture = Content.Load("img1");
}
 
protected override void Draw(GameTime gameTime)
{
   GraphicsDevice.Clear(Color.CornflowerBlue);
 
   spriteBatch.Begin();
   spriteBatch.Draw(someTexture, new Vector2(32,64), Color.White);
   spriteBatch.End();
 
   base.Draw(gameTime);
}

2D Text Drawing

For writing text to the screen, we will need to load some more content, but this time you will need to add something new to the project:

  1. Right click on the Content project.
  2. Select Add, then Add New Item. A dialogue will come up giving the content items we can create using Visual Studio for XNA.
  3. Select Sprite Font, and give it a name.

A new tab will open in your project showing you the properties of the sprite font you created; you can fiddle with these; they are well documented.

Now, just like with Texture2D, you will need to declare a variable (of type SpriteFont) and load the sprite font you just created inside the LoadContent method, which you will be familiar with from the previous section. The next part you will find familiar as well: we turn to our spriteBatch for drawing text to the screen, but instead of spriteBatch.Draw(…), we use spriteBatch.DrawString(…) to draw the string.

As with the Draw method, you can do all sorts of wonderful things with the text you are drawing. As a simple demonstration we will use the first overload. It takes (SpriteFont, string, Vector2, Color). The Vector2 and Color have the same use as for Texture2D drawing, but you will notice you give a SpriteFont container and the text (in a string) to spriteBatch.

Before we move on, there is a trick to learn with DrawString: if you have a very long message to show, or multiple lines of text, you don’t have to call DrawString hundreds of times for each line, you can simply use C#’s new line marker (\n) in your string to split the lines.

protected override void LoadContent()
{
   spriteBatch = new SpriteBatch(GraphicsDevice);
   someTexture = Content.Load("img1");
   myfont = Content.Load("myfont");
}
 
protected override void Draw(GameTime gameTime)
{
   GraphicsDevice.Clear(Color.CornflowerBlue);
 
   spriteBatch.Begin();
   spriteBatch.Draw(someTexture, new Vector2(32,64), Color.White);
   spriteBatch.DrawString(myfont, "to show you\nthis works", Vector2.Zero, Color.Red);
   spriteBatch.End();
 
   base.Draw(gameTime);
}

Particles

Particles in XNA can be a very tricky thing to implement, and that is why for the purpose of this tutorial we will turn to a particle engine that someone else wrote: the<Mercury Particle Engine (MPE). If you glance down and think it is complex I must reassure you that it really isn’t; give it a try! This section was adapted from this YouTube video.

To use MPE:

  1. Download the version that corresponds with your version of XNA.
  2. Unzip the references for the platform which you are developing on (so mine would be the References | X86 folder for 32-bit windows) somewhere in your game’s project folder.
  3. On your Solution Explorer, right click on the References for your project and choose Add Reference.
  4. Under the Browse tab, navigate to the folder you unzipped, and add the two *.dll files that are there.

The next big step is to add some references to our code. Right at the top of the file, under the lines saying using Microsoft.Xna…; add the following using statements:

using ProjectMercury;
using ProjectMercury.Emitters;
using ProjectMercury.Renderers;
using ProjectMercury.Modifiers;

Declare variables for ParticleEffect and Renderer. This is how we initiate them in our game’s constructor:

particleEffect = new ParticleEffect
{
   //A particle effect is essentially a list of Emitters
   new Emitter
   {
      Budget = 1000, //the number of particles allowed
      Term = 3f, //how long a particle "lives" in seconds
      Name = "FirstEmitter", //the name of the effect
      BlendMode = EmitterBlendMode.Alpha,
      ReleaseQuantity = 3, //the number of particles to emit
 
      //the rotation of the spawned particle
      ReleaseRotation = new VariableFloat { Value = 0, Variation = MathHelper.Pi },		
 
      ReleaseScale = 64f, //the speed they move
      ReleaseSpeed = new VariableFloat { Value = 64f, Variation = 32f },
 
      //the next important thing is modifiers,
      //these change the behaviour of the particles
 
      Modifiers = new ModifierCollection
      {
         //just as an example
         //make particles fade away as their life expirs
 
         new OpacityModifier
         {
            Initial = 1f, //start at full opacity
            Ultimate = 0f, //finish off not visible
         },
      },
   },
};
 
particleRenderer = new SpriteBatchRenderer
{
   //the renderer needs to know what it uses to render
   GraphicsDeviceService = graphics,
};

The code above may look scary, but if you read the inserted comments you will see it is simpler than it looks.

The next step is to move to our Initialize method, and initialize our particleEffect using particleEffect.Initialise(); and after that to move to our LoadContent method, and use particleRenderer.LoadContent(Content);.

After that line we need to give the emitter the texture to use as a particle. You could do this using point sprites instead of using the SpriteBatchRenderer, but this is fairly easy to use already. After calling the particleRenderer’s LoadContent, you need to give the particleEffect a loaded texture using ParticleEffect[0].ParticleTexture = Content.Load<Texture2D>("particle");.

protected override void LoadContent()
{
   spriteBatch = new SpriteBatch(GraphicsDevice);
   someTexture = Content.Load("img1");
   myfont = Content.Load("myfont");
   particleRenderer.LoadContent(Content);
   particleEffect[0].ParticleTexture = Content.Load("particle");
}

To use the particle effect we no longer have to jump through hoops. We simply trigger the effect we want somewhere in our game logic. We use particleEffect.Trigger(new Vector2(120, 120)); and pass in the time elapsed in seconds.

protected override void Update(GameTime gameTime)
{
   if(/* some input event; see below*/)
      particleEffect.Trigger(new Vector2(Mouse.GetState().X,Mouse.GetState().Y));
 
   particleEffect.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
}

The very last step is to add a single line to our Draw method that uses our renderer to draw the particles, particleRenderer.RenderEffect(particleEffect); .

protected override void Draw(GameTime gameTime)
{
   GraphicsDevice.Clear(Color.CornflowerBlue);
 
   spriteBatch.Begin();
   spriteBatch.Draw(someTexture, new Vector2(32,64), Color.White);
   spriteBatch.DrawString(myfont, "to show you\nthis works", Vector2.Zero, Color.Red);
   spriteBatch.End();
 
   particleRenderer.RenderEffect(particleEffect);
 
   base.Draw(gameTime);
}

If you want to know more about the types of emitters, the modifiers and the engine in general, you should head on over to the MPE documentation page and take a look around. You can also look at some of the effects you can achieve with this engine.

Sounds

To move off from doing things that people can see, how about giving them things to hear? XNA has two methods for adding sound to your project, using XACT, or the built in SoundEffect and Song classes.

If you want to use XACT, you can read about it on the MSDN website.

First off, get hold of a sound file that you want to use. I use the free tool sfxr to generate sounds.

For sound effects we will follow the same method of importing our content that we used before, except that this time we use the type SoundEffect. You will also need a SoundEffectInstance variable.

Once you load your SoundEffect you can use the SoundEffect.CreateInstance(); method to give a value to your SoundEffectInstance variable.

protected override void LoadContent()
{
   spriteBatch = new SpriteBatch(GraphicsDevice);
   someTexture = Content.Load("img1");
   myfont = Content.Load("myfont");
 
   particleRenderer.LoadContent(Content);
   particleEffect[0].ParticleTexture = Content.Load("particle");
 
   soundEffect = Content.Load("blip");
   soundInstance = soundEffect.CreateInstance();
}

You can then simply call the Play method of when you want to play the sound, usually within the Update method.

A few things you should note:

  • You can stop a SoundEffectInstance mid-way through playing, or create a new instance if you want to be able to play the sound at shorter intervals than you can with just Play() (due to it first finishing a single play through before a call to Play() is accepted).
  • You can also control the volume, pitch and pan of the SoundEffectInstance using code.

If you want to try your hand at the Song class built in for music, be sure to read Playing .mp3 and .wma Audio Files in XNA by RB Whitaker.

Basic Input

There are 4 main methods of input available to you in XNA: Keyboard; Mouse; Gamepad; and Gestures (Windows 7 Phone only). For simplicity’s sake we will only touch on the keyboard and mouse input for now.

First off, if you look at the code you started with in your XNA project, you will see that there is already a very simple example of how to use input. This is used to exit the game:

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();

The game looks to see if the back button was pressed on controller number 1. Keyboard input follows a similar structure, notably you don’t ‘pick’ a keyboard like you do for gamepads, and you do not check a button state. To exit the game using the Enter button, you would use the following code:

if (Keyboard.GetState().IsKeyDown(Keys.Enter)) this.Exit();

Finally, if you wanted to get things from the mouse, like clicks (hopefully you noticed the structure pattern) you do the same for the mouse:

if (Mouse.GetState().LeftButton == ButtonState.Pressed) this.Exit();

You can also get the mouse position from the Mouse.GetState().

What you chose to do with the input is up to you, but if you would like to expand your input to make it more powerful be sure to take a look at this extension class I wrote specifically to get the most out of my XNA projects.

protected override void Update(GameTime gameTime)
{
   // Allows the game to exit
   if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
      this.Exit();
 
   if (Keyboard.GetState().IsKeyDown(Keys.Enter))
      this.Exit();
 
   if (Mouse.GetState().LeftButton == ButtonState.Pressed)
      particleEffect.Trigger(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
 
   if (Mouse.GetState().LeftButton == ButtonState.Pressed)
   {
      particleEffect.Trigger(new Vector2(Mouse.GetState().X,Mouse.GetState().Y));
      soundInstance.Play();
      soundInstance.Pitch = 0.05f;
   }
 
   particleEffect.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
 
   base.Update(gameTime);
}

All the best for your experimentation, if you have any questions be sure to leave a comment below.

Download

StartingSmall_XNA.zip (399 KB)

Further Reading


avatar

About Ernest Loveland

The zombie of the team, edg3 is kept in dark rooms (much like engine coders) most of the time, just to keep up with the social norm. He constantly takes up projects that are too big for him, in the hopes that, one day, zombies will be recognised as people! [Articles]