Moving Sprite Based on Keyboard Input
Now that we covered how to
read the current state of the keyboard and can determine whether a key
is pressed or released, let’s move a sprite around the screen using the
keyboard. First, you need to declare some member variables to store your
sprite texture, the sprites position, and the last keyboard state:
Texture2D spriteTexture;
Vector2 spritePosition;
float spriteSpeed = 100f;
KeyboardState lastKeyboardState;
Next, load a texture to use as your sprite. In the game’s LoadContent method, add the following line of code:
spriteTexture = Content.Load<Texture2D>("XnaLogo");
Now, you are ready for some keyboard input. In the game’s Update method, you need to store the current state and move the sprite’s position based on the arrow keys, as follows:
// Store the current state of the keyboard
KeyboardState currentKeyboardState = Keyboard.GetState();
// Move the sprite position based on keyboard input
if (currentKeyboardState.IsKeyDown(Keys.Left))
spritePosition.X -= spriteSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (currentKeyboardState.IsKeyDown(Keys.Right))
spritePosition.X += spriteSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (currentKeyboardState.IsKeyDown(Keys.Up))
spritePosition.Y -= spriteSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (currentKeyboardState.IsKeyDown(Keys.Down))
spritePosition.Y += spriteSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
// Store the keyboard state for next frame
lastKeyboardState = currentKeyboardState;
Whenever
the arrow keys are pressed, you move the sprite in the appropriate
direction. Notice that “up” corresponds to moving the sprite in the
negative Y direction because values for Y increase when moving down the
screen.
When you move the sprite,
you don’t move by a constant value. Take the elapsed game time into
account. Each frame can take a different amount of time to complete. If a
constant movement amount is used to update the position of the sprite,
it might appear to shudder as it moves across the screen. Correct this
using the GameTime.ElapsedGameTime
property. You previously used the elapsed time in total seconds. This
means pressing the right arrow for one second moves the sprite 100
pixels to the right no matter how many frames happen over that second.
Note
Older PC video games didn’t
always take into account the elapsed time when animating and moving
sprites. As computing power of PCs increased, these games were able to
run faster and so did the animations and speed of the games, making some
of the game unplayable.
Finally, you need to
display the sprite on the screen using the position you have been
updating. To display the sprite, add the following lines of code to your
game’s Draw method:
spriteBatch.Begin();
spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
spriteBatch.End();
Now if you run your game,
you can control the sprite on the screen using the keyboard arrow keys.
Congratulations—you have created your first XNA Game Studio interactive
game. This simple game should look similar Figure 1.
Onscreen Keyboard
If your goal is to gather user text input, the Keyboad class is not the best solution. If you try to implement text entry using the Keybord state APIs, you run into a number of issues such has handling the current shift state of the keyboard and making sure to call GetState quick enough that you don’t miss a single key press. The Keyboad API is not designed for text entry (see Figure 2).
For text entry, use the Guide.BeginShowKeyboardInput method. If you are familiar with C#, notice that BeginShowKeyboardInput
is an asynchronous method. This means that when you ask to show the
keyboard, this method quickly returns but that does not mean the
keyboard has been shown or that the user has entered text into the
keyboard. For your game to continue running, it needs to handle a method
that takes a number of frames to return. BeginShowKeyboardInput returns an IAsyncResult object. This object is used each frame to check on the state of the call to BeginShowKeyboardInput. If the IsCompleted
property of the result is true, that means the call has completed and
that you can request the string from the keyboard input. This is done
calling Guide.EndShowKeyboardInput
passing in the result. The method returns the string the user entered.
The string might be null because the user can cancel the onscreen
keyboard.
To
add the onscreen keyboard to your game, store the async result over
multiple frames. You also need to store the string returned. Add the
following members to your game class:
IAsyncResult keyboardAsyncResult;
string message = "";
We cover the Guide.BeginShowKeyboardInput
method, sometimes called gamer services. The
important point to know now is that your game constructor needs to
initialize gamer services on Windows and Xbox 360 to use the onscreen
keyboard. This is done by adding the GamerServicesComponent to the Components list of your game. On Windows and Xbox, add the following line of code at the end of your game’s constructor:
Components.Add(new GamerServicesComponent(this));
Next, check for a spacebar
press. If the user presses the spacebar and the onscreen keyboard is not
already visible, you start to display the keyboard. Add the following
lines of code to your game’s Update method:
if (keyboardAsyncResult == null &&
currentKeyboardState.IsKeyDown(Keys.Space) && lastKeyboardState.IsKeyUp(Keys.Space))
{
keyboardAsyncResult = Guide.BeginShowKeyboardInput(PlayerIndex.One,
"Title",
"Description",
"Default Text",
null, null);
}
In the previous code,
check the keyboard async result to make sure you are not already waiting
for a previous call. Then, if the spacebar is pressed, you make a call
to BeginShowKeyboardInput. The first parameter is the PlayerIndex you want to have control of the onscreen keyboard. This can be only PlayerIndex.One
for the Windows and Windows Phone platforms, but on Xbox 360, up to
four players can play a game and you might want player three to enter
his or her character’s name. In this case, player three should be the
one controlling the onscreen keyboard. The next three parameters are string values representing the title, description, and default text to display. We cover the last two values a little later.
Now that you asked for the
keyboard to display, it should display when the user presses the
spacebar. But when a user enters text or cancels the keyboard, the
return value is thrown away. To get the result of what the user enters
into the keyboard, you need to call Guide.EndShowKeyboardInput. Calling this method blocks until the previous call to BeginShowKeyboardInput completes, so first check whether the call is complete. After the code you just added, place the following lines of code:
// Check to see if the result has completed
if (keyboardAsyncResult != null && keyboardAsyncResult.IsCompleted)
{
message = Guide.EndShowKeyboardInput(keyboardAsyncResult);
if (message == null)
{
// String is null since the user canceled the input
}
keyboardAsyncResult = null;
}
Notice that the EndShowKeyboardInput call takes the IAsyncResult from the BeginShowKeyboardInput call and returns the string entered by the user. This string can be null if the user canceled the onscreen keyboard.
In the previous code, you use the IAsyncResult from the BeginShowKeyboardInput call to check whether the call completed each frame. Instead of checking the status of each frame, you can use an AsyncCallback method that is called after the method completes. To use the callback method, add the following method to your game:
void StoreKeyboardResult(IAsyncResult result)
{
message = Guide.EndShowKeyboardInput(result);
if (message == null)
{
// String is null since the user canceled the input
}
}
After the BeginShowKeyboardInput is complete and the user has entered text, this method is called. Similar to the previous example, you then call EndShowKeyboardInput and check the resulting string. To have BeginShowKeyboardInput use the callback, change the call to look like the following:
if (currentKeyboardState.IsKeyDown(Keys.Space) && lastKeyboardState.IsKeyUp(Keys.Space))
{
keyboardAsyncResult = Guide.BeginShowKeyboardInput(PlayerIndex.One,
"Title",
"Description",
"Default Text",
StoreKeyboardResult, null);
}
You no longer need to store the IAsyncResult because the StoreKeyboardResult method is called after the user has completed entering text.
The last parameter of BeginShowKeyboardInput is used to pass in an object to be stored in the IAsyncResult.AsyncState
property. This can be useful when you need a specific object after the
operation is completed. For example, if you are asking a user to enter
his or her character name and you want to store the result in your
game’s specific Character class, you can pass the user’s instance of that Character class into BeginShowKeyboardInput and when the call completes, use the IAsyncResult.AsyncState property to access the instance of the Character class.