Sgt. Conker We are "absolutely fine"

24Aug/1018

Article: Programming your first XNA 4.0 game for PC, Xbox 360 & Windows Phone 7: Pong

by Thomas "Mister Helmut" Altenburger

Programming your first XNA 4.0 game

I’d like to introduce the XNA framework to you with this very simple and straightforward tutorial. We will see what XNA is and how its basic features works to continue with the programming of your first game, a simple Pong clone for Windows. At last, we’ll try to convert it to the Xbox 360 and Windows Phone 7. This tutorial assumes that the readers have a basic C#/.Net understanding. I hope that it will help you to understand the basics of the framework and that it will motivate you to go further in. The article should be suitable to complete beginners in game programming.

About this tutorial: I wrote this tutorial back in the days of XNA 2.0 beta and used it as an introductory course I taught to master degree students. Since then, it evolved to support XNA 4.0 beta with added Window Phone 7 development.

English not being my native language, please forgive and/or report any English oddities.

This tutorial targets XNA 4.0 beta. It will be updated when the final version will be released. It uses some new features of XNA 4.0 so it will not run out-of-the-box on previous XNA version (mainly due to the usage of Viewport.Bounds and changed SpriteBatch.Draw arguments order).

Required assets: download link.

The XNA framework

XNA is a free development framework by Microsoft, enabling cross-platform game development thanks to simple and comprehensive programming tools. The supported platforms are Windows PC’s, the Xbox 360, Zune media players and Windows Phone 7. The framework integrates itself into Visual Studio 2010 (as of XNA 4.0), including the free Express edition, and features the following components:

  • A powerful .NET API to handle graphics, sounds, videos, inputs, networking and storage;
  • An audio development tool, XACT, to build complex cross-platform sound banks;
  • A debugger capable of remote debugging Xbox 360 and Zune games;
  • A Windows Phone 7 emulator so that you don’t need to own such a device to debug WP7 games.

XNA has been designed for hobbyist and independent developers and builds upon a strong community. If you have any problems during your XNA journey, don’t hesitate to ask for help at the official XNA community, the creators club. If correctly and gently exposed, your problem will most likely be resolved with the help of XNA experts or the XNA team itself.

Last but not least, the creators club allows you to release and sell your creations on the Xbox 360 or Windows Phone 7 marketplaces.

First steps to the XNA framework

In order to begin with the framework, let’s review the requirements before setting up our environment. You’ll need a computer with the following specifications (see XNA 4 specs):

  • Windows Vista or Seven (or their “server” equivalent);
  • A DirectX 9.1c capable graphic card (or better) with up to date drivers and DirectX runtime; A DirectX 10 capable graphic card if you’d like to debug Windows Phone 7 XNA projects;
  • Windows Phone 7 Development Tools (which includes XNA 4.0 and an Express edition of Visual Studio 2010).

If you’re a student, you may be able to get a copy of Windows Vista/Seven and/or a professional release of Visual Studio through the MSDN AA of your school (if it has subscribed to it). So go ask the IT manager of your school for further detail. If you’re school don’t have access to the MSDN AA, you can still pretend to student advantages thanks to the Microsoft’s Dreamspark program (sign up and follow the instructions).

If you’re not a student or simply don’t want to bother with a professional edition of Visual Studio, the Express edition (included in the WP7 dev tools) is fairly enough for XNA purpose. Another version wouldn’t bring you more features unless you have other programming need than XNA.

When you’re fully equipped and may now install the WP7 dev tools. You’re now ready to go.

Note about XNA 4.0 beta: commercial copies of Visual Studio aren't supported by XNA 4.0 beta.

Creating your first project

Start up Visual Studio and create a new project by clicking the “File/New Project…” menu. Here you’ll have a list of the available project templates. Give the name and directory you want to your project and under the Visual C# item, pick “Windows Game (4.0)”.

The project you’ve just created is now composed of the following elements:

  • The “Properties” directory, holding application manifest (not-so-important stuff for our needs);
  • The “References” meta-directory, listing all the .Net assemblies your project needs;
  • The “Content References”, listing the linked Content Pipeline (we’ll see what it is soon);
  • The “App.config” file, holding basic .Net setup, no need to edit it (for a XNA game);
  • The “Game.ico” file, is the icon of the executable file of your game;
  • The “Game1.cs” file, is the main source code file of a XNA project (I’ll detail this later);
  • The “GameThumbnail.png” file, is the icon of your game that will be displayed on the Xbox 360 and Windows Phone 7 marketplaces;
  • The “Program.cs” file, is the entry point of your program (initializing your Game1 class).

Alongside your project, you’ll find another project having the same name with a “Content” suffix. This represents the Content Pipeline of your project, a very important XNA component.

The Content Pipeline

The Content Pipeline has an almost explicit name. Its purpose is to manage your game assets (graphics, sounds, 3D models…) from the importation up to the execution with barely any hassle. Its main features are:

  • Importing the most common asset file formats and transparently load them into comprehensible C# objects;
  • Ease of use (one line asset loading);
  • Automatically links dependent assets (3D models, textures, shaders…);
  • Ensures that your assets are cross-platform, loaded and displayed the same way;
  • Offers on-the-fly assets compression/decompression;
  • Bake up your assets and bundle it with you game.

The use of the Content Pipeline is mandatory for non-Windows deployment. You can still load your assets the “hard way” from files on PC if you’d like to.

You can create as many Content projects as you want but it is more convenient to stick with one per game project. Remember the “Content References” in your game project? This is where you link your project to your Content project. By default, your project is linked to the already created Content project (you can check this by unfolding the “Content References”).

Now for the supported file formats, here’s the list:

  • 2D graphics: .bmp, .png, .jpg;
  • 3D models: .x, .fbx;
  • Shaders: .fx;
  • Audio: .mp3, .wav, .wma;
  • Video: .wmv;
  • Font: TrueType;
  • Any XML, text or binary files.

You may find many other format importers on the web or code one yourself if you need to (http://msdn.microsoft.com/en-us/library/bb447754.aspx).

Adding assets to your project

To add assets to your project, right click on your Content project, and “Add/Add an existing item” and pick your assets. You can also organize your assets into folders or add assets just by drag’n’droping files to your Content project.

The Game class

Now open up the Game1.cs file. You should notice that the Game1 class is inherited from a class named Game. The Game class is provided by XNA and manages the game loop (more generally the game workflow). It is composed of the following methods:

  • Initialize: called after the construction of the class, it ensures that all the class fields are constructed and ready to be initialized. The purpose of this method is (as you may have guessed) to initialize class members (in the same way of the Winforms’ Initialize method);
  • LoadContent: called after Initialize, this method is meant to be used to load your game assets;
  • UnloadContent: called during the disposal of the Game class and ensure that your game assets are disposed correctly when you exit it (it doesn’t mean that you don’t have to take care of your memory allocation);
  • Update: called every time before the Draw method and should be used to update your game engine. This will be the place of collision handling, inputs handling, game world updating etc. Its GameTime argument propose services to know the amount of time elapsed since the last Update call. This is very useful to synchronize your game engine;
  • Draw: called in an infinite loop, it is meant to draw your game. Even if it is possible, for the sake of clean code, you should avoid writing any logic update here.

The Update and Draw methods represent the Game Loop and are called in an infinite loop so that your game is constantly refreshed and running. XNA default settings set the loop frequency to 60 loops per second, meaning that your game should run at 60 frames per second (as long as your Update/Draw don’t take too much time). You can adjust the settings to void this limit so that your game runs as fast as your Update/Draw allow it to, but we’re not going to cover this in this tutorial.

The following figure depicts the general workflow of a game:

You’re free to not use the Game class but it provides useful services behind the Game Loop and if you’re coding yourself such a class, you’ll most likely end up with a pretty similar one. So why bothering re-writing the wheel? Plus the fact the Game class ensures the workflow of your game.

The Game1 class comes with two fields initialized:

  • GraphicsDeviceManager graphics: manages what is called the “graphical context”. More accurately, it allows you to setup the graphics card states (i.e. screen resolution, vertical synchronization, anti-aliasing, etc.) and to get information about the hardware capabilities;
  • SpriteBatch spriteBatch: provides services for 2D drawings. This will be the most important object covered by this tutorial.

Initializing and drawing 2D graphics

We will now take a look at how one can import 2D graphics and display it. You will need some game assets in order to continue. I’ve prepared a bundle with everything you need to follow this tutorial: grab it there.

Now take a look at the file named texture1.bmp. It is a simple image with 2D graphics. Notice that there are many single graphic entities inside the picture. An individual graphical entity is called a sprite and if an image contains many sprites, we’re in front of a sprite sheet.

Other terms do exist to describe sprites and sprite sheets. Like tiles and tile sets. If you have already toyed with some “RPG maker”, you should be familiar with their meanings. Like a sprite sheet, a tile set is an image containing several tiles, a tile being an independent rectangle of your image. To the difference with sprites, tiles inside a tile set are usually the same size, forming a regular grid. You may have noticed that tiles can be disposed side by side to form complex graphics, like a puzzle, using several times the same tile. This technique is commonly used in 2D games (RPG, side-scroller…) and was historically used because of the lack of memory of the 8/16bit systems.

Let’s import the texture: right click on your Content project, create a new folder named “Textures”, and add the existing texture1.bmp to this folder. It should now appear in your project. Right click on the texture1.bmp item and select “properties”, this will obviously show the asset properties. You’ll see a bunch of stuff, the one that is important for now is the “Asset Name” which should be set to “texture1”. This is the internal name of your asset, not its filename. XNA set a default name corresponding to the filename without its file extension, but you can freely change it. Keep in mind it has nothing to do with the filename.

Now, inside your Game1 class, as a class member, add a new member to host our texture. To this purpose, the Texture2D is intended manage your 2D texture (as you could have found out by yourself).

Texture2D myTexture;

We now need to code the actual loading of the asset, so go inside the LoadContent method and add the following line of code:

myTexture = Content.Load<Texture2D>(@"Textures/texture1");

The Content object is a default Game class member representing your Content project. Everything linked to your Content project should be accessed through the Content member. Its Load function is generic and will allow you to easily load any supported type of asset by specifying the wanted type to the Load<Type> template. To load an asset, just specify the desired type and the asset name (not its file name). If you try to load an unsupported type for the given asset, you’ll encounter an exception at runtime. The “@” before the asset name is a C# operator specifying that the special characters of the following string don’t need to be escaped. It is way more convenient to use it when dealing with asset locations since it will avoid you from doubling all the dashes (making your code more readable). Notice that you access assets as if they were files in the hierarchy of your Content project, but with assets names as their filename (this may be a bit puzzling at first glance).

Take a look at the content of the Draw method:

protected override void Draw(GameTime gameTime)
{
      GraphicsDevice.Clear(Color.CornflowerBlue);
      // TODO: Add your drawing code here
      base.Draw(gameTime);
}

The first line cleans the entire buffer so that the whole screen is reset to a blue color (the infamous CornflowerBlue). The last line calls the Game base Draw method and should always be the last instruction of your Draw method. I will not cover this feature as we will not use inheritance and game components.

The SpriteBatch.Draw(…) method

To draw 2D graphics, the only thing you will need is the SpriteBatch.Draw(…) method. But beforehand, take a look at how the screen is addressed by XNA:

axes.png

As you can see, the screen origin is in the upper left corner and goes positively to the opposite screen corner. This is a bit strange if you’re used to the Cartesian system. The unit is the pixel, meaning that (with default XNA screen resolution of 800x480) the X axis goes up to 800 and the Y one to 480. Textures are addressed the same way.

Here’s one the SpriteBatch.Draw(…) overload header:

SpriteBatch.Draw( Texture2D, // the texture to use for drawing
Rectangle, // the rectangle where you want to draw on the screen (in screen coordinate) Rectangle, // the part of the texture you want to draw (in texture coordinate)
Color); // the color hue of the drawing, Color.White will give a default hue

Now, considering our texture1, here’s an example call and the expected result:

spriteBatch.Begin();
spriteBatch.Draw(
      myTexture,
      new Rectangle(10, 20, 50, 100),
      new Rectangle(0, 0, 50, 100),
      Color.White);
spriteBatch.End();

The main difficulty in understanding 2D drawing is to understand the difference between source and destination rectangles. Have a look at the following illustration.

sprite.png

On the left, we have the texture containing the objects we’d like to draw, but we only want to draw a portion of that texture. The source rectangle defines this portion and must use the texture coordinates. Then, you define where on the screen you want that portion to be drawn by defining the destination rectangle (in screen coordinates). Note that the destination rectangle doesn’t have to be as wide/high as the source rectangle. Different sizes will result in stretched drawing.

SpriteBatch.Begin(…) / End()

You may notice that the SpriteBatch.Draw call is surrounded by two other methods, namely Begin and End. Begin could mean “ok mister GPU, here’s what you’re going to draw and how you’re going to draw it”. It tells the GPU to draw what’s coming and the properties to apply, like the usage of AlphaBlend, the order in which the sprites are going to be drawn etc.

The End method tells to the GPU that the Draw calls are complete and that he can now draw everything with the properties it have been configured.

You have to know that the CPU will tell the GPU what to draw only when you call the End method. The result of this method is the transfer of the drawing primitives to the GPU and transferring data between the CPU and the GPU is very expensive. So try to put as many Draw calls as possible between the Begin and End statements, this will greatly improve your game performance by reducing transfers to the GPU. Using several Begin/End is mainly intended to use different drawing parameters or to ensure that some elements are drawn before/after other elements.

Coding a Pong clone in 5 minutes

You should now master the basics of XNA. Despite the title of this section, we’re not going to write a Pong clone in 5 minutes, but if you were confident in XNA, you should be able to do it.

Defining the basics

Ok, what do we need to make a Pong game?

  • Two playable bars (2 x Rectangle);
  • A ball (1 x Circle);
  • A tennis field;
  • A score display;
  • Motivated supporters.

Inside the assets package you should have already downloaded (if not so, grab it here), you should find the following files:

  • Objects.png: an image file containing the graphics for the two bars and the ball;
  • Grass.png: an image file, a grass texture;
  • Bounce.wav: a sound file for ball bounces;
  • Supporters.wav: a sound file for goals.

Add all of them to your Content project.

Let’s now define all those entities in our project, as Game1 class members.

// our pong entities
Rectangle blueBar;
Rectangle redBar;
Rectangle ball; // since there’s no "circle" class in XNA, we’ll simulate it with a bounding rectangle box

// our pong clone textures
Texture2D grass;
Texture2D spriteSheet;

// our sound effects
SoundEffect ballBounce;
SoundEffect playerScored;

We now need to initialize our three Rectangle structures and we’ll do it in the (suspense…) Initilize method:

protected override void Initialize()
{
      // initializing our entities
      blueBar = new Rectangle(
            32, // x coordinate of the upper left corner of our rectangle
            GraphicsDevice.Viewport.Bounds.Height / 2 - 64, // y coordinate of the upper left corner
            32, // its width
            128); // and its height
      redBar = new Rectangle(
            GraphicsDevice.Viewport.Bounds.Width - 64, // x coordinate of the upper left corner of our rectangle
            GraphicsDevice.Viewport.Bounds.Height / 2 - 64, // y coordinate of the upper left corner
            32, // its width
            128); // and its height
      ball = new Rectangle(
            GraphicsDevice.Viewport.Bounds.Width / 2 - 16, // x coordinate of the upper left corner of our rectangle
            GraphicsDevice.Viewport.Bounds.Height / 2 - 16, // y coordinate of the upper left corner
            32, // its width
            32); // and its height

      base.Initialize();
}

Explanation:

We want our bars to be 32x128 pixels and be placed left and right to the screen and perfectly centered. Defining a Rectangle consist of four parameter: the x coordinate of the upper left corner (in screen coordinate), the y coordinate of the upper left corner, the width of the rectangle (32) and its height (128).

Noticed this kind of math?

GraphicsDevice.Viewport.Bounds.Height / 2 - 64

This is how we vertically center our bars. GraphicsDevice.Viewport.Bounds is a Rectangle provided by XNA that will always represent the actual bounds of the screen. If the resolution of your game is 800x480, the bounds will be Rectangle(0, 0, 800, 480). So if we want to center something, we just have to consider the half of the screen, minus the half of the rectangle we’d like to be centered (or else only its upper left corner will be centered).

If you understood all this, you shouldn’t have difficulties understanding our ball definition. We’d like our ball to have a 32 pixels diameter so we bound it with a 32x32 Rectangle (since no circle class exists in XNA, up to you write one). Yes, using a Rectangle to simulate a circle isn't very accurate, but as long as our game is simple enough, we just don't care about it (the purpose of this tutorial isn't to be super accurate).

Let’s now load the assets we’ve defined earlier, so go to the LoadContent method. The following code should be straightforward if you read the introduction of this article:

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

      // load our textures from the Content Pipeline
      grass = Content.Load<Texture2D>(@"Textures/Grass");
      spriteSheet = Content.Load<Texture2D>(@"Textures/Objects");

      // load our sound effects from the Content Pipeline
      ballBounce = Content.Load<SoundEffect>(@"Sounds/Bounce");
      playerScored = Content.Load<SoundEffect>(@"Sounds/Supporters");
}

Drawing the scene

At last, we will draw the entire scene but beforehand, I’ll introduce to you a simple problem.

The painter’s problem: even if you never heard about it, you should be familiar to it. When a painter would like to drawn a landscape on the background and a house in front of it, it has to first complete the background and then paint the house over it. If he did the house first, he would encounter problems detailing the background at the house’s edges. Graphics API works pretty much the same way but are more restrictive: everything that is drawn will recover what’s already drawn if it overlaps. So you have to draw graphics according to their distance from the camera.

Currently, what I said is not completely true since there’s some hardware magic called the Z-buffer which sorts everything out even if you draw everything in a random order. But the Z-buffer has been designed for 3D purposes and when dealing with 2D graphics, we’re no more working with Z coordinate so we have to sort this ourselves.

XNA offers mechanisms to sort 2D graphics via the SpriteSortMode argument of the SpriteBatch.Draw method but it is cleaner to draw things in the right order and to tell XNA to draw directly (SpriteSortMode.Immediate) because: 1) your code will be much more readable 2) it will be faster at execution.

Let’s draw our background first:

protected override void Draw(GameTime gameTime)
{
      GraphicsDevice.Clear(Color.CornflowerBlue); // clear all the screen with a blue color

      // draw the grass background
      spriteBatch.Begin();
      spriteBatch.Draw(
            grass, // use the grass texture
            GraphicsDevice.Viewport.Bounds, // stretch the texture to the whole screen
            // GraphicsDevice.Viewport.Bounds is Rectangle corresponding to the actual viewport (meaning the entire screen no matter the resolution), only available as of XNA 4.0
            Color.White);
      spriteBatch.End();

What’s new here? We’re using the Viewport.Bounds as our destination Rectangle, why? Do you remember that this Rectangle always represent the whole screen resolution? We’re just telling the Draw method to stretch the entire grass texture to the entire screen. And we’re done with our background. At this state, if you try to run the game (by pressing F5 to debug it), it should display our nice grass tennis field.

game (by pressing F5 to debug it), it should display our nice grass tennis field.

grass.png

What comes after the background? The bars and the ball should be on the same plane. We can draw all of them at the same time since there’s no risk that they overlap but, wait… all the assets of those entities are in the same single texture. Once again, do you remember the usage of “source rectangle”? (Check the introduction if the answer is no). Before drawing our elements we need to define a source rectangle for each of them to access individual portion of the texture. Look at what’s inside the texture and how it is placed:

spritesheet.png

The following three source rectangles should be easy to understand (define them as Game1 class members):

// source rectangles of our graphics
Rectangle blueSrcRect = new Rectangle( // blue bar src rectangle
      0, // upper left corner x-coordinate of the blue bar inside the spriteSheet
      0, // upper left corner y-coordinate
      32, // width
      128); // height
Rectangle redSrcRect = new Rectangle( // red bar src rectangle
      32, // upper left corner x-coordinate of the red bar inside the spriteSheet
      0,
      32,
      128);
Rectangle ballSrcRect = new Rectangle( // ball src rectangle
      64, // upper left corner x-coordinate of the ball inside the spriteSheet
      0,
      32,
      32);

We now have everything up to draw the remaining elements. Here’s what comes after our backgrounds drawing:

// draw the entities (bars and ball)
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); // setup alpha blend to support transparency
// draw the red bar
spriteBatch.Draw(
      spriteSheet, // use the sprites texture
      redBar, // the rectangle where to draw the bar on the screen
      redSrcRect, // the source rectangle of the bar inside the sprite sheet
      Color.White);
// draw the blue bar
spriteBatch.Draw(
      spriteSheet,
      blueBar,
      blueSrcRect,
      Color.White);
// draw the ball
spriteBatch.Draw(
      spriteSheet,
      ball,
      ballSrcRect,
      Color.White);
spriteBatch.End();

Press F5, and see that our game almost look like it is finished. There’s still all the gameplay to implement but our graphics are setup and we’re now free to toy with the gameplay.

nogameplay.png

Coding the gameplay

Handling the inputs

Now that we’re done with the Draw method, we’ll take a look at the Update method, so go at the corresponding line of code, we’re going to examine its default content:

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) // if the Back gamepad button is pressed
      this.Exit(); // exit our game

The GamePad class allows getting the current state of an Xbox 360 gamepad, being plugged to a PC or to your 360. Its GetState(PlayerIndex) method returns the GamePadState of the player corresponding to the given PlayerIndex.

A GamePadState object has a Buttons member, listing every up-down buttons and their state (A, B, Y, X, RB, LB, Back, Start, and Buttons on Sticks). If you want to know if a button is pressed, just compare a button to a ButtonState. Buttons of the directional pad works the same way and their states are stored in the DPad member of a GamePadState. The other inputs of a gamepad are different, they’re continuous (Sticks’ X & Y axes and Triggers) so you can’t compare them to a ButtonState since they are floating point values. When in default position, those controls have 0.0f value. Triggers go up to 1.0f when fully pressed and the axes range from -1.0f up to 1.0f depending the direction of the axes. (X = 1.0f and Y = -1.0f would mean that the stick is in the lower right corner).

Getting the keyboard state works pretty much the same way. Let’s add a keyboard control to the default code:

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || // if the Back gamepad button is pressed
      Keyboard.GetState().IsKeyDown(Keys.Escape)) // or if the Escape key is down
      this.Exit(); // exit our game

Keyboard.GetState() should be simple to understand now. But what is this IsKeyDown method? Unlike a GamePadState, KeyboardState don’t hold directly fields with keys’ states, you have to ask for a state with the IsKeyDown / IsKeyUp methods. Actually, GamePadState has a similar mechanism and propose the IsButtonDown / IsButtonUp methods.

Moving the bars

Our bars are represented by the redBar and blueBar rectangles, so if we want them to move, when need to modify their Y position (in a Pong game, bars only move along the Y axis). We will use the keyboard as our main input method, up to you to andle two different gamepads (this will be covered later).

// handling keyboard inputs
if (Keyboard.GetState().IsKeyDown(Keys.E)) // E key down ?
      blueBar.Y -= 10; // move the blue bar up
else if (Keyboard.GetState().IsKeyDown(Keys.D)) // D key down ?
      blueBar.Y += 10; // move the blue bar down
if (Keyboard.GetState().IsKeyDown(Keys.Up)) // Up key down ?
      redBar.Y -= 10; // move the red bar up
else if (Keyboard.GetState().IsKeyDown(Keys.Down)) // Down key down ?
      redBar.Y += 10; // move the red bar down

Quite simply isn’t it? If you launch the game and try to move the bars (with the E/D and Up/Down keys) you’ll notice that the bar can go behind the screen. We don’t want that behavior since they should stop at screen borders.

// limit the bars movement to the screen bounds
if (redBar.Y < 0) // upper bound
      redBar.Y = 0; // limit
if (blueBar.Y < 0)
      blueBar.Y = 0;
if (redBar.Y + redBar.Height > GraphicsDevice.Viewport.Bounds.Height)
      redBar.Y = GraphicsDevice.Viewport.Bounds.Height - redBar.Height;
if (blueBar.Y + blueBar.Height > GraphicsDevice.Viewport.Bounds.Height)
      blueBar.Y = GraphicsDevice.Viewport.Bounds.Height - blueBar.Height;

Now the bars don’t move outside the screen. The only trick resides in the handling of the lower bound: don’t forget to consider the bar height (the Y coordinates being the one of the upper left corner).

Ball’s motion

In order to make the ball moving, we need something to represent its velocity. Using a 2D vector seems to be the way to go. Fortunately, XNA already have everything at your disposal. Here we define a 2D vector (Vector2) for our ball velocity (as a Game1 class member):

// ball speed
Vector2 ballVelocity = Vector2.Zero;

And back in the Update method, we can now make our ball move by adding its velocity to its current position:

// move the ball
ball.X += (int)ballVelocity.X;
ball.Y += (int)ballVelocity.Y;

But, our ball isn’t really moving yet. Notice that we initialized the velocity to zero speed. We need to set a positive speed somewhere so the gameplay can begin. We will do this by adding a separate method we can re-use to re-initialize the ball every time we need it (when players score):

private void InitBall()
{
      int speed = 5; // default velocity
      Random rand = new Random();

      // randomize the ball orientation
      switch (rand.Next(4))
      {
            case 0: ballVelocity.X = speed; ballVelocity.Y = speed; break;
            case 1: ballVelocity.X = -speed; ballVelocity.Y = speed; break;
            case 2: ballVelocity.X = speed; ballVelocity.Y = -speed; break;
            case 3: ballVelocity.X = -speed; ballVelocity.Y = -speed; break;
      }

      // initialize the ball to the center of the screen
      ball.X = GraphicsDevice.Viewport.Bounds.Width / 2 - ball.Width / 2;
      ball.Y = GraphicsDevice.Viewport.Bounds.Height / 2 - ball.Height / 2;
}

We have a default velocity defined and a random number generator to add some unpredictability to the ball direction. In a switch statement, we make the ball go randomly in one of the four diagonal. Then, we set the ball’s position to the center of the screen, and the gameplay shall begin.

We can now call our newly created method to initialize the game. Back in the Update method:

// handling ball initialization
if (Keyboard.GetState().IsKeyDown(Keys.Space))
      InitBall();

This code means that every time we press the space bar, the ball is reset. Meaning that even during the gameplay, one can re-init the ball. That’s not a correct behavior but we don’t care since we’re aiming a playable game, not a polished one ;-) . Keep in mind that this is for educational purpose.

Worlds collide

If you tried to run the project, you’ll see that the ball is not bouncing on screen bounds. In the Update method, we add the required code:

// collision handling
if (ball.Y < 0 || // if the ball reach the upper bound of the screen
      ball.Y + ball.Height > GraphicsDevice.Viewport.Bounds.Height) // or the lower one
{
      ballVelocity.Y = -ballVelocity.Y; // make if bounce by inverting the Y velocity
}

This is similar to the way we managed the bars’ movement, with the particularity that instead of blocking the movement, we invert the Y velocity so the ball bounce.

We also need to manage the collision with the bars. It is a little bit different, since we’re going to check for Rectangle to Rectangle collisions. To this purpose, XNA is making our life a lot easier since the Rectangle class has a very useful method to test if two Rectangle are colliding:

if (ball.Intersects(redBar) || // if the ball collide with blue bar
      ball.Intersects(blueBar)) // or the red one
{
      ballVelocity.X = -ballVelocity.X; // make if bounce by inverting the X velocity
}

Very simple: we just check if the ball (which is a Rectangle) is colliding with either one of the bars, and invert the X velocity to make it bounce.

Here we are! We have a fully playable gameplay. The game only need some polish and it is almost ready for huge commercial success (if we were in the 70’s by the way…).

Scoring

To add some competition, we will handle a simple score counter and display. We’ll use two more Game1 class members to manage the scores:

// scores
int blueScore = 0;
int redScore = 0;

We need to detect if the ball goes behind a bar in order to increment the corresponding score. We’ll do it in the Update method:

// score handling
if (ball.X < 0) // red scores
{
      redScore++;
      InitBall(); // re-init the ball
}
else if (ball.X + ball.Width > GraphicsDevice.Viewport.Bounds.Width) // blue scores
{
      blueScore++;
      InitBall(); // re-init the ball
}

Same math as before to check screen bounds. When the ball goes behind the left or right bound, a player just scored and we re-initialize the ball.

Displaying the score involves to draw some text. Drawing text with graphics API is a bit problematic, text fonts are not ready-to-be-drawn graphics and their structure is not suitable with how graphics cards operate. Prior to use a font, it has to be processed or converted to a suitable asset. Some tools allow you to do so by generating a sprite sheet containing one sprite per character in the font. I let you imagine how boring it is to write a helper class able to seamlessly use this sprite sheet. Graphics API like SDL or OpenGL don’t do this out-of-the-box but some useful libraries exist.

XNA has the feature to handle fonts easily. Let’s have a look at it. We need to add a special type of asset to our Content project: right on the Content project, add a new file, select the Sprite Font item and name it “ScoreFont.spritefont”. An XML file should be showing up after you added it. This file will hold the properties of the font we want to use: the font name, its size, its type (bold, italic…) and other not-so-important stuff. We want the score to be displayed with big readable font, so change the size to 18 and the type to “Bold” (case sensitive!):

<Size>18</Size>
...
<Style>Bold</Style>

Like any asset, we have to define the corresponding variable and to load it. As a Game1 member:

SpriteFont font;

And then in the LoadContent method:

// load our score font
font = Content.Load<SpriteFont>(@"ScoreFont");

All we have to do now is to draw the text, the same way if it was 2D graphics with the SpriteBatch (in the Draw method, after the grass drawing so it isn’t underneath):

// draw the score
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
spriteBatch.DrawString( // draw our score string
      font, // our score font
      blueScore.ToString() + " - " + redScore.ToString(), // building the string
      new Vector2( // text position
      GraphicsDevice.Viewport.Bounds.Width / 2 - 25, // half the screen and a little to the left
      10.0f),
      Color.Yellow); // yellow text
spriteBatch.End();

Note that drawing text needs a dedicated method, DrawString. It takes as argument the font, the string to display, a 2D vector for the screen coordinate where we want the text to be displayed (remind that it consider the upper left corner of the text box), and finally, the color of the text.

Sound blasting

Last but not least, the sound. We already have our two SoundEffect ready (ballBounce & playerScored). Since XNA 3.0, playing a sound is simple as pie: SoundEffect.Play(). All we have to do, is to call the play method when the ball bounce and when a player scores. Here’s the code update:

// collision handling
if (ball.Y < 0 || // if the ball reach the upper bound of the screen
      ball.Y + ball.Height > GraphicsDevice.Viewport.Bounds.Height) // or the lower one
{
      ballVelocity.Y = -ballVelocity.Y; // make if bounce by inverting the Y velocity
      ballBounce.Play(); // bounce sound
}

if (ball.Intersects(redBar) || // if the ball collide with blue bar
      ball.Intersects(blueBar)) // or the red one
{
      ballVelocity.X = -ballVelocity.X; // make if bounce by inverting the X velocity
      ballBounce.Play(); // bounce sound
}

// score handling
if (ball.X < 0) // red scores
{
      redScore++;
      playerScored.Play(); // hurray !
      InitBall(); // re-init the ball
}
else if (ball.X + ball.Width > GraphicsDevice.Viewport.Bounds.Width) // blue scores
{
      blueScore++;
      playerScored.Play(); // hurray !
      InitBall(); // re-init the ball
}

Intermediary conclusion

full.png

Our Windows game is now complete! At least what I wanted to expose. Our prototype is far from being perfect, but one of the goals of this article is to give you opportunity to express yourself in the polishing of the game. You may copy/pasted everything but now, it’s up to you to enhance it. Here’s a list of thing you may consider doing:

  • Enhance collision handling: our model is very rough and far from a true physical model, plus the fact that the bar-to-ball collision is poorly managed (if the ball collide with another border than the front one, you can expect strange behaviors);
  • Enhance the beginning of the game: in a classic Pong, the ball should go randomly in any direction while our game only throw the ball with 45° angles (more randomness is needed!); when a player score, the ball should be thrown at the him next (and not randomly);
  • Add a menu;
  • Handle the end of the game: our prototype doesn’t stop the score;
  • Add an artificial intelligence: pretty easy, just apply a movement to the enemy’s bar if the ball is up or down the middle of the bar;

Porting our Pong clone to the Xbox 360 and testing it

To run a game on the Xbox 360, we need to setup a new project, a 360 one. When it comes to port an existing project, XNA allow you to copy your current project and to create a port with one click. Right click on your Windows project, and select “Create a Copy of Project for Xbox 360”. Tadam, you’ve successfully ported your game. Note that this “copy” isn’t really a copy: all the source files are the one from the original project, they’re just shared between all the ports. This means that if you modify the Windows project, the changes will be applied to the Xbox 360 version as well. You’ll see that you know have two Game1.cs files (one in the Windows project, and one in the 360 one) but actually, there’s only one file, shared. The fact that it appears in all the projects is a little bit misleading.

Running the game on your 360

Please note that the XNA 4.0 beta doesn’t allow deploying games on an Xbox 360 yet. The following process is valid as of XNA 3.1 and should remain the same with the final XNA 4.0 version. I’ll update the article once it is released. Meanwhile, it is advised to skip this part since if you create an Xbox 360 project inside XNA 4.0 beta, it will prevent you from debugging any other project since you can't set a target device for the Xbox 360 port.

Prior to be able to run code on your Xbox 360, you need your Xbox to be on the same LAN as your computer (I suppose you know how to setup a network) and a Creators Club license. This license is mandatory to publish games on the Xbox and Windows Phone marketplaces and to debug code on the gaming console. This is because Microsoft host and promote your games on the marketplaces and it is a good way to regulate the community by letting in only serious people (plus the fact that Microsoft want to stay in control of what’s being executed on the 360, for obvious security reasons). The license has a yearly cost of $99 and can be acquired from the Xbox Live marketplace or from xbox.com.

There’s a possibility to get a free trial license enabling you to debug code on your console, but not to sell games (as well as not having access to premium content and forums on the creators club). You may find a trial license via a MSDN AA account, if you’re a student/teacher and if your school has subscribed to the program. If you don’t know if your school can get you a MSDN AA account, go ask the person in charge of your school’s computers. Another way to a trial license is to (still) be student and subscribe to the Microsoft’s Dreamspark program.

Once you have successfully registered your license, you can start downloading the XNA launcher to your 360. You’ll find it on the games marketplace along with the Indie Games. It should be named “XNA Game Studio Connect”. This program will only run if you’re connected to the Xbox Live and if the connected GamerTag has a valid creators club license. Run it and go back to Visual Studio.

You now have to link your PC to your Xbox 360. This is to ensure that only trusted computer can deploy trusted code to a trusted console. In Visual Studio, click on the “Add a New Device”.

add_device.png

Choose to add an Xbox 360 console. Visual Studio should now ask you to enter a keycode, this code should be displayed on your 360 if you’ve successfully started the XNA launcher. Enter the code and validate. If everything went ok, you’re now ready to go and you won’t need to do this step anymore. If not, check out your network setup.

When you’re ready, select the Xbox 360 as the target of your application.

change_target.png

Before launching our Pong game we need to adjust the screen resolution to fit the native Xbox 360 resolution of 1280x720. To do so, we will add a conditional pre-processing code so that the screen resolution code is only considered when the project is compiled to target the Xbox 360 (in the Game1 constructor):

#ifdef XBOX360
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
graphics.ApplyChanges();
#endif

And run the Xbox 360 project. You now have your game running on your TV! You can also debug the game like if it was running on the computer by putting some break points etc. Remote debugging works pretty darn well.

We now have a problem: we didn’t handle the gamepad so the game isn’t playable. The only thing you can do, is to press the “back” button to exit the game. The update is simple to achieve:

// handling keyboard and gamepad inputs
if (Keyboard.GetState().IsKeyDown(Keys.E) // E key down ?
      || GamePad.GetState(PlayerIndex.One).DPad.Up == ButtonState.Pressed)
      blueBar.Y -= 10; // move the blue bar up
else if (Keyboard.GetState().IsKeyDown(Keys.D) // D key down ?
      || GamePad.GetState(PlayerIndex.One).DPad.Down == ButtonState.Pressed)
      blueBar.Y += 10; // move the blue bar down
if (Keyboard.GetState().IsKeyDown(Keys.Up) // Up key down ?
      || GamePad.GetState(PlayerIndex.Two).DPad.Up == ButtonState.Pressed)
      redBar.Y -= 10; // move the red bar up
else if (Keyboard.GetState().IsKeyDown(Keys.Down) // Down key down ?
      || GamePad.GetState(PlayerIndex.Two).DPad.Down == ButtonState.Pressed)
      redBar.Y += 10; // move the red bar down
// handling ball initialization
if (Keyboard.GetState().IsKeyDown(Keys.Space)
      || GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed)
      InitBall();

Here it is, we can now play with two gamepad and our Xbox 360 port is complete!

Serious business: porting our game to Windows Phone 7

As we did for the Xbox 360 port, create copy for Windows Phone of your project. If you try to run the game in the emulator, you’ll most likely end up with a cornflower blue screen. This means our game has incompatible graphics properties. To fix this, we need to switch to Windows Phone 7 specific parameters, namely a screen resolution of 800x480, a refresh rate of 30 frames per second, forcing the landscape mode (playing our Pong in lortrait wouldn't be natural for a Pong game) and updating the referenced assemblies. But we don’t want to apply these changes to our other ports. Fortunately, there’s a way to write code that we only be considered by a specific port thanks to preprocessor statements (like we did for the Xbox 360 port). The changes will be applied in the Game1 constructor:

public Game1()
{
      graphics = new GraphicsDeviceManager(this);
      Content.RootDirectory = "Content";

#if XBOX360
      graphics.PreferredBackBufferWidth = 1280;
      graphics.PreferredBackBufferHeight = 720;
#endif

#if WINDOWS_PHONE
      // Frame rate is 30 fps by default for Windows Phone.
      TargetElapsedTime = TimeSpan.FromTicks(333333);

      // Pre-autoscale settings.
      graphics.PreferredBackBufferWidth = 800;
      graphics.PreferredBackBufferHeight = 480;
      graphics.IsFullScreen = true; // so the battery meter and other stuff are not displayed
      graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft; // forcing our game to support only one landscape mode so the screen doesn't rotate if the two players are shaking the device
#endif

      graphics.ApplyChanges();
}

Note that we also set the game to run in full screen mode. On Windows Phone 7, it means that the battery status will not show up over the game. If it wasn’t in full screen, the battery status would hide our score display.

We also need to update the references. Unfold the references of the Windows Phone 7 project and delete all references with a warning icon (Avatar, Net, Storage, Video, XACT) and add a new reference to the project: Microsoft.Xna.Framework.Input.Touch. Last thing to do, adding a conditional statement to include this new reference only to our WP7 port (after the other using statements):

#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif

Try to run the game and look at our super pong.

change_target.png

You may notice that the inputs don’t work. Some Windows Phone 7 devices come with a physical keyboard, but since some are not, XNA designer choose to not support keyboards as an input method so that games are experienced the same way on all Windows Phone 7 devices. The only method we have at our disposal if the multi-touch screen.

First, we need to design some sort of interaction scheme: to move the blue bar, we’ll consider the upper left and lower left corners of the screen as our up and down buttons; the opposite corners will be used to move the red bar; to initialize the game, we’ll consider a tap on the middle of the screen. The following code comes right after the inputs handling in the Update method:

#if WINDOWS_PHONE
// for each detected touch
TouchCollection touchCollection = TouchPanel.GetState();
foreach (TouchLocation touchLocation in touchCollection)
{
      // upper left corner touche detection
      if (touchLocation.Position.X < 50
            && touchLocation.Position.Y < 50)
            blueBar.Y -= 10; // move the blue bar up
      // lower left corner
      else if (touchLocation.Position.X < 750
            && touchLocation.Position.Y > 50)
            blueBar.Y += 10; // move the blue bar down

      // upper right corner touche detection
      if (touchLocation.Position.X > 430
            && touchLocation.Position.Y < 50)
            redBar.Y -= 10; // move the red bar up
      // lower right corner
      else if (touchLocation.Position.X > 750
            && touchLocation.Position.Y > 430)
            redBar.Y += 10; // move the red bar down

      // ball touch
      if (touchLocation.Position.X < 420
            && touchLocation.Position.X > 380
            && touchLocation.Position.Y < 260
            && touchLocation.Position.Y > 220)
      InitBall();
}
#endif

This is a quite rough technique. If you’d like to better use the touch screen, try to use the TryGetPreviousPosition method of a TouchLocation to follow a finger along the left or right corner of the screen and make the bar follow the gesture.

By the way, our Pong clone is now fully playable on Windows Phone 7! And that’s it.

Conclusion

Your Pong clone is now (almost) finished and working on three different platforms. There are still many aspects to fine tune, but this up to you now. You should have the background to do basic 2D games.

Hopefully, you noticed that I’m referring to the MSDN online documentation as much as possible. This was a secret message telling you that reading the documentation can save you much time and avoid you some useless search on the internet. If you still have problems, search on the creators club forum, or expose your problem on the forum.

I hope that you enjoyed following this tutorial and don’t hesitate to give me some feedback about it.

Download sample code: PongCode.zip (public domain)

About Absolutely Fine Tutorial Contest

Look out for the complete list of entries in our tutorial contest! Coming soon (it's being built step by step)!
Comments (18) Trackbacks (2)
  1. Just getting started with XNA. This is the one easiest to understand starting example. Great job dude.

  2. Well done, this is an excellent tutorial, complete and with solid explanations. And surprisingly thorough, too!

    I’m sure this will help lots of beginners find their way into XNA!

  3. Thank you for your comments :) .
    By re-reading it, I am a bit annoyed. There is some gramatical errors and missing words ^^ (I think I will send an updated version to the sgt/cpts when XNA 4.0 will reach the RTM).

    The Creators Club now has a lot of introductory tutorials, especially concerning WP7. So this tutorial feels a little bit ‘deprecated’ to that regard. My intention was only to share my course material with the world. I hope that beginners will still find it useful.

  4. Doh! I have just noticed something terribly wrong in my tutorial. The big mistake is about SpriteSortMode. The sentence mentioning it should definitely not exist (don’t laugh too loud if you spot it ;) . An awful copy/paste from an old article I wrote years ago.
    I will fix the problem by the time of the mentioned article update.

  5. Thanks for this interesting XNA tutorial! In my bookmarks you go ! :)

  6. The best 101 I have seen to date. Thank you very much for the great tutorial! Awesome work!

  7. I see wmv files are supported. How can you load them into a class? When I add a wmv file it does get imporrted and processed by the “wmvImporter” class but I have no idea how to load it.

  8. Ofcourse I meant loading it in a windowsPhoneGame project d

  9. Kevin, on WP7, you can’t play videos/wmv through the content pipeline. It is only supported on Windows and Xbox. However, you can play videos from the media library of the phone (using MediaPlayerLauncher) but I think that it will not fit your needs since you can not package a video with a WP7 game.

  10. This tutorial is amazing. I have read through about 5 – 10 books/tutorials and this one is the best. it is straight to the point without going to fast/not explaining properly.

    Do you have any more? if so post links please. thanks

  11. Hi I am having trying to load the texture1.bmp i followed all the steps but yet the exception says texture1 not found. I don’t know what is going.
    Can you help me with this please. I did exactly what the directions said.

  12. never mind just worked

  13. @TVS
    I am still having that same problem, what did you do to fix it? I am getting a file not found exception.

  14. Hi,

    I have an error code coming up from the redSrcRect, blueSrcRect, ballSrcRect. Can someone plz help me out?

    The error states “it does not exist in current state”

    Thanks

  15. Hi,

    I can’t find the Objects.png in the zip file you have. Instead there are 2 grass.png.

    Thanks

  16. This was an excellent tutorial and an awesome intro to XNA. Very inspring to know that it is pretty basic once you have your logic thought out. There’s no doubt alot more to XNA but this is an inspiring start. Thanks alot for taking the time to do this.
    *Your english isn’t bad at all.

  17. GOOD tutorial.. appreciated all teh help especially for windows phone.

  18. Great Tutorial. I have a question on moving a group of objects around.


Leave a comment


*