With the release of Windows 95, Microsoft also introduced the DirectX Application Programming Interface (API), which allowed Windows-based applications to integrate closely, in a standard way, with the graphics hardware available on the system. Prior to DirectX, most PC game development targeted MS-DOS, as Windows-based graphics were too slow for most gaming needs.
Although faster, working with the DirectX API could be challenging. The DirectX Software Development Kit (SDK) is targeted at C++, with no official support for other languages. The developer is also faced with large volumes of background work to get a DirectX project to the point where he can display images on the screen before ever considering the logic of the game itself.
In 2002, Microsoft released Managed DirectX as an interface to the API from its new .NET development environment. The .NET Framework consists of a set of code libraries to perform common programming tasks, and the Common Language Runtime (CLR) which allows code written in the various .NET languages (including Visual Basic .NET and C#) to be compiled into common runtime code. In order to support devices such as Windows Mobile phones, a subset of the .NET Framework was released, called the .NET Compact Framework. The .NET CF, as it is often abbreviated, removed non-essential components of the full Framework in the interest of saving storage space on handheld devices.
While Managed DirectX 2.0 was still in the beta phase, the project was cancelled, and Microsoft XNA was introduced in its place. XNA consists of the XNA Framework, a set of code libraries to perform common graphics, sound, and other game related tasks, and XNA Game Studio, an extension of the Visual Studio C# interface that includes a number of project templates to make use of the XNA Framework.
The XNA project templates include an integrated game loop, easy to use (and fast) methods to display graphics, full support for 3D models, and simple access to multiple types of input devices.
In addition to Windows games, XNA allows deployment to both the Xbox 360, the Zune handheld media player (with XNA 3.1) and Windows Phone 7 Series phones (with XNA 4.0). For the first time, a game console manufacturer has released a supported method for individual game developers to create (and sell!) content for their game console. Microsoft has even established the Xbox Indie Games system on Xbox Live to allow you to sell your creations to the world.
Tip
What does XNA stand for, anyway?
According to the developers, XNA is an acronym for "XNA's Not Acronymed".
In this introductory chapter you will:
Look at an overview of the games presented in this book
Download and install XNA Game Studio
Create a new Windows Game project
Modify the default Windows Game template to build your first XNA game
Many beginning developers make the mistake of attempting to tackle far too large a project early on. Modern blockbuster video games are the result of the efforts of hundreds of programmers, designers, graphic artists, sound effects technicians, producers, directors, actors, and many other vocations, often working for years to create the game.
That does not mean that the efforts of a solo developer or small team need to be dull, boring, and unplayable. This book is designed to help you develop a solid understanding of 2D game development with XNA Game Studio. By the time you have completed the projects in this book, you will have the knowledge necessary to create games that you can complete without an army of fellow game developers at your back.
In this chapter, you will build your first XNA mini game, chasing squares around the screen with your mouse cursor. In subsequent chapters the following four more detailed games are presented:
Flood Control : An explosion in one of the research laboratories has cracked the pressure dome protecting your underwater habitat. Work quickly to construct a series of pipes to pump water out of the habitat before it floods. Flood Control is a board-based puzzle game with simple game mechanics and slowly increasing difficulty.
Asteroid Belt Assault : After being separated from your attack fleet in Hyper Space, you find yourself lost in an asteroid field without communications or navigation systems. Work your way through the chaos of the asteroid belt while combating alien pilots intent upon your destruction. A vertically scrolling space shooter, Asteroid Belt Assault introduces scrolling backgrounds, along with player and computer controlled characters.
Robot Rampage : In the secret depths of a government defence facility, a rogue computer has taken control of robotic factories across the world, constructing an army of mechanical soldiers. Your mission—infiltrate these factories and shut down their network links to break the computer's control. A multi-axis shooter utilizing both of the analog control sticks on the Xbox 360 gamepad controller, Robot Rampage generates and manages dozens of on-screen sprites and introduces world map construction.
Gemstone Hunter : Explore the Australian wilderness, abandoned mines and ancient caves in a search for fabulous treasures. In Gemstone Hunter you will construct a classic platform-style game, including a Windows Forms-based level editor and a multi-map "world" to challenge the player.
The games are each presented over two chapters. In the first chapter, the basics are implemented to the point where the game is playable. In the second chapter, features and polish are added to the game.
Each game introduces new concepts and expands on topics covered in the previous games. At the end of each game chapter, you will find a list of exercises challenging you to use your newly gained knowledge to enhance previous games in the book.
We will focus on Windows as our platform for the games presented in this book. That said, the code presented in this book requires very little in the way of changes for other XNA platforms, generally only requiring implementation of platform-specific controls (gamepads, touch screen, and so on) and consideration of the differences in display sizes and orientation on non-Windows devices.
In order to develop games using XNA Game Studio, you will need a computer capable of running both Visual C# 2010 Express and the XNA Framework extensions. The general requirements are:
Component |
Minimum requirement |
Notes |
---|---|---|
Operating System |
Windows Vista SP2 or Windows 7 (All editions except Starter) |
As of XNA 4.0, Windows XP is no longer officially supported. |
Graphics card |
Shader Model 1.1 support DirectX 9.0 support |
Microsoft recommends Shader Model 2.0 support as it is required for many of the XNA Starter Kits and code samples. The projects in this book similarly require Shader Model 2.0 support. |
Windows Phone |
DirectX 10 or later, Compatible Video Card |
Development tools include a Windows Phone emulator to test applications without deployment to a physical device. |
Zune platform |
Zune Software 3.0 or higher, Visual C# 2008, XNA Game Studio 3.1 |
Only required if you plan to deploy games to a Zune handheld device. Zune development is supported under XNA 3.1. |
Xbox Live |
Xbox Live Silver membership, XNA Creator's Club Premium membership |
Xbox Live Silver is free. The XNA Creator's Club Premium membership costs $49 for 4 months or $99 for 1 year. |
Tip
HiDef vs. Reach
As of version 4.0, XNA now supports two different rendering profiles. The HiDef profile is available on the Xbox 360 and Windows PCs with DirectX 10 or better video cards, and uses Shader Model 3.0. The Reach profile is available on all XNA platforms, and uses Shader Model 2.0. If you have a DirectX 9 video card, or wish to distribute your games to computers with DirectX 9 support, you will need to right-click on your project in Solution Explorer and select Properties. On the XNA Game Studio tab, select the Reach profile.
To get started developing games in XNA, you will need to download and install the software. You will need both Visual C# and XNA Game Studio. With the release of XNA 4.0, the install packages have been consolidated, and both required components are included in the Windows Phone Developer Tools package.
Visit http://www.microsoft.com/express/Phone/ and download the Windows Phone Developer Tools package. Run the setup wizard and allow the installation package to complete.
Open Visual Studio Express. Click on the Help menu and select Register Product. Click on the Register Now link to go to the Visual Studio Express registration page. After you have completed the registration process, return to Visual Studio Express and enter the registration number into the registration dialog box.
Close Visual Studio Express.
Download the Font Pack from http://go.microsoft.com/fwlink/?LinkId=104778.
Extract the ZIP file contents to a temporary folder (leave this folder open).
From the Start Menu, select Control Panel. Under Classic View, choose Fonts.
Drag the fonts from the temporary folder to the
Fonts
folder.Close both Explorer windows.
Launch Visual Studio Express, and the Integrated Development Environment (IDE) will be displayed as seen in the following screenshot:
Tip
Other versions of Visual Studio and XNA
Different versions of Visual Studio and XNA can be installed on the same PC without interfering with each other. If you wish to target the Zune platform, you will need to install Visual C# 2008 Express and XNA 3.1. Additionally, Visual Studio Express and Visual Studio Professional can coexist on the same PC, and XNA will integrate with both of them if it is installed after Visual Studio.
You have now successfully installed the Windows Phone Developers Tools, including XNA Game Studio 4.0 and the Redistributable Font Pack provided by Microsoft for XNA developers.
Tip
The redistributable fonts package
To use its integrated text drawing methods, XNA games need to convert normal Windows fonts into an internal format called a SpriteFont. These SpriteFonts get distributed with your game, which means you will not be able to use most of the fonts on your computer due to licensing restrictions. For this reason, Microsoft has provided a selection of fonts that XNA developers can freely distribute without purchasing an individual license to do so.
XNA attempts to simplify many of the basic elements of game development by handling things like the game update loop and simplifying the display of graphical objects. To illustrate just how much of the background work is integrated into the XNA project templates, let's jump in straight away and create your first game within a few minutes of finishing the installation.
In SquareChase, we will generate randomly positioned squares of different colors while the user attempts to catch them with their mouse pointer before they disappear. While building the project, we will discuss each of the major code sections pre-defined by the XNA templates.
Each of the XNA project templates is a series of files and settings that get copied to your new project folder. Included in this set of files is the Game1.cs
file, which is the heart of your XNA game.
Tip
Backup your projects
When you create your project, the Location field specifies where it will be saved. By default, Visual Studio creates a folder in your user documents area called Visual Studio 2010
to store both programs and configuration information. Under this folder is a Projects
folder that contains subfolders for each new project you create. Make backups of your projects on a regular basis. You do not want to lose your hard work to a disk failure!
The most basic XNA game will have all of its code contained in the file called Game1.cs
. This file is generated when you create a new project, and contains override declarations for the methods used to manage your game. In addition to the Game1 class' declarations area, there are five primary methods you will customize for any XNA project.
Right below the class declaration for Game1 is the class level declarations area. By default, this area contains two variables:
GraphicsDeviceManager graphics; SpriteBatch spriteBatch;
The graphics
object provides access to, not surprisingly, the system's video card. It can be used to alter the video mode, the size of the current viewport (the area that all drawing work will be clipped to if specified), and retrieve information about Shader Models the video card supports.
XNA provides the SpriteBatch class to allow you to (very quickly) draw 2D images (called "sprites") to the screen.
The declarations area is the spot for any variables that need to be maintained outside of any of the individual methods listed below. In practice, any data you need to keep track of throughout your game will be referenced, in some way, in your declarations section.
These are all the variables you will need for the SquareChase mini game. Here is a quick breakdown:
rand
: This instance of the Random class is used to generate random numbers via the Next()
method. You will use this to generate random coordinates for the squares that will be drawn to the screen.
squareTexture
: The Texture2D
class holds a two dimensional image. We will define a small texture in memory to use when drawing the square.
currentSquare
: The XNA Framework defines a structure called Rectangle
that can be used to represent an area of the display by storing the x and y position of the upper left corner along with a width and height. SquareChase will generate random squares and store the location in this variable.
playerScore : Players will score one point each time they successfully "catch" a square by clicking on it with their mouse. Their score accumulates in this integer variable.
timeRemaining: When a new square is generated, this float will be set to a value representing how many seconds it will remain active. When the counter reaches zero, the square will be removed and a new square generated.
TimePerSquare : This constant is used to set the length of time that a square will be displayed before it "runs away" from the player.
colors: This array of Color objects will be used when a square is drawn to cycle through the three colors in the array. The Color structure identifies a color by four components: Red, Green, Blue, and Alpha. Each of these components can be specified as a byte from 0 to 255 representing the intensity of that component in the color. The Alpha component determines the transparency of the color, with a value of 0 indicating that the color is fully transparent and 255 indicating a fully opaque color. Alternatively, each component of a color can be specified as a float between 0.0f (fully transparent) and 1.0f (fully opaque).
The XNA templates define an instance of the Microsoft.Xna.Framework.Game class with the default name "Game1" as the primary component of your new game. Slightly more goes on behind the scenes, as we will see when we add an XNA game to a Windows Form in Chapter 8, but for now, we can consider the Game1 constructor as the first thing that happens when our XNA game is executed. The class constructor is identified as public Game1()
, and by default, it contains only two lines:
graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content";
For most of the games in this book, we will not need to make extensive modifications to the Game1 constructor, as its only job is to establish a link to the GraphicsDeviceManager
object and set the default directory for the Content
object which is used to load images, sound, and other game content.
After the constructor has finished and your XNA game begins to run, the Initialize()
method is called. This method only runs once, and the default code created with a new project template simply calls the base version of the method. The Initialize()
method is the ideal place to set up things like the screen resolution, toggle full screen mode, and enable the mouse in a Windows project. Other game objects that do not rely on external content such as graphics and sound resources can also be initialized here.
By default, the mouse is not visible inside the XNA game window. Setting the IsMouseVisible
property of the running instance of the Game1 class enables the mouse cursor in Windows.
Tip
Input types on other platforms
The Xbox, Zune, and Windows Phone do not support a mouse, so what happens when the code to enable the mouse runs on these platforms? Nothing! It just gets ignored. It is also safe to ask other platforms about their non-existent keyboards and check the state of a gamepad on a Windows PC without one attached.
Part of the responsibility of the base Initialize()
method is to call LoadContent()
when the normal initialization has completed. The method is used to read in any graphical and audio resources your game will need. The default LoadContent()
method is also where the spriteBatch
object gets initialized. You will use the spriteBatch
instance to draw objects to the screen during execution of the Draw()
method.
Open Microsoft Paint or your favourite image editor and create a new 16 by 16 pixel image and fill it with white.
Save the image as SQUARE.BMP in a temporary location.
Back in Visual C# Express, right-click on SquareChaseContent (Content) in Solution Explorer (you may need to scroll down to see it) and select Add | Existing Item. Browse to the image you created and click on Ok.
Add the following code to the
LoadContent()
method after thespriteBatch
initialization:squareTexture = Content.Load<Texture2D>(@"SQUARE");
To load content, it must first exist. In steps 1 and 2, you created a bitmap image for the square texture. In step 3, you added the bitmap image as a piece of content to your project.
Tip
Powers of two
Very old graphics cards required that all texture images be sized to "powers of two" (2, 4, 8, 16, 32, 64, 128, 256, etc). This limitation is largely non-existent with modern video hardware, especially for 2D graphics. In fact, the sample code in the XNA Platform Starter Kit uses textures that do not conform to the "powers of two" limitation. In our case, the size of the image we created previously is not critical, as we will be scaling the output when we draw squares to the screen.
Finally, in step 4 you used the Content
instance of the ContentManager
class to load the image from the disk and into the memory when your game runs. The Content
object is established automatically by XNA for you when you create a new project. When we add content items, such as images and sound effects to our game project, the XNA Content Pipeline converts our content files into an intermediate format that we can read via the Content
object. These XNB files get deployed alongside the executable for our game to provide their content data at runtime.
Once LoadContent()
has finished doing its job, an XNA game enters an endless loop in which it attempts to call the Update()
method 60 times per second. This default update rate can be changed by setting the TargetElapsedTime
property of the Game1 object, but for our purposes, the default time step will be fine. If your Update()
logic starts to take too long to run, your game will begin skipping calls to the Draw()
method in favour of multiple calls to Update()
in an attempt to catch up with the current game time.
All of your game logic gets built into the Update()
method. It is here that you check for player input, move sprites, spawn enemies, track scores, and everything else except draw to the display. Update()
receives a single parameter called gameTime
, which can be used to determine how much time has elapsed since the previous call to Update()
or to determine if your game is skipping Draw()
calls by checking its IsRunningSlowly
property.
The default Update()
method contains code to exit the game if the player presses the "Back" button on the first gamepad controller.
Add the following to
Update()
right before the call tobase.Update(gameTime);
if (timeRemaining == 0.0f) { currentSquare = new Rectangle( rand.Next(0, this.Window.ClientBounds.Width - 25), rand.Next(0, this.Window.ClientBounds.Height - 25), 25, 25); timeRemaining = TimePerSquare; } MouseState mouse = Mouse.GetState(); if ((mouse.LeftButton == ButtonState.Pressed) && (currentSquare.Contains(mouse.X, mouse.Y))) { playerScore++; timeRemaining = 0.0f; } timeRemaining = MathHelper.Max(0, timeRemaining - (float)gameTime.ElapsedGameTime.TotalSeconds); this.Window.Title = "Score : " + playerScore.ToString();
The first thing the Update()
routine does is check to see if the current square has expired by checking to see if timeRemaining
has been reduced to zero. If it has, a new square is generated using the Next()
method of the rand object. In this form, Next()
takes two parameters: an (inclusive) minimum value and a (non-inclusive) maximum value. In this case, the minimum is set to 0
, while the maximum is set to the size of the this.Window.ClientBounds
property minus 25
pixels. This ensures that the square will always be fully within the game window.
Next, the current position and button state of the mouse is captured into the "mouse" variable via Mouse.GetState()
. Both the Keyboard
and the GamePad
classes also use a GetState()
method that captures all of the data about that input device when the method is executed.
If the mouse reports that the left button is pressed, the code checks with the currentSquare
object by calling its Contains()
method to determine if the mouse's coordinates fall within its area. If they do, then the player has "caught" the square and scores a point. The timeRemaining
counter is set to 0, indicating that the next time Update()
is called it should create a new square.
After dealing with the user input, the MathHelper.Max()
method is used to decrease timeRemaining
by an amount equal to the elapsed game time since the last call to Update()
. Max()
is used to ensure that the value does not go below zero.
Finally, the game window title bar is updated to display the player's score.
Tip
The Microsoft.Xna.Framework
namespace provides a class called MathHelper that contains lots of goodies to make your life easier when dealing with numeric data, including converting degrees to and from radians, clamping values between a certain range, and generating smooth arcs between a starting and ending value.
The final method in the default Game1.cs
file is responsible, not surprisingly, for drawing the current game state to the display. Draw()
is normally called once after each call to Update()
unless something is happening to slow down the execution of your game. In that case, Draw()
calls may be skipped in order to call Update()
more frequently. There will always be at least one call to Update()
between calls to Draw()
, however, as sequential Draw()
calls would provide no benefit—nothing in the game state will have changed.
The default Draw()
method simply clears the display window in the Cornflower Blue color.
Alter the
GraphicsDevice.Clear(Color.CornflowerBlue);
call and replaceColor.CornflowerBlue
withColor.Gray
to make the game a bit easier on the eyes.Add the following code after the call to clear the display:
spriteBatch.Begin(); spriteBatch.Draw( squareTexture, currentSquare, colors[playerScore % 3]); spriteBatch.End();
Any time you use a SpriteBatch
object to draw to the display, you need to wrap the calls inside a Begin()
and End()
pair
. Any number of calls to spriteBatch.Draw()
can be included in a single batch and it is common practice to simply start a Begin()
at the top of your Draw()
code, use it for all of your drawing, and then End()
it right before the Draw()
method exits. While not benefiting our SquareChase game, batching sprite drawing calls greatly speeds up the process of drawing a large number of images by submitting them to the rendering system all at once instead of processing each image individually.
The SpriteBatch.Draw()
method is used to draw a Texture2D
object to the screen. There are a number of different options for how to specify what will be drawn. In this case, the simplest call requires a Texture2D
object (squareTexture
), a destination Rectangle
(currentSquare
), and a tint color to apply to the sprite. The expression playerScore % 3
takes the player's score, divides it by 3
, and returns the remainder. The result will always be 0, 1, or 2. This fits perfectly as an index to the elements in the colors
array, allowing us to easily change the color of the square each time the player catches one.
Finally, the spriteBatch.End()
tells XNA that we have finished queuing up sprites to draw and it should actually push them all out to the graphics card.
You just finished your first XNA game, that's what!
Granted it is not exactly the next blockbuster, but at only 33 lines of code, it implements a simple game mechanic, user input, score tracking and display, and clock-based timing. Not bad for a few minutes work.
As simple as it is, here are a couple of enhancements you could make to SquareChase:
Vary the size of the square, making it smaller every few times the player catches one, until you reach a size of 10 pixels.
Start off with a higher setting for
TimePerSquare
and decrease it a little each time the player catches a square. (Hint: You'll need to remove theconst
declaration in front ofTimePerSquare
if you wish to change it at runtime).
You now have a development environment set up for working on your XNA game projects, including Visual Studio Express and XNA Game Studio 4.0.
We also saw how the XNA game loop initializes and executes, and constructs an elementary game by expanding on the default methods provided by the Windows Game template.
It is time to dive head first into game creation with XNA. In the next chapter, we will begin building the puzzle game Flood Control in which the player is challenged to pump water out of their flooding underwater research station before the entire place really is underwater!