Sep 19 2011

Introduction to XNA 4.0

Category: Novice | XNABrian Legg @ 09:49

If you're already familiar with .NET (either VB or C#) and you want to start creating video games for the XBox 360, Windows Phone, or Windows PC then XNA is exactly what you're looking for. This post is not intended to be the all-encompassing learn everything about XNA tutorial. I'm creating this as a starting point for those who have never used XNA before and want to jump in quickly and also as a reference point for those (like me) who frequently forget some of the basic things you set once and forget about. I like to keep explanations short and prefer to demonstrate things using code, so if you want to know more about how XNA works internally you'd be better off getting one of the many books out there on the topic. On the other hand, if you just want to start coding a game right now to see how XNA works you've come to the right place.

What this post will cover

  • Full screen game
  • 2D graphics
  • Keyboard, Mouse, and XBox 360 controller support
  • Sound effects
  • Collision Detection
  • Sprites
  • Keeping score and using Fonts

What this post will NOT cover

  • The installation of XNA Game Studio 4.0
  • Proper game architecture
  • 3D game design
  • Animation
  • Models
  • Asset Creation

The only prerequisite for beginning this tutorial is the XNA Game Studio 4.0 which is available from Microsoft's website. I intend to keep this game extremely simple and it will NOT be anything worth keeping. However, I hope you learn a lot while writing it. Here's a brief description of what we'll be making - A mouse and keyboard (or XBox 360 controller) controlled dinosaur which can roar and eat meat. Each time he eats a piece of meat it will be replaced somewhere else on the screen and a score will be kept in the top right corner. Without further ado, let's get started.

Creating a new XNA Windows Game

Since you've already installed XNA Game Studio 4.0 you should have the XNA templates installed for Visual Studio. To test this let's create a new project (File -> New Project...) and you should see a new sub-heading under your language of choice titled "XNA Game Studio 4.0". We'll be creating a new Windows Game under this heading. The window should look similar to the following:

New Project
New Project

After selecting OK you should see your new solution has 2 projects - SimpleGame and SimpleGameContent. The first project (SimpleGame) is our actaul game and will contain all of the code to make it work. The second project (SimpleGameContent) will contain the resources and assets which will be used in our game. XNA has a built in Content Pipeline which automatically compiles any resources in your Content project and turns them into XNB files which can easily be read into the game. We'll get into that a bit more later. For now let's take a brief look at the code which has been provided to us as part of the Windows Game template.

What is all this code?

The Windows Game template has dropped a couple variables into our code and a number of methods. But what are these for and where do we begin? First let's take a look at the 2 variables located at the top of our class:

To keep things easy we'll simply think of graphics as an object which represents our monitor in this case and spriteBatch is simply an object which makes 2D screen rendering simple. When drawing 2D images and sprites to the screen it's more efficient to draw them all at once instead of one at a time, this is where spriteBatch comes in. Next you'll notice that our template has provided a number of methods:

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

protected override void Initialize()
{
    base.Initialize();
}

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
}

protected override void UnloadContent()
{
}

protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();

    base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    base.Draw(gameTime);
}

Game1() is simply our constructor and it's responsibility is to set up our game and our graphics object. You will very rarely add code here. Initialize() is where we will initialize private members, services, and other non-graphic components of our game. LoadContent() will be used heavily. This method is responsible for loading graphics, sounds, and any other resources loaded through the content pipeline. UnloadContent() is rarely used and in our case not at all. Resources loaded through the content pipeline are automatically disposed of when the game ends. UnloadContent() is used for resources which were loaded outside of the content pipeline. Update() is called repeatedly while our game runs and is where we will update our game state based on timers and user interaction. This is where we will check for button or keyboard presses, update the positions of our sprites, change game states, etc. Anything which needs to be updated or monitored will take place in Update(). Draw() will draw everything in our game to the monitor. It's important to keep update code out of Draw() and to keep drawing code out of Update(). This will become more apparant as you make larger games.

Let's begin

First, let's run the code we currently have. Even though we haven't changed anything you should still see a windowed blue screen like the following:

Blank Screen
Blank Screen

If you did not get this screen but instead got errors when finding a suitable graphics card you may need to switch over to use Reach. XNA uses 2 different game profiles, HiDef and Reach. In an effort to keep this tutorial on track I'll just explain that if you get errors running XNA in relation to your graphics card you may be able to switch over to the Reach profile to solve the problem. To do this Right Click your game project and you should get a window like the following:

Properties
Properties

Just switch from HiDef to Reach, recompile and see if you get the blue windowed screen as shown above.

Going Full Screen

We're making a game here, so it's nice to be able to use 100% of the screen real estate. To do this we're going to add a bit of code to our Initialize method to allow us to use the entire screen. One thing to note is that debugging XNA code when running full screen is about impossible. I would recommend removing full screen support whenever debugging XNA. Add the following code to the beginning of our Initialize method:

graphics.IsFullScreen = true;
graphics.ApplyChanges();

That's how easy it is to run full screen! Now, before running your program we need to make one other change. Notice the code in our Update method under the comment "Allows the game to exit"? This code causes the game to exit if the Back button is pressed on the XBox 360 controller. If you don't have one hooked up to your pc right now you won't have a way of stopping the game from running. In order to fix this lets add the following code:

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

This will allow the game to exit if we press the Esc key on the keyboard. Now run your game and notice the blue screen takes up the entire monitor. I would now comment out the 2 lines of code which make the game full screen. Once your game is working as expected you can always uncomment these lines to see your game full screen. This will make debugging much easier.

Adding a Background

So far our game is ugly. The background is plain blue and there is nothing else to look at. Let's add a simple background image. I mentioned it earlier in this post but I will not be going into asset creation. The image I am using for my background was pulled from a random site off Google Images. In order to add your background Right-click your Content project and choose Add -> Existing Item... Browse to your file and press Add to add it to the content pipeline. If you don't see your image change the drop down in the right corner to search for Texture Files or All Files.

Once your image is loaded as a resource in our content project we need a way to easily access it in our game. First we'll create a variable to store our image in. Add the following code to the top of our class:

Texture2D background;

Next we'll need to load the background image. We'll add the following code to our LoadContent method:

background = Content.Load<Texture2D>("Background");

This notifies our game that we are loading this content as a Texture2D from the content pipeline. Notice the friendly name I gave the resource ("Background")? How do I know this is the resource name or how can I change this? It's obviously different than the file name because I didn't specify a path to this file or a file extension. Right-click the Background.png or whatever file you loaded into your content project and look at the Asset Name property. This name is how XNA will identify this resource and must match the string passed into Content.Load. File paths are not needed because when your project is built all resources in your content project are encoded and moved into the bin\Content directory as an XNB file. This file is ready for XNA consumption as a resource and your project knows exactly where to look for this file. OK, enough explaination, let's draw our background image. Add the following code to our Draw method (right after the call which clears the screen):

spriteBatch.Begin();
spriteBatch.Draw(background, new Vector2(0f, 0f), Color.White);
spriteBatch.End();

There are a few things going on here, the most important of which is the use of SpriteBatch. SpriteBatch was added for us when we first created a new XNA project and it's been set up for rendering 2D graphics (in batch) for us. To use it we call the Begin method. By default the images will be drawn in the order they are passed to SpriteBatch and will allow for transparent backgrounds (for our future sprites). If you need to override the default behavior there are a number of overloads to this method. Next we call Draw and pass in what we'd like drawn (background), where it should be drawn (top left position of the screen 0,0), and what color the tint should be (Color.White adds no tinting). Finally you must call End. This lets the SpriteBatch know we are done sending draw commands and it's time to render them to the screen. If you did this correctly you should see a screen like this when running your game:

Background added
Background added

Well, our background was drawn to the screen but it's smaller than the current resolution in my case. On your machine it may be too big or it may be perfect. The issue is that we need this to stetch across the whole screen regardless of the user's screen resolution. Thankfully this is very easy in XNA. We just change our Draw call to use one of the overloaded methods. Change the call to spriteBatch.Draw we used above to the following:

spriteBatch.Draw(background, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White);

The only thing that changed is we are no longer passing a Vector2 (which only specifies where to put the image) but instead we're passing a Rectangle object which tells the SpriteBatch where to put the image AND how wide and how tall the image should be. We're also making use of our GraphicsDevice to get the current viewport width and height which makes this dynamic at run time. Using the Viewport object instead of DisplayMode keeps our game looking the same as we switch from fullscreen to windowed mode! Running this now should give you a fullscreen background image displayed. Yay!

Adding our Sprites

Sprites are just images or animations that can move around the screen. Since we've already loaded our background image let's add 2 more: our main character the T-Rex and a hunk of meat. Again, I pulled these images off the web and spent 5 seconds making their background transparent. This will be nearly identical to drawing the background image, the only difference is that a sprite moves so we'll want to track it's position. First, add both images to your content project: Right-click the project and choose Add -> Existing Item... and locate your 2 images. Next add the following code to the top of your class:

Texture2D trex;
Texture2D meat;

Vector2 trexPos;
Vector2 meatPos;

Again, we'll store the images in Texture2D's and we'll store our sprite locations in Vector2's. Next, we'll initialize our sprite positions in the Initialize method:

trexPos = new Vector2(10f, GraphicsDevice.Viewport.Height - trex.Height);
meatPos = new Vector2(GraphicsDevice.Viewport.Width - meat.Width - 15f, GraphicsDevice.Viewport.Height - meat.Height - 15f);

Here I am setting the T-Rex start position to 10 pixels right and setting his feet to align with the bottom of the screen. I do this by taking the entire screen height and subtracting our dinosaur's height. I am setting the meat position to the bottom right corner of the screen with a bit of spacing around it. Make sure you add these lines of code AFTER the call to base.Initialize(). We are using trex.Height and meat.Height in our code and they aren't initialized until base.Initialize() is called. Next we'll load the sprites:

trex = Content.Load<Texture2D>("TRex");
meat = Content.Load<Texture2D>("Meat");

Note that the resource names are friendly; in my case "TRex" and "Meat". I specifically set these using the Right-click Properties change Asset Name method I described above. Finally, we draw our sprites. Add the following code immediately after drawing the background:

spriteBatch.Draw(meat, meatPos, Color.White);
spriteBatch.Draw(trex, trexPos, Color.White);

This must be added AFTER drawing the background in this exact order. This lets SpriteBatch know that we want the background drawn first, the meat drawn second and our T-Rex main character should appear above everything. Run your code and you should have a screen similar to the following:

Sprites added
Sprites added

Moving our Sprite

Our first game is looking a bit more interesting now but there's no movement or user interaction. Let's add keyboard support to allow our T-Rex to move around the screen. First we'll need to know how fast he should move, so let's add a variable to hold his speed. Add this to the top of our class:

float trexSpeed;

We'll also need to initialize our variable in the Initialize method:

trexSpeed = 10f;

Next let's make this T-Rex move. We'll be checking the state of our keyboard a lot so let's store this in a local variable at the top of our Update method:

KeyboardState keyState = Keyboard.GetState();

We'll use the 4 arrow keys (Up, Down, Left and Right) to move our T-Rex. Add the following code to the Update method inbetween the exit checks and the call to base.Update:

UpdateTRex(keyState);

Along with our newly created method:

private void UpdateTRex(KeyboardState keyState)
{
    if (keyState.IsKeyDown(Keys.Right))
        trexPos.X = MathHelper.Clamp(trexPos.X + trexSpeed, 0f, GraphicsDevice.Viewport.Width - trex.Width);
    if (keyState.IsKeyDown(Keys.Left))
        trexPos.X = MathHelper.Clamp(trexPos.X - trexSpeed, 0f, GraphicsDevice.Viewport.Width - trex.Width);
    if (keyState.IsKeyDown(Keys.Up))
        trexPos.Y = MathHelper.Clamp(trexPos.Y - trexSpeed, 0f, GraphicsDevice.Viewport.Height - trex.Height);
    if (keyState.IsKeyDown(Keys.Down))
        trexPos.Y = MathHelper.Clamp(trexPos.Y + trexSpeed, 0f, GraphicsDevice.Viewport.Height - trex.Height);
}

First off, I chose to put this code into a separate method because we'll be doing lots of checks and updates to our T-Rex and I wanted to separate this logic. Next you'll notice that all I need to do to check if a key is pressed is to use IsKeyDown() and supply the key I want to check. I'm also using a handy function called MathHelper.Clamp(). This method does the calculation we were going to do anyways (changing our T-Rex X or Y position) but it keeps the end value in the supplied range. In this case we want to make sure to keep the T-Rex on the screen at all times. Using Clamp is a lot easier than a long list of "if then" statements checking value constraints. If you run your code you should now be able to move your T-Rex around the screen using the keyboard... and he should not leave the screen whether the game is windowed or full-screen.

Collision Detection

If your anything like me you're dying for the T-Rex to eat that meat. This is where things get interesting. Our player will use the Space key on the keyboard to take a bite and if the T-Rex is positioned over the meat we'll relocate the meat to a random position on the screen. To accomplish this we'll use 2 BoundingBox objects, one for the T-Rex and one for our meat. A BoundingBox simply defines a rectangular cubic area in 3D space and has some handy methods for us to use with collision detection. Because we are coding in 2D, not 3D, we'll simply pass 0 into the z parameter which will make the BoundingBox flat just like we want. We'll also need an object to help us position our meat randomly. The old C# System.Random works just fine for what we're doing. Add the following code to the top of our class:

Random rand;

BoundingBox trexBounds;
BoundingBox meatBounds;

We'll also want to initialize the bounds of our sprites in the Initialize method. This way the initial bounds have already been set the moment we start checking for collisions. Add the following code to the Initialize method (after setting our Vector2 position objects):

trexBounds = new BoundingBox(new Vector3(trexPos.X, trexPos.Y, 0f), new Vector3(trexPos.X + trex.Width, trexPos.Y + trex.Height, 0f));
meatBounds = new BoundingBox(new Vector3(meatPos.X, meatPos.Y, 0f), new Vector3(meatPos.X + meat.Width, meatPos.Y + meat.Height, 0f));

rand = new Random();

BoundingBox takes 2 Vector3 parameters. The first defines the top left corner of our sprite and the other defines the bottom right corner. Notice we're passing 0 into the z parameter - if you pass anything larger you will get a 3D BoundingBox. Next we'll want to add our keyboard check for the Space key and collision detection code. Add the following code to the Update method right after the call to UpdateTRex:

if (keyState.IsKeyDown(Keys.Space))
{
    CheckForCollisions();
}

Along with our newly created method:

private void CheckForCollisions()
{
    if (meatBounds.Intersects(trexBounds))
    {
        meatPos.X = rand.Next(0, GraphicsDevice.Viewport.Width - meat.Width);
        meatPos.Y = rand.Next(0, GraphicsDevice.Viewport.Height - meat.Height);

        meatBounds = new BoundingBox(new Vector3(meatPos.X, meatPos.Y, 0f), new Vector3(meatPos.X + meat.Width, meatPos.Y + meat.Height, 0f));
    }
}

If the BoundingBox for our meat intersects our T-Rex BoundingBox then we reposition our meat to a random spot on the screen and update it's bounds. Don't forget - if you move an object you need to update it's bounds, they aren't attached. Also keep in mind, this is not ideal code. This is tossed together quickly to teach. In a real program your sprites would be encapsulated into their own classes and the bounds would be automatically updated after changing position. Speaking of remembering to update your bounds if you move an object... out T-Rex can move too. Add the following line of code to the very end of UpdateTRex:

trexBounds = new BoundingBox(new Vector3(trexPos.X, trexPos.Y, 0f), new Vector3(trexPos.X + trex.Width, trexPos.Y + trex.Height, 0f));

If you run your game you should now be able to bite the meat and see it jump around the screen.

Adding Text to the Screen

Eating meat is nice, but it'd be even nicer if we got points for doing it. This will require displaying text on the screen and keeping track of score. Using text is a bit different in XNA but you'll get used to it soon enough. To use text we need to create a Sprite Font asset which thankfully comes built into XNA. Right-Click your Content project and choose Add -> New Item... and choose the Sprite Font.

SpriteFont
SpriteFont

That's all you need to do to create this asset. SpriteFont is just a specially formatted bit of XML which you can edit any way you like. For our game let's change the Size parameter to 28 so we can see the text clearly.

<Size>28</Size>

We'll also need a variable to store this in along with a variable to keep track of our score. Add the following code to the top of the class:

SpriteFont font;

int score;

Next initialize the score in the Initialize method:

score = 0;

Then we need to load our SpriteFont in the LoadContent method:

font = Content.Load<SpriteFont>("Font");

That's all there is to loading a SpriteFont, it's exactly the same as loading an image except we tell the content pipeline that we're loading a SpriteFont. Next let's draw our score on the screen. Add the following code to the Draw method right before the call to spriteBatch.End(). This will make sure the text on the screen is always printed above everything else:

spriteBatch.DrawString(font, "Score: " + score, new Vector2(GraphicsDevice.Viewport.Width - 250f, 0f), Color.Green);

This code will print "Score:" along with our score in the top right corner of the screen in green text. Don't forget, we need to increase our score if we eat the meat. Inside CheckForCollisions we'll add the following code right after the check "if (meatBounds.Intersects(trexBounds)) {":

score += 1;

That's it. You should see your score increase as your T-Rex eats meat.

Handling Key States

You may have already noticed a flaw in our game. The Space key doesn't need to be newly pressed when hovering over the meat, it simply needs to be currently pressed. Why does this matter? Because right now the user can just hold down the space key and move around collecting points and in some cases the meat respawns under the T-Rex making the collison test pass which raises your score multiple times. We need to somehow know if the user just pressed the space key or if it was down last time we checked. To do this we'll keep the previous keyboard state saved to compare against. Let's add a variable at the top of our class to store our old keyboard state:

KeyboardState prevKeyState;

We don't want to check the state of a null object, so make sure to initialize the previous state in the Initialize method:

prevKeyState = Keyboard.GetState();

Now to change how we're checking for key presses (in this case just the space key). Right before we call CheckForCollisions() we check to make sure the Space key is pressed down. Let's change that if statement to look like this:

if ((keyState.IsKeyDown(Keys.Space)) && (!prevKeyState.IsKeyDown(Keys.Space)))

The beginning of the statement remains the same, we still check to make sure the space key is pressed. What we've added is a check to make sure that the last time we checked the keyboard that the space key was NOT pressed. This means they just pressed the space key right now. The game loop runs so fast its basically instantaneous. We only need to add 1 more line of code to complete this fix. Update the previous keyboard state every time the Update method completes. Add the following code to the end of Update just before base.Update is called:

prevKeyState = keyState;

The game should look identical to the last time you ran it only now the space key must be pressed when your over the meat and it will only register 1 time per press.

Adding Sound Effects

We'll do this in 2 parts. The first part will be really easy, we're going to add a "Bite" sound effect that will play when our users press the Space key. Again, I'm not supplying game assets but I found my sound effects at SoundBible (use WAV format). The first thing we'll do is add our sound effect to our Content project. Right-Click Add -> Existing Item... and select your WAV file. Next we'll need an object to store this resource in. Add the following code to the top of your class:

SoundEffect bite;

Next, we'll load the sound effect in LoadContent. Add the following code:

bite = Content.Load<SoundEffect>("Bite");

Isn't that convenient? The XNA content pipeline loads this resource just like all the rest, just let it know we're loading a SoundEffect. Finally, to use this sound effect we'll need to add some code to our Space key checking logic inside the Update method. The entire if statement should now look like this:

if ((keyState.IsKeyDown(Keys.Space)) && (!prevKeyState.IsKeyDown(Keys.Space)))
{
    CheckForCollisions();
    
    bite.Play(0.6f, 0f, 0f);
}

That's all it takes to add a sound effect to your game, 3 lines of code added. The Play method can also be called with 0 parameters but for some reason they are pretty loud on my machine in relation to everythng else so I used the overloaded version which takes volume, pitch and pan values. All 3 of these values should be between 1 and 0, so when I passed 0.6f into the first parameter I set the volume at 60%. You can also play with the pitch and pan, and feel free to look up the documentation on them because you can do some fun stuff by adjusting these according to where your sprite is located (for instance location based sound - more sound in right speaker, etc).

Now that we've got a nice simple sound effect let's move on to part 2 where we'll add a longer sound effect and control it's state. My "Bite" sound effect was less than a second long so state was not important. No matter how fast the user pressed the space key the sound was still ok. What if you replace that file with a longer sound effect? The user can press space multiple times and the sound will begin again and again over the top of itself causing a nasty echo effect. For this part I downloaded a "Roar" sound effect (same site) that lasts for roughly 4 seconds. Again, load the sound effect into your Content project the same way as before. This time we will need 2 variables, 1 to hold the sound effect and another to hold it's state. Add the following code to the top of your class:

SoundEffect roar;
SoundEffectInstance roarInstance;

SoundEffectInstance will hold an instance of our sound effect which lets us pause, stop and resume playing a sound. It also gives us a property we can use to check it's state. Before I get ahead of myself we should initialize these 2 in the LoadContent method:

roar = Content.Load<SoundEffect>("Roar");
roarInstance = roar.CreateInstance();

That's how easy it is to creat a SoundEffectInstance. Now that we have one let's set up our Update method so that if the user presses Enter then we will play the Roar sound effect. We'll make sure the Enter key is pressed and was not the last time we checked. We'll also make sure the sound effect has finished playing before starting it up again. Add the following code to the Update method right after the call to UpdateTRex:

if ((keyState.IsKeyDown(Keys.Enter)) && (!prevKeyState.IsKeyDown(Keys.Enter)) && 
    (roarInstance.State == SoundState.Stopped))
{
    roarInstance.Volume = 0.6f;
    roarInstance.Play();
}

Notice that all we need to do is check the State property of our sound effect instance and make sure the sound has already stopped. Also, notice how I changed the volume, I did this to point out the difference between using SoundEffect and SoundEffectInstance. SoundEffectInstance does not have the overloaded Play method so you must set the properties.

XBox 360 Controller Support

The keyboard is nice but nothing beats holding a controller in your hand. You can purchase an XBox 360 controller which will connect to the pc via wireless usb connection. I've got one and the responce time is perfect and it's 100% the same as a regular XBox 360 consol controller (except that it works on pc). If you have one (or even if you don't) it's a great feature to add to your game and it's relatively easy to do. The logic is simple, we'll add another variable to hold our controller state and we'll check for keyboard or XBox controller input. Let's start by adding the following code to the top of our class:

GamePadState prevPadState;

Like always, we need to initialize our state before we use it. Add the following code to the Initialize method:

prevPadState = GamePad.GetState(PlayerIndex.One);

As you can see when it comes to XBox controllers we have control over multiple players. The keyboard object also has an overloaded GetState which can return multiple players, but using this limits 2 people to 1 keyboard (they basically split the keys between them and one person used the number pad) which I don't like. Next, just like the keyboard state, we'll want to get the current controller state. Add the following code to the top of your Update method:

GamePadState padState = GamePad.GetState(PlayerIndex.One);

This should be looking familiar by now. Alright, now we need to change some of our code a bit. The current check we make for the user pressing Enter (the button that causes the roar) needs to be updated to also check the controller state. We'll replace the current if statement with the following:

if ((((keyState.IsKeyDown(Keys.Enter)) && (!prevKeyState.IsKeyDown(Keys.Enter))) ||
    ((padState.Buttons.X == ButtonState.Pressed) && (prevPadState.Buttons.X == ButtonState.Released))) && 
    (roarInstance.State == SoundState.Stopped))

The logic here is pretty straight forward - if the user just pressed Enter or if they just pressed X on the controller and the roar is not playing then play the sound effect. We'll also replace the if statement we used to check the Space key which caused our T-Rex to bite. Replace the entire if statement with the following:

if (((keyState.IsKeyDown(Keys.Space)) && (!prevKeyState.IsKeyDown(Keys.Space))) ||
    ((padState.Buttons.A == ButtonState.Pressed) && (prevPadState.Buttons.A == ButtonState.Released)))

The logic here is exactly the same as the roar only we're checking against the Space and A keys and we're not interested in sound effects. Don't forget to update your previous state. Add the following code to the end of the Update statement, just before calling base.Update():

prevPadState = padState;

There's only 1 thing we still need to update - the arrow keys which make T-Rex move. Change the signature of UpdateTRex to accept a GamePadState object in addition to the KeyboardState object, and pass the method our Update method instance variable padState. The call to this method in Update should look like this now:

UpdateTRex(keyState, padState);

And the method signature should look like this:

private void UpdateTRex(KeyboardState keyState, GamePadState padState)

Finally, we can add the controller movement code for T-Rex. We'll use the directional pad to move him around so it's similar to the keyboard functionality. Again, we'll use MathHelper.Clamp to ensure he stays on screen. Add the following code to the beginning of UpdateTRex:

if (padState.DPad.Right == ButtonState.Pressed)
    trexPos.X = MathHelper.Clamp(trexPos.X + trexSpeed, 0f, GraphicsDevice.Viewport.Width - trex.Width);
if (padState.DPad.Left == ButtonState.Pressed)
    trexPos.X = MathHelper.Clamp(trexPos.X - trexSpeed, 0f, GraphicsDevice.Viewport.Width - trex.Width);
if (padState.DPad.Up == ButtonState.Pressed)
    trexPos.Y = MathHelper.Clamp(trexPos.Y - trexSpeed, 0f, GraphicsDevice.Viewport.Height - trex.Height);
if (padState.DPad.Down == ButtonState.Pressed)
    trexPos.Y = MathHelper.Clamp(trexPos.Y + trexSpeed, 0f, GraphicsDevice.Viewport.Height - trex.Height);

You should now be able to run the game and use an XBox 360 controller exactly like the keyboard worked.

Making the XBox 360 Controller Rumble

Actually making the XBox controller rumble is very easy and can be done in 1 line of code, but there is a bit of a trick to getting the controller to vibrate for exactly how long you want it to. First we'll create a variable to hold the amount of time we want to rumble. Add the following code to the top of your class:

double rumbleTime;

Initialize the rumble time in your Initialize method:

rumbleTime = 0f;

Ok, let's add some rumble code. When the T-Rex eats the meat which happens in CheckForCollisions inside the statement "if (meatBounds.Intersects(trexBounds))" we want to make the controller rumble. Add the following code right after updating our score:

GamePad.SetVibration(PlayerIndex.One, 1.0f, 1.0f);
rumbleTime = 1.0f;

You can see that SetVibration lets you pick which player is being rumbled and how much to rumble the right and left motors individually. We want to make our controller rumble for exactly 1 second so we need to set rumbleTime to 1. The final step is to make sure our controller will stop rumbling, the code as it is right now will never stop rumbling. Add the following code to the bottom of our Update method (before the base.Update call of course):

if (rumbleTime > 0.0f)
    rumbleTime -= gameTime.ElapsedGameTime.TotalSeconds;
else
    GamePad.SetVibration(PlayerIndex.One, 0.0f, 0.0f);

If our rumble time has been set then we keep decreasing the amount of time left using the gameTime object. Once the rumble time runs out we simply turn the vibration off by setting the motor speeds to 0. GameTime is a great object to use when doing anything related to time. GameTime is independant of system speed which means your game will run exactly the same on your pc than it does on a super computer. More processor speed means the Update and Draw methods will be called more often but that won't disrupt our logic which is based on real time.

Bonus Code

I'm supplying the full source demonstrated in this post with a bonus inside. If you run the code exactly as is it will be identical to what we've gone through in this post. However, there is quite a bit of commented code sprinkled throughout the class. Uncomment the code and run it to see a bit more added to the game (mostly stuff we've covered but done a little different here and there).

Additional commented code adds - A dragonfly which flys across the screen every 4 seconds starting in a random position. The dragonfly also flies in a Sin wave pattern across the screen. Collision detection is added so the T-Rex can eat the dragonfly which is worth 3 points and also causes the controller to rumble. The dragonfly also uses a BoundingSphere instead of a BoundingBox for a bit more accurate collision detection. The dragonfly also buzzes as he flys past using a SoundEffectInstance with looping enabled which is paused and resumed as needed. Finally, the game keeps track of the dragonfly's state and uses timers centered around it's state to determine respawning.

DinosaurGame.zip (3.56 mb)

Thanks for reading my post, I hope you found it informative. Please leave feedback so I can improve my posts.

Tags: , , , , ,

Comments (3) -

2.
Mike H Mike H United States says:

This information is worth everyone's attention. Where can I find out more?

3.
Brian Legg Brian Legg United States says:

One of my favorite XNA sites is http://rbwhitaker.wikidot.com/xna-tutorials. Keep checking back though, I have several more XNA tutorials planned. Smile

4.
Brighton Mortgage Brighton Mortgage United Kingdom says:

Sussex now is a great place to be I have lived Brighton my town for 11 months Thanks for your blog!

5.
online backgammon online backgammon United Kingdom says:

Thanks for the kind words. Not sure what’s going on with the people telling you to stay away from informational niches, ALL my niches are information based. I have no product based sites that I’m earning with. Maybe these people were never earning and might not have things set up right? Not sure what to think of that kind of logic. If it has a high CPC and high volume of traffic with a lot of ads I can’t imagine you won’t earn. Maybe stay away from gambling and other niches that might eventually be banned/go down the tubes
I hope that helps…
ts my first ever visit on this site……good information being shared here..
thanks once again.
<a href="http://www.topbananabackgammon.co.uk/ ">Online Backgammon</a>

6.
Abayas On Sale Abayas On Sale United States says:

Wow bravo what a wonderful post you did Indeed it was an awesome knowledge base you have!.I will revisit your blog and have bookmarked it! Cheers

7.
business directory business directory United States says:

Again, excellent post. Really, I am truly thrilled I found this. I'll digg your blog post so my buddies can read your writing too!

8.
letterman jacket letterman jacket United States says:

excellent post...

9.
harley davidson leather jacketharley davidson leather jacket harley davidson leather jacketharley davidson leather jacket United States says:

Nice information. Don't forget always keep your antivirus program updated for your computer security. Thanks for sharing.

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading