Tutorial 2: Making Your Model Move Using Input

This article details how to use the XNA Framework Input API to take user input from an Xbox 360 Controller and apply it to the model displayed in Tutorial 1.

For more information on using a keyboard to control the model, see Optional Step: Controlling the Ship With Keyboard Input.

Bb203896.note(en-US,XNAGameStudio.30).gifNote
This tutorial builds on code you have written during the previous tutorial: Tutorial 1: Displaying a 3D Model on the Screen. Follow the steps in the previous tutorial before starting this tutorial.

The Complete Sample

The code in this tutorial illustrates the technique described in the text. A complete code sample for this tutorial is available for you to download, including full source code and any additional supporting files required by the sample.

Step 1: Connect Your Xbox 360 Controller

The first step in this tutorial is to make sure you can provide some input to your game. We'll use the Xbox 360 Controller. Designed for use with both a Windows computer and an Xbox 360 console, the controller features many analog and digital inputs, as well as vibration motors to give feedback to the user.

This tutorial uses only the Xbox 360 Controller, but there are more ways to take input: the XNA Framework has support for keyboard and mouse devices. Mouse devices are supported only on Windows, not on Xbox 360. For more information on the different input types, see Input Overview.

For now, connect your Xbox 360 Controller and get ready to code!

Step 2: Create Variables to Turn and Move the Model

We want our ship to move around on the screen. To do that, we'll need to create some variables to track the position and orientation of our model in the world.

Fortunately, from our last tutorial (Tutorial 1: Displaying a 3D Model on the Screen), we have two variables to do just that: modelPosition, which is a three-dimensional vector, and modelRotation, which is a floating-point value.

Currently, this system allows three degrees of translation (changing position in the world), but only one degree of rotation (changing orientation). For this demonstration, we will use that limitation to simplify our input. In many 3D games, there are three degrees of translation, and three degrees of rotation, but this is a good start.

What we can do right now to make input a little more interesting is add another vector for velocity. By updating the position with the velocity each frame, our 3D model can accelerate and decelerate smoothly. Let's try it.

  • Make sure your project from Tutorial 1: Displaying a 3D Model on the Screen is open. If it isn't, open it by selecting the File menu, clicking Open Project, and browsing to your project.
  • View the code by double-clicking Game1.cs in Solution Explorer.
  • In the code, find the Update method. Modify it to look like this:

    // Set the velocity of the model, applied each frame to the model's position.
    Vector3 modelVelocity = Vector3.Zero;
    
    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
    
        // Get some input.
        UpdateInput();
    
        // Add velocity to the current position.
        modelPosition += modelVelocity;
    
        // Bleed off velocity over time.
        modelVelocity *= 0.95f;
    
        base.Update(gameTime);
    }
    

The code you've just added to input runs every frame, and does a few different things. First, it gets rid of the code that automatically rotates the ship. You'll be controlling that by using your controller. Next, it calls a method named UpdateInput. That method does not exist yet. You'll have to create it in the next step. Last, it adds the velocity of our model to its position, moving it in the world by its velocity, and decays the velocity so that eventually the model will slow down.

Step 3: Take Input from the User

Now that the model is set up to move with velocity, you now need to provide some logic that will change the velocity based on controller input.

A simple system that we can use is an orientation thrust method: in essence, you can point the front of your model in different directions using your controller's thumbstick, then apply thrust in the direction you are pointing using your controller's trigger. By building up thrust in a direction, the model will begin to move.

We can map our controls to the game this way.

  • Pressing left or right on the left thumbstick will increase or decrease the value of the modelRotation variable by some amount.
  • Pressing on the right trigger will add a vector in the direction of our modelRotation variable to our modelVelocity vector.
  • Pressing the A button will reset the position, velocity, and rotation values of the model to "warp" the ship back to the center of the screen.

    Bb203896.note(en-US,XNAGameStudio.30).gifNote
    Both the triggers and the thumbsticks are analog controls, meaning that they can report their movements in varying amounts, rather than just on or off. On the Xbox 360 Controller, the two thumbsticks and two triggers are analog controls, and all other buttons are digital buttons.

Let's code it!

  • Find some empty space in your code below the Update method.
  • Add a new method called protected void UpdateInput().
  • Modify the method to look like this:

    protected void UpdateInput()
    {
        // Get the game pad state.
        GamePadState currentState = GamePad.GetState(PlayerIndex.One);
        if (currentState.IsConnected)
        {
            // Rotate the model using the left thumbstick, and scale it down.
            modelRotation -= currentState.ThumbSticks.Left.X * 0.10f;
    
            // Create some velocity if the right trigger is down.
            Vector3 modelVelocityAdd = Vector3.Zero;
    
            // Find out what direction we should be thrusting, using rotation.
            modelVelocityAdd.X = -(float)Math.Sin(modelRotation);
            modelVelocityAdd.Z = -(float)Math.Cos(modelRotation);
    
            // Now scale our direction by how hard the trigger is down.
            modelVelocityAdd *= currentState.Triggers.Right;
    
            // Finally, add this vector to our velocity.
            modelVelocity += modelVelocityAdd;
    
            GamePad.SetVibration(PlayerIndex.One, currentState.Triggers.Right,
                currentState.Triggers.Right);
    
    
            // In case you get lost, press A to warp back to the center.
            if (currentState.Buttons.A == ButtonState.Pressed)
            {
                modelPosition = Vector3.Zero;
                modelVelocity = Vector3.Zero;
                modelRotation = 0.0f;
            }
        }
    }
    

That method does a lot. Let's take it piece by piece to investigate exactly what you're doing with input and the model.

// Get the game pad state.
GamePadState currentState = GamePad.GetState(PlayerIndex.One);

This call to GetState retrieves a GamePadState object, which contains the information we need about the controller—in this case, thumbstick and trigger positions.

// Rotate the model using the left thumbstick, and scale it down.
modelRotation -= currentState.ThumbSticks.Left.X * 0.10f;

Retrieving the x-axis value of the left thumbstick (left and right movement) returns a value that is added to the modelrotation variable. The value is scaled down so that the rotation isn't too fast.

// Create some velocity if the right trigger is down.
Vector3 modelVelocityAdd = Vector3.Zero;

// Find out what direction we should be thrusting, using rotation.
modelVelocityAdd.X = -(float)Math.Sin(modelRotation);
modelVelocityAdd.Z = -(float)Math.Cos(modelRotation);

// Now scale our direction by how hard the trigger is down.
modelVelocityAdd *= currentState.Triggers.Right;

A little math here helps translate the rotation of the ship into a vector. Taking the sine value of the rotation gives us the proper amount of X (left and right) movement, and the cosine gives us the Z (forward and back) movement. Then, we take the vector and lengthen it by how hard the player is holding down the right trigger.

// Finally, add this vector to our velocity.
modelVelocity += modelVelocityAdd;

Finally, the created vector is added to the current velocity vector to create the final velocity vector that will be applied to move the model around.

GamePad.SetVibration(PlayerIndex.One, currentState.Triggers.Right,
    currentState.Triggers.Right);

We're using the right trigger values to give some feedback to the player with the Xbox 360 Controller vibration motors, using SetVibration . The Xbox 360 Controller has two motors that run at different speeds, so experiment to find the best combination for the action that's happening in the game.

// In case you get lost, press A to warp back to the center.
if (currentState.Buttons.A == ButtonState.Pressed)
{
    modelPosition = Vector3.Zero;
    modelVelocity = Vector3.Zero;
    modelRotation = 0.0f;
}

This little extra will move the model back to its original position and orientation in case it leaves the screen.

Optional Step: Controlling the Ship With Keyboard Input

An Xbox controller is not the only means of control available for your ship. You can also use the standard keyboard that is hooked up to your computer. The following instructions assume that you have completed the previous steps in this tutorial.

Bb203896.caution(en-US,XNAGameStudio.30).gifCaution
Keyboard input is not supported for the Xbox 360 platform. Controlling the ship on an Xbox 360 requires an Xbox 360 Controller.

The keyboard-specific code will be added to the Game1.Update and Game1.UpdateInput methods that were modified earlier. Let's handle the easy stuff first – modification of the Game1.Update method.

Modifying the Update Method

Currently, you can only exit your game with the controller or by clicking the Close button. Let's modify the following code:

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

to match this:

        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
        Keyboard.GetState().IsKeyDown(Keys.Escape))
      

The new code now closes the game when you press the Escape key.

Modifying the UpdateInput Method

This step involves more code than the previous step. Once you have modified the code, you can control the ship's rotation with the A (rotate left) and D (rotate Right) keys and accelerate with the Enter key.

In the Game1.Update method, after the existing declaration of the currentState variable, add the follwoing line of code:

        KeyboardState currentKeyState = Keyboard.GetState();
      

This captures the current state of the keyboard, including which keys were being pressed at the time.

Remove the following line (Don't forget the matching curly brace!):

        if (currentState.IsConnected)
        {
      

This allows keyboard input to be received if a controller is not connected.

Search farther down the code and find the line that assigns the X value of the left thumbstick to modelRotation. Right before that line, add the following:

        if(currentKeyState.IsKeyDown(Keys.A))
        modelRotation += 0.10f;
        else if (currentKeyState.IsKeyDown(Keys.D))
        modelRotation -= 0.10f;
        else
      

This first examines the keyboard for input (specifically, the A and D keys) and changes the rotation value accordingly. If no keyboard input is detected, the game controller is checked for input.

Search farther down for the following line of code:

modelVelocityAdd *= currentState.Triggers.Right;

Right before that code, add the following:

        if (currentKeyState.IsKeyDown(Keys.W))
        modelVelocityAdd *= 1;
        else
      

This code checks to see if the W key is pressed and, if so, increases the current velocity. If the W key is not pressed, the game controller is checked for input.

The final modification warps the ship back to its original posiiton if the Enter key is pressed.

Look for the code that checks the state of the A button on the controller (near the end of the method). Modify this line to also check for a key press. After the modification, it should look like this:

if (currentState.Buttons.A == ButtonState.Pressed || currentKeyState.IsKeyDown(Keys.Enter))

Rebuild and run the game and you can now steer the ship with the A and D keys, accelerate with the W key, and warp back with the Enter key!

Congratulations!

At this point, your ship moves and gives you feedback through your Xbox 360 Controller. The player is in control of the action.

When you're ready, let's add the final element—audio—to get you on your way. Once the player can control the action, and see and hear the results of their actions, you're well on your way to creating a game.

Next...

Ideas to Expand

Want to play around some more with input? Try these ideas.

  • Change the game to view your model from the top, as in a top-down arcade game. (Hint: Play with the cameraPosition vector. Note that you can't set it exactly up and down because the camera vector cannot be the same as the "up" vector.)
  • Scale the vibration to occur more powerfully as the ship approaches the viewer. (Hint: Use the distance between modelPosition and cameraPosition.)
  • Try using a keyboard to control the ship. See the Keyboard class. (Hint: You can plug a USB keyboard into your Xbox 360 console.)
  • Get more ideas and resources at XNA Creators Club Online.

Community Additions

ADD
Show: