Got Game?

Unleash Your Imagination With XNA Game Studio Express

Charles Cox and Michael Klucher

This article discusses:

  • Game development basics
  • The Spacewar Starter Kit
  • Adding features to Spacewar
  • Customizing game graphics
This article uses the following technologies:
XNA Game Studio Express, Xbox 360, C#

Code download available at: XNA2007_05.exe(155 KB)

Contents

Game Development Basics
The Spacewar Starter Kit
Understanding the Game Loop
Shields Up!
Rendering Details
Energize Shields!
Bonus Credit: Change Space!
Blast Off!

While playing a video game, have you ever thought to yourself, "I could put something together like this"? XNA™ Game Studio Express is a new game development solution for both Microsoft® Windows® and Xbox 360™ targeted primarily at students, hobbyists, and independent game developers—people like you!

Based on Visual C#® 2005 Express Edition, XNA Game Studio Express is built on the XNA Framework, a managed-code class library designed for game development. The framework streamlines the process of writing code for two very different platforms—Xbox 360 and Windows XP SP2. In fact, much of the code you write will work on either system with no changes at all.

You can immediately start writing games for Windows simply by downloading XNA Game Studio Express from MSDN® at msdn2.microsoft.com/xna. To develop games on your Xbox 360 console, you’ll need to obtain a membership in the XNA Creators Club (for more information, see msdn2.microsoft.com/bb219592.aspx). As a member, you’ll have access to game assets as well as exclusive XNA Creators Club versions of starter kits.

Before we get started walking you through XNA Game Studio Express, you’ll need to download and install it. If you’d like help getting set up on your Xbox 360 console, you can download a video walkthrough from the MSDN Download Center.

Game Development Basics

Game development presents several unique challenges. Games must strive to be compelling for users—not only from a design perspective, but technically as well—with eye-catching graphics, all while maintaining reasonable frame rates. Intuitive ways for the user to interact with the game via input as well as quality audio output are additional considerations.

Before the introduction of XNA Game Studio Express, game developers used technologies such as DirectX® and OpenGL. These APIs allow basic access to the hardware, but are not intuitive. As a result, developers often assembled core components such as graphics, audio, and input into a framework called an engine, then layered specific game logic over the top of the framework. This abstraction is often costly and time-consuming to create and use.

Furthermore, managing memory and logic in C++, the language of choice for game development, is technically demanding, as is managing the many pieces of art content present in a game. No wonder game development has been particularly daunting for amateur developers. Similarly forbidding are the complex technical and business requirements of developing for next-generation video game consoles such as the Xbox 360.

Enter XNA Game Studio Express, built atop Visual C# 2005 Express Edition. XNA Game Studio Express is designed for hobbyists, students, and other game development enthusiasts, enabling them to create their own games on both Windows and Xbox 360 using C# and the Visual C# 2005 Express Edition integrated development environment.

XNA Game Studio Express includes the XNA Framework, a cross-platform game development API for both Windows and Xbox 360. The XNA Framework drastically simplifies not only graphics, audio, input, and storage, but also the fundamental timing and drawing loops used in all games.

Also included in XNA Game Studio Express is the XNA Framework Content Pipeline, which is integrated into the build environment of Visual C# 2005 Express Edition. With the Content Pipeline, game developers can now add their art content—such as 3D models, graphical textures, shaders, and audio—into their solution in the IDE, and the content will be built into runtime objects that can be accessed in code. This reduces the overhead associated with art content.

Previous game development solutions typically started from scratch: manually writing timing and render loops that managed each tick of time and ensured the game action was handled properly. XNA Game Studio Express constructs the timing and render loops automatically. You are provided with a Game class that handles its own timing and rendering, exposing an Update method where you can add objects to update each frame and a Draw method where you can add objects to render to the screen.

Furthermore, the provided game framework, called the Application Model, automatically sets up the appropriate graphics device without complicated device enumeration code. With this code in place, an XNA Game Studio Express project can be compiled and run, with a fully functional timing and rendering loop running from the moment it is created.

The Spacewar Starter Kit

In addition to XNA Framework code templates, XNA Game Studio Express comes with the Spacewar Starter Kit—a fully functional game, complete with source code and art assets you can modify. The Spacewar Starter Kit is a top-down space shooter game for two players, based on the original implementation of Spacewar, developed for the DEC PDP-1 in 1962 by Stephen Russell, Peter Samson, and Dan Edwards. The starter kit features two versions of the game: a Retro version that retains the original’s gameplay and graphics, and an Evolved version that features improved 3D graphics and sound.

We’ll open up the starter kit, try out the Evolved version, explore how the game is constructed, and then add a new feature: a shield that each player can use to temporarily block enemy shots.

First, launch XNA Game Studio Express, click the File menu, and then click New Project. When the New Project window opens, click the Spacewar Windows Starter Kit, and then click OK (see Figure 1). If you’ve joined the Creators Club and have set up your Xbox 360 to receive from XNA Game Studio Express, click the Spacewar Xbox 360 Starter Kit instead, and then click OK.

Figure 1 Opening the Spacewar Starter Kit

Figure 1** Opening the Spacewar Starter Kit **(Click the image for a larger view)

The Spacewar Starter Kit will take a moment to load. When it has fully loaded, you’ll see the code files displayed in the Solution Explorer pane. You’ll also see instructions on how to play the game. For the best experience, plug in two Xbox 360 controllers. You can also use the keyboard; the controls are shown in the instructions page when the Spacewar Starter Kit is loaded.

Compile and run the Spacewar application by pressing F5. Once the game has started, select Evolved mode and try out the gameplay for a few moments.

Understanding the Game Loop

One of the core features provided by XNA is the game loop. This loop continually updates the state of the game (based on user input, in-game conditions, and any other applicable condition) and renders it (which involves drawing to the screen, playing appropriate audio, rumbling a controller, and providing any other form of output to the user). Understanding how the game loop in Spacewar works will better help you to understand some of the modifications we’ll be making to the Starter Kit in this article.

Game execution begins as in any other .NET application, with a Main method. The Main method simply constructs an instance of the SpacewarGame class and calls its base class’s Run method:

static void Main(string[] args)
{
    using (SpacewarGame game = new SpacewarGame())
    {
        game.Run();
    }
}

Note that SpacewarGame is derived from the Game class in the Microsoft.Xna.Framework namespace; it exposes key methods, properties, and events for the game lifecycle. You then override methods on Game to provide specific functionality for your game. For example, SpacewarGame overrides the BeginRun method (which is called from Run) to play the game’s title music, change its internal state machine to the logo screen stage, and create the initial camera from which a player views everything.

SpacewarGame’s ChangeState method, as called by the BeginRun method, is responsible for creating many of the objects used in the game. When the BeginRun method calls ChangeState with GameState.LogoSplash as an argument, it reacts by creating a TitleScreen. TitleScreen derives from FullScreenSplash, which in turns derives from the abstract Screen class defined in the Spacewar project. The Screen class represents a unit of rendering for the game, such as a splash screen, selection screen, or an actual game level. It exposes several virtual methods, which its derived types override to provide screen-specific functionality. TitleScreen overrides the Screen.Update method to check whether the user has pressed A, B, or X to indicate where to go next in the game (Evolved, Retro, or Information). TitleScreen is just one of several Screen-derived types used in Spacewar; others include SelectionScreen (for ship selection), EvolvedScreen (for playing the "evolved" version of the game), VictoryScreen (for when a player has won), and so forth.

Each Screen encapsulates the state necessary for that screen’s functionality along with update and render behavior specific to that screen. Both EvolvedScreen and RetroScreen, which represent the main game play for their respective game types, derive from SpacewarScreen:

public class SpacewarScreen : Screen
{
    protected Projectiles bullets;
    protected Ship ship1;
    protected Ship ship2;
    protected SceneItem sun;
    protected Particles particles;
    protected SceneItem backdrop; 

    protected bool paused = true;
    protected int player1Score;
    protected int player2Score;

    ...
}

SpacewarScreen contains items to be rendered on the screen, including SceneItems for both ships (implemented as Ship, which derives from SpacewarSceneItem, itself derived from the base SceneItem class), all of the miscellaneous particles and projectiles to be rendered, the backdrop for the game, the sun, and so forth. It also contains game state such as player scores and whether or not the game is paused.

When a Game is created (in our case, a SpacewarGame), it ensures that a host is created to house the game and to provide application-level services to the game, namely notifications for events such as activation, deactivation, exiting, and idling. The latter is most important. The Game.Tick method is called by this idle handler, and the Tick method in turn calls the Game.Update and Game.DrawFrame methods.The Game.Update method is overridden by SpacewarGame.Update, which calls Update on whatever has been set as the current scene (from the ChangeState method). Game.DrawFrame calls Game.Draw, which is overridden by SpacewarGame to call Render on whatever is the current scene.

Shields Up!

It’s easy to augment Spacewar with new features. Let’s add an energy shield. This shield will protect a ship from damage caused by other items in the scene. We’ll need to edit two code files: Ship.cs and SpacewarScreen.cs. You can download it or walk through it at msdn.microsoft.com/msdnmag/code07.aspx.

The file Ship.cs is where we’ll do most of our coding. In Spacewar, the Ship.cs file defines a Ship object. The Ship object contains methods and data that define a spaceship object controlled by a player. Look in Solution Explorer for Ship.cs and double-click it to open the file.

First, we need to define a few variables to control the logic of the shield. The shield will stay up for five seconds, and can be used only once per round. Go to line 40 in Ship.cs, and add the following code (you’ll be adding this among other private members that store information such as whether the ship is currently invulnerable, inHyperspace, or inRecovery):

private bool inShield;
private bool shieldUsed;
private double exitShieldTime;
private const double shieldTime = 5.0;

This establishes a few variables that will track whether the shield is active, whether the shield has already been used, and how much time is left on the shield’s timer.

Let’s use a property so we can easily access whether the ship’s shield is active. This will be accessed by our modifications in SpacewarScreen.cs shortly. Go to line 188 in Ship.cs (what will now be line 188 after adding the previous snippet), and add the following code:

public bool Shield { get { return inShield; } }

So far, we’ve put a set of variables and an accessor in place. Let’s tie these variables to some game action. We’ll be modifying the Update method inside Ship.cs. As described, the Ship’s Update method is called by the game loop’s Update call, which the XNA Framework application model calls automatically once per frame (or more often, depending on how fast the game is running). Update is where you will move your game objects around, have them interact with each other, and take user input, which is what we’ll be doing in this Update method.

Next, we need to modify the Ship’s Update method to respond to input from the user to enable our new shield. Go to line 248 in Ship.cs, which is inside the Update method just before the check for "if (inHyperspace)", and add the following code:

if (XInputHelper.GamePads[player].XPressed && !shieldUsed)
{
    inShield = true;
    shieldUsed = true;
    exitShieldTime = shieldTime;
    Sound.PlayCue( Sounds.PhaseActivate );
}

One line of input, shown in the conditional statement, is all it takes to check for a keystroke or controller button press. XInputHelper, provided in the Spacewar Starter Kit, provides a wrapper around both the Xbox 360 controller and keyboard inputs to simplify input. Using XInputHelper, this if statement checks whether the X button is pressed for the player that the ship is associated with. If you are using a keyboard, the equivalent key is F for player 1, and Page Up for player 2.

You can easily change these keyboard mappings by modifying the settings.xml file in the Spacewar project. Settings.xml is unique to Spacewar (it’s loaded in SpacewarGame’s override of the Game.Initialize method), but XML data files are a good choice in general for game data. Since XNA Game Studio Express is built on the .NET Framework, the Xml namespace is exposed to games.

The code inside the conditional raises the shield, marks the shield as having been used this round, and starts the shield timer. It also uses the XNA Framework audio system to play a cue, which in turn plays a sound for the user. Spacewar abstracts the audio layer of the XNA Framework using enumeration values such as Sounds.PhaseActivate to identify the audio cues to play. Generally, you call cues using a string value. If the number of cues in your game is limited, such as in Spacewar, an enumeration is a good solution, especially if cue names are changing during development.

You add sounds to a game by using the Microsoft Cross-Platform Audio Creation Tool (XACT), provided with XNA Game Studio Express. All of the audio files and their associated cues are kept in an XACT project, which is displayed in a graphical interface that allows you to drag and drop sounds into the project. When you build your game, the XNA Framework Content Pipeline loads the XACT project and builds the necessary audio files. If you want to add a sound to Spacewar, you’ll have to take the extra steps of adding the cue name to the array of cue names and providing an enumeration value to the Sounds index into the cue name array. Then you can call your sound by using the Sounds enumeration.

In the Update call, you’ll want to add the necessary logic to run the shield timer if the shield is active. Go to line 348 (at the end of the "if (!Paused)" block), and add the following code:

if (inShield)
{
    exitShieldTime -= elapsedTime.TotalSeconds;
    if (exitShieldTime <= 0)
    {
        inShield = false;
        Sound.PlayCue( Sounds.PhaseExpire );
    }
}

This code simply decrements the time left on the shield by the elapsed time. Elapsed time is a measure of how much time has passed between calls to Update. This amount, applied to game logic such as motion or timers, simulates the passing of time, and is passed into Update by the XNA Framework. Once the shield time runs out, the shield is deactivated and an appropriate sound is played.

Rendering Details

While we’ve handled input and sound handily, the question remains: what will the shield look like? We’ll need to put some code into the Render method. So using some math and drawing functionality provided in the XNA Framework, we’ll draw a sprite—a 2D texture with transparency—on top of the ship’s 3D position.

In Solution Explorer, find the Content folder in the solution tree, open it, and then open the Textures folder beneath it. You’ll see a variety of .tga files. We’ll be using one of these as our shield texture. A .tga file represents a 2D graphical image, a sprite.

Go to line 433 in Ship.cs and make a new line immediately following it—after the end of the block for "if (showThrust && evolved)". In that new line, add the code shown in Figure 2.

Figure 2 Rendering a Sprite

if (inShield && evolved)
{
    Texture2D shieldtexture = 
        SpacewarGame.ContentManager.Load<Texture2D>( 
        SpacewarGame.Settings.MediaPath + @”textures\circle” );

    batch.Begin( SpriteBlendMode.AlphaBlend );

    // Move into screen space.
    Vector3 location = device.Viewport.Project(
        position, SpacewarGame.Camera.Projection,
        SpacewarGame.Camera.View, Matrix.Identity );

    batch.Draw(
        shieldtexture,
        new Vector2( location.X - shieldtexture.Width / 2.0f, 
                     location.Y - shieldtexture.Height / 2.0f ),
        new Rectangle( 0, 0, shieldtexture.Width, shieldtexture.Height ),
        new Color( new Vector4( 0.0f, 1.0f, 0.0f, 0.5f ) ), 0.0f,
        new Vector2( shieldtexture.Width / 2.0f,
                     shieldtexture.Height / 2.0f ),
        (3.0f * (float)(Math.Abs( Math.Cos( exitShieldTime ) ))) + 2.0f,
        SpriteEffects.None, 0.0f);

    batch.End();
}

Let’s walk through what’s happening in this code. First, using the XNA Framework and the XNA Framework Content Pipeline, the ContentManager class loads the texture called circle from the Content\Textures folder. The ContentManager class is an XNA Framework class that loads content from the Content Pipeline at run time. It is part of the prepopulated Application Model code in each new XNA Game Studio Express project. The name "circle" is associated with the circle.tga file in the Content\Textures folder. The XNA Framework Content Pipeline assigns a friendly name to each content file, which is used to load the content at run time.

Next, the texture is loaded into a Texture2D class provided by the XNA Framework. The Texture2D object will be drawn on the screen using a SpriteBatch (named batch in this code block), which represents a batch of textures that will be drawn on the screen at once.

We call batch.Begin to signal that sprite drawing is about to begin, and note that we want to use alpha blending. This will allow the circle texture to be transparent around its edges and partially transparent throughout, because we want to still be able to see the ship below the shield.

Before we draw, we must know where on the screen to draw. Spacewar Evolved is a 3D game, so the objects move around in three dimensions and are projected onto a 2D plane only when they are drawn. We must find the 2D position of the 3D ship before we know where we should draw the shield.

The XNA Framework allows us to get this information from the graphics device. We call device.Viewport.Project and pass in the 3D coordinates of the ship, as well as view and projection matrices. The view matrix defines the linear algebra transformation applied to each 3D object to place the object correctly relative to the player’s viewpoint. The projection matrix transforms the 3D coordinates for each object to 2D screen coordinates that allow the objects to be drawn to the screen. These matrices are often precomputed and stored. In Spacewar, they are kept in a Camera class that also defines the player’s viewpoint. Calling device.View­port.Project with these values simulates transforming the ship’s location to 2D coordinates without actually drawing it at those coordinates. This call returns a vector that holds the 2D draw coordinates we will use to draw our shield.

The next step is to call batch.Draw, passing in the following:

  • The Texture2D object.
  • Our projected coordinates minus half the width and height of the sprite; this centers the sprite right on the ship.
  • The source size of the texture.
  • A color to multiply with the sprite’s existing colors. (We construct it as a Vector4, with three components representing red, green, and blue, and a fourth for alpha, or opacity. These particular values define a partially transparent green; the transparency allows us to see the ship underneath the shield, rather than obscuring it.)
  • A scaling factor. (We’re being tricky here by taking the cosine of the remaining shield time, which makes the size of the shield fluctuate over time, just for graphical kicks.)
  • A sprite effect flag that we set to None, but which could be used to flip the sprite horizontally or vertically.
  • A screen depth. A value of zero makes the sprite draw above everything else, so it’s guaranteed to render on top of the ship instead of underneath it.

Energize Shields!

At this point a player can raise a shield, visualize it, and have the shield disappear after an appropriate period of time. Unfortunately, the shield doesn’t yet provide any protection. To get that functionality, let’s open SpacewarScreen.cs and add two simple blocks of code, one for each player. Go to line 94 in SpacewarScreen.cs, and add the following code to the beginning of HitPlayer2:

if (ship2.Shield)
{
    // Make a bouncy sound rather than an explosion.
    Sound.PlayCue( Sounds.WeaponPickup );
    return;
}

And add the following at line 122, to the beginning of HitPlayer1:

if (ship1.Shield)
{
    // Make a bouncy sound rather than an 
    // explosion.
    Sound.PlayCue( Sounds.WeaponPickup );
    return;
}

As described earlier, SpacewarScreen.cs represents the update logic for the Spacewar game loop. Many games, including Spacewar, utilize separate game logic loops for the various states of a game, such as the main menu, weapon selection, and the actual game itself. As the players progress through the games, control is handed off from state to state. In Spacewar, these states are called screens and are represented by the Screen class. SpacewarScreen is derived from Screen and handles the actual gameplay state, in which the two players control their spaceships and do battle.

In the SpacewarScreen class, the Update loop manages the game objects, such as the spaceships, weapons, and asteroids that float around the screen. When these objects collide with the spaceships, the logic calls HitPlayer1 or HitPlayer2, which calculates the damage to be dealt to the spaceships.

This code modifies the game logic in HitPlayer1 and HitPlayer2 so that when a ship is dealt damage, the shield is checked before any damage is resolved. If the shield is up, a deflection sound is played, and the damage function exits before taking any more action.

That’s it. Compile and run with F5, and try it out! From the Main Menu, select the Evolved game mode. Once you start the game, press the X button or the keyboard equivalent (F for player 1, Page Up for player 2) to raise the shield, as shown in Figure 3. Both players can use the shield only once per round, but the shield is very strong: it will absorb bullets of every type, asteroid hits will cause no damage—even the sun can’t hurt you!

Figure 3 Shields Energized!

Figure 3** Shields Energized! **(Click the image for a larger view)

Take a spin around the block with the shield, but be sure not to get yourself too close to the sun when the shield runs out—it only lasts five seconds!

Some ideas about where to go from here: using some vector math, you could modify bullets to bounce off the shield rather than being absorbed. It could mean a painful surprise for an opponent when his own shots return to him! Or modify the damage system to make a shielded ship deal double damage when it strikes an opponent! Ramming speed!

The fun of writing games is in experimentation. With XNA Game Studio Express, it’s easy to get down to the good stuff and truly make a game your own creation.

Bonus Credit: Change Space!

XNA Game Studio Express doesn’t only help people worried about how to best code new features into their games. It also incorporates a feature set, mentioned earlier, called the XNA Framework Content Pipeline. The XNA Framework Content Pipeline helps you create 2D, 3D, and even audio content for your XNA Framework–based game.

Part of what the XNA Framework Content Pipeline does is to integrate the content building and compiling process into Visual C# Express. The added benefit of this is that you can launch certain types of content directly from Visual C# Express: you edit the content and then recompile to see the changes in your game. By compiling your content at the same time you compile your code, you can unearth content errors before you run your game.

The XNA Framework Content Pipeline supports various 2D graphics file formats, including .bmp, .jpg, .png, .tga, and .dxt. Two 3D file formats are also supported: .x and .fbx. Both of these 3D formats have file format exporters for a large number of both free and professional 3D graphics creation tools. The XNA Framework Content Pipeline is also designed to be extremely extensible, which means you may be able to find support for more of your favorite file formats by searching online.

Let’s explore the XNA Framework Content Pipeline using Spacewar. Back in XNA Game Studio Express, go to the Solution Explorer. Open the Content folder, then the Textures folder. Here, you’ll see many of the textures that Spacewar uses. Click the file B1_nebula01.tga; this is one of the backgrounds used when playing the game. The property grid at the bottom of the Solution Explorer should then show some relevant properties (see Figure 4). If you cannot see the property grid, right-click the B1_nebula01.tga file and click Properties.

Figure 4 Textures Properties

Figure 4** Textures Properties **

In the property grid, you’ll see several unique properties dedicated to the XNA Framework Content Pipeline. The Content Importer property is being used here. An importer is responsible for taking the source data—in this case a .tga file—and transforming it to the XNA Framework content DOM. The content DOM holds data that has been transformed and normalized by the importers, so that processors can work independently of file formats. The next property is the Content Processor property. The processor is responsible for converting data from the content DOM to an object suitable for use at run time—in this case, a sprite. The Asset Name property is used for loading the asset at run time; you can turn off processing of the asset by setting the XNA Framework Content property to false.

Let’s try editing the B1_nebula01.tga graphic file to see how it changes in Spacewar. You’ll need an image editor capable of working with a .tga file. If you don’t have one, you can download Paint.NET from getpaint.net.

Double-click the file to launch the associated editor for that file. Then go ahead and edit the file a bit (see Figure 5). One easy way in Paint.NET would be to change the hue and saturation by clicking the Adjustments menu, then clicking Hue/Saturation. Move the sliders to change the hue, saturation, and lightness of the graphic, then click OK to commit the changes. After you’ve changed the file, save it, and return to XNA Game Studio Express. Compile and run the Spacewar project by pressing F5. XNA Game Studio Express will detect that the file has been changed and will rebuild and redeploy the new asset (even to the Xbox 360 console). When you start an evolved game, you should see your modified background!

Figure 5 Editing a Texture in Paint.NET

Figure 5** Editing a Texture in Paint.NET **(Click the image for a larger view)

Blast Off!

These simple examples demonstrate only a fraction of the power of XNA Game Studio Express. The cross-platform combination of the XNA Framework and XNA Framework Content pipeline means that you’ll be able to focus on what really makes game development fun: great content and inventive gameplay.

Furthermore, we recommend that you join the XNA Creators Club so that you’ll be able to take your game development skills to the next level. Becoming a member of the club not only opens the door to developing your own games on the Xbox 360 platform, but also gives you access to exclusive content, samples, starter kits, and a lot more resources that will help you take your game from your imagination into reality.

Charles Cox is a Developer Educator in the Microsoft Game Platform Documentation Group. Previously a Technical Writer for DirectX and Microsoft Game Studios, Charles now develops and delivers XNA Game Studio Express tutorials and educational workshops.

Michael Klucher is a Program Manager on the XNA Game Studio team at Microsoft, working on the XNA Framework Content Pipeline and XNA Game Launcher for the Xbox 360 console. Contact Michael via his blog at klucher.com.