Moving Items with Microsoft Visual Studio, XNA Game Studio and a GamePad

In my previous article, I briefly covered installing the XNA Game Studio. The article proceeded to how you how to create a simple application that displayed an image. You also learned how to change the size and location of that image within your code.

Using an Xbox Game Pad with Your Applications

Now, you'll learn how to use an Xbox game pad with your applications. You'll see how to use some of the buttons on the Xbox controller including the X, Y, A, and B button as well as the LeftShoulder, RightShoulder, LeftStick, RightStick, and other controls. Specifically, you'll see how to move an image around on the page using some of these controls.

You should start with the final code listing from the previous article that helped you load an image. Alternatively, you can create a new XNA Windows Game project. From either listing, you'll find two lines of code that were automatically added to the project when it was created. These lines are worth pointing out now:


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

If you look at this code, you'll see that it is referencing a game pad. If you have a wired Xbox controller, you can plug it into a USB port on your PC and use it. The above line of code will then exit the game if the back button on the game pad is pressed. You could also test for other buttons on the controller. For example, the following adds checks to see if the A button is pressed:

if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
{
    // Do something...
}

In addition to the A button, you can also check the B, X, Y, and other buttons for a press in the same way. You can also check for button presses on the left and right sticks as well. If you do this in the Update method, then you will check the status of the button each time the game loops, which will be around 30 times a second. As such, each time Update is checked, if the A button is pressed, the logic above will execute. For example, if you wanted to move a rectangle in your program, you could use the A button to move it down on the page. You could do this by adjusting where the rectangle is drawn. A rectangle called myRect could have its Y value incremented each time the program loops and it finds the A button is pressed:

if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
{
    if (myRect.Y < (GraphicsDevice.Viewport.Height - myRect.Height ))
        myRect.Y++;
}

This code would adjust the Y coordinate where the rectangle called myRect is being drawn on the screen. It would do this when the Y coordinate is less than the size of the window minus the size of the image. This means that the image will move down the window, but not off the window. Of course, this code only adjusts the Y coordinate in the rectangle. It doesn't actually draw the rectangle on the page, nor does it set up the rectangle initially. Of course, you learned how to do that in the previous article.

Using what has just been covered, you could use the X, Y, A, and B buttons to move the image around the window. To do this, you follow similar logic to what was presented earlier:

if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
{
    if (myRect.Y < (GraphicsDevice.Viewport.Height - myRect.Height ))
        myRect.Y++;
}
if (GamePad.GetState(PlayerIndex.One).Buttons.Y == ButtonState.Pressed)
{
    if (myRect.Y > 1)
        myRect.Y--;
}
if (GamePad.GetState(PlayerIndex.One).Buttons.B == ButtonState.Pressed)
{
    if (myRect.X < (GraphicsDevice.Viewport.Width - myRect.Width )) 
        myRect.X++;
}
if (GamePad.GetState(PlayerIndex.One).Buttons.X == ButtonState.Pressed)
{
    if (myRect.X > 1)
        myRect.X--;
}

This code can be added to the update method of the listing from the previous article. Listing 1 provides you with the complete listing with this code added.

Listing 1. Moving a picture around with a gamepad

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace PictureMover
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Texture2D myPicture;
        private Rectangle myRect;

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

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            myPicture = Content.Load<Texture2D>("CGPicture");
            myRect = new Rectangle(50, 100, myPicture.Width, (2 * myPicture.Height));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values. </param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
            {
                if (myRect.Y < (GraphicsDevice.Viewport.Height - myRect.Height))
                    myRect.Y++;
            }
            if (GamePad.GetState(PlayerIndex.One).Buttons.Y == ButtonState.Pressed)
            {
                if (myRect.Y > 1)
                    myRect.Y--;
            }
            if (GamePad.GetState(PlayerIndex.One).Buttons.B == ButtonState.Pressed)
            {
                if (myRect.X <  (GraphicsDevice.Viewport.Width - myRect.Width ))
                    myRect.X++;
            }
            if (GamePad.GetState(PlayerIndex.One).Buttons.X == ButtonState.Pressed)
            {
                if (myRect.X > 1)
                    myRect.X--;
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(myPicture, myRect, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Moving Items with Microsoft Visual Studio, XNA Game Studio and a GamePad

Checking GamePad Button Presses

It is worth noting that the previous code listing checks to see if the button is pressed. As you hold the button down, your image will continue to move until it reaches the edge of the window. In many games, you want to test the pressing of a button. It might be that when you press a button down and release it that you only want a single action to occur. If that is the case, then if it takes you a half second to press and release a button, then you might actually call the Update method around 15 times. For the picture application that means a single push and release could cause you to move the image 15 pixels.

What if you wanted only one action to happen each time you pressed the button? You need a way to tell that the button was pressed and released. The easiest way to determine a full button press is to check for the button to be pushed down and then check for it to be released. When both actions have happened, then you know that you have a full button press.

To check for a button press, start by setting up a variable to hold the state of the controller the first time through. If the button was in a pressed state and then changes to released, you can then react. Otherwise, you don't have to count it as a press.

Tracking GamePad State

In the previous listing, the state of the game pad is called several times. In order to save a few processing cycles, you can actually create an object to hold the gamepad state and then check the various button states from that variable. More importantly, you can create a second object to hold the state of the gamepad the previous time you checked.

For the sample listing, two GamePadState objects are created called GamePad1 and origGamePad1. These objects are declared at the top of the Game1 class along with the other variables:

private GamePadState GamePad1;
private GamePadState origGamePad1;

With the variables declared, you now need to initialize them. You can initialize the origGamePad1 in the LoadContent method. You can initialize it to the state of the first gamepad:

origGamePad1 = GamePad.GetState(PlayerIndex.One);

This puts the game state from the first pad into origGamePad1. You'll now be able to use origGamePad1 to check the status of each of the buttons. More importantly, you'll be able to use it to check what the status was on the previous cycle of the gaming loop.

Of course, each time the game loops, you need to get the status again for the gamepad. You can do this using the GamePad1 type you declared:

GamePad1 = GamePad.GetState(PlayerIndex.One);

Once you get this new status you'll want to then compare each button's new state with the old state that was stored in origGamePad1. If the new state is released and the old state was pressed, then you know you have a completed button press and can therefore take action:

if (GamePad1.Buttons.A == ButtonState.Pressed &&
    origGamePad1.Buttons.A == ButtonState.Released )
{
    // Do something...
}

Once you've checked the states of the button and determined if button has been pressed, you can then take action. After all of your code is completed, you can then wrap up by shifting the current gamepad state to the previous state stored in the origGamePad1 object:

origGamePad1 = GamePad1;

With this, you've got all the pieces to check for button presses. Listing 2 at the end of this article contains a re-write of Listing 1 using. In Listing 2, you use the new logic for checking for completed button presses. Running the code in listing 2 requires that you keep pressing the button if you want to move an image around on the screen.

Wrapping Up

In this article you learned how to check for a complete button press rather than allowing the game to keep reacting over and over when a button is held down. In an upcoming article, you also learn how to do a few other actions based on what you can do with the controllers. This includes working with the cap controls on the gamepads.

Listing 2: Capturing individual key presses

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace PictureMover
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Texture2D myPicture;
        private Rectangle myRect;

        private GamePadState GamePad1;
        private GamePadState origGamePad1;

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

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            myPicture = Content.Load<Texture2D>("CGPicture");
            myRect = new Rectangle(50, 100, myPicture.Width, (2 * myPicture.Height));

            origGamePad1 = GamePad.GetState(PlayerIndex.One);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            GamePad1 = GamePad.GetState(PlayerIndex.One);

            if (GamePad1.Buttons.A == ButtonState.Pressed &&
                origGamePad1.Buttons.A == ButtonState.Released )
            {

                if (myRect.Y < (GraphicsDevice.Viewport.Height - myRect.Height))
                    myRect.Y++;

            }
            if (GamePad1.Buttons.Y == ButtonState.Pressed &&
                origGamePad1.Buttons.Y == ButtonState.Released )
            {
                if (myRect.Y > 1)
                    myRect.Y--;
            }
            if (GamePad1.Buttons.B == ButtonState.Pressed &&
                origGamePad1.Buttons.B == ButtonState.Released)
            {
                if (myRect.X < (GraphicsDevice.Viewport.Width - myRect.Width))
                    myRect.X++;
            }
            if (GamePad1.Buttons.X == ButtonState.Pressed &&
                origGamePad1.Buttons.X == ButtonState.Released )
            {
                if (myRect.X > 1)
                    myRect.X--;
            }

            origGamePad1 = GamePad1;

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(myPicture, myRect, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}


About the Author

Bradley Jones

Bradley Jones, in addition to managing CodeGuru, Brad! oversees the Developer.com Newtwork of sites including Codeguru, Developer.com, DevX, VBForums, and over a dozen more with a focus on software development and database technologies. His experience includes development in C, C++, VB, some Java, C#, ASP, COBOL, and more as well as having been a developer, consultant, analyst, lead, and much more. His recent books include Teach Yourself the C# Language in 21 Days, Web 2.0 Heroes, and Windows Live Essentials and Services.
Google+ Profile | Linked-In Profile | Facebook Page

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds