Intro to XNA on Windows Phone Part 4

The first 3 parts of this series covered the first step of game development, Draw Stuff. Now we move on to the next step, Handle and Act on Input.

This sample starts where the last one left off, here is the source code we’ll build on:

I’ll be following up this series of posts with more information on checking for input, but for now we’ll focus on a simple case where we’re just checking to see if the screen is being touched.

Step 2: Handle and Act on Input

For this demo we’ll handle touch data, but the techniques are similar for the accelerometer and other common inputs on phone, PC, and Xbox. In XNA all of these inputs are done using a polling technique, and generally you’ll call a GetXXXState method in your Update method.

One of the most popular categories of games on phones are “one button” games. These come in a bunch of variations, but basically the input is simplified so that you only detect whether the screen is currently pressed and you don’t care where. It’s surprising how much fun a game can be with such limited input. Some examples of this type of game are Funny Jump, Penguin, and Gravity Guy.

Given the simplicity of input and the popularity of this type of game, we’ll focus on this type of game for this sample. We’ll make the player jump if the screen is touched.

First, let’s add another texture to the content project. Click on the image below and then right click on it to save it to your computer as jump.png.


Add this image to the Content project.

Then, we need some fields to store the current state of the jump:

Texture2D playerJump;
bool isPressed = false;
bool jumping = false;
float velocityY = 0;
float offsetY = 0;
float accelerationY = 600f;
float startVelocityY = -500f;

Then as we’ve seen a few times already, we load the content in the LoadContent method:

playerJump = Content.Load<Texture2D>("jump"); 

The Update method is where the most changes will happen. Replace the previous Update logic we added with the following:

if (jumping)
    offsetY += velocityY * (float)gameTime.ElapsedGameTime.TotalSeconds;
    if (offsetY > 0)
        offsetY = 0;
        jumping = false;
    velocityY += accelerationY * (float)gameTime.ElapsedGameTime.TotalSeconds;

TouchCollection touchPoints = TouchPanel.GetState();
isPressed = touchPoints.Count > 0;

if (isPressed && !jumping)
    jumping = true;
    currentPlayerAnimationDelay = 0;
    velocityY = startVelocityY;

currentPlayerAnimationDelay += gameTime.ElapsedGameTime.TotalSeconds;
if (!jumping)
    while (currentPlayerAnimationDelay > playerAnimationDelay)
        playerCurrentFrame = playerCurrentFrame % 10;
        currentPlayerAnimationDelay -= playerAnimationDelay;
    double totalJumpTime = Math.Abs(startVelocityY * 2) / accelerationY;
    playerCurrentFrame = (int)MathHelper.Lerp(0, 10.999f, (float)(currentPlayerAnimationDelay / totalJumpTime));
    if (playerCurrentFrame > 10) playerCurrentFrame = 10;
platformOffset += platformScrollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (platformOffset > platform.Width) platformOffset -= platform.Width;

There are a few things going on in here. First of all it handles gravity if the character is jumping. Remember your high school physics class right now! The change in velocity is equal to acceleration multiplied by time. So when the character is jumping, we modify the Y velocity based on gravity and elapsed time and also check if we’re done jumping (if we’re back to the ground level).

Next we see if any touch points are pressed by getting the state of the TouchPanel. If this wasn’t a one button game you could interrogate how many points were active, and what the state is. These include Moved, Pressed, and Released, so you can tell if the user is pressing, dragging a finger, or letting go.

Finally if we’re jumping, the current frame to display is based on how far we are through the jump. Here comes high school physics again. If I remember correctly the total jump time will be double the start velocity divided by gravity, please let me know if this is incorrect.

One thing that may look a little different in this part is that since the amount of time jumping depends on the initial velocity and we want to spread all of the frames of the jump animation across that time, we need to calculate how far we are through the jump at any given time and show the correct frame. This is a bit more complicated than just changing the frame on an interval but it’s not too bad. One way we could do this is to calculate the total time, divide it by the number of frames we have, and use this interval to handle animation just like we did for running.

In order to show something a little different I’ve decided to use the MathHelper.Lerp method. The MathHelper is a very useful class provided by XNA and I highly recommend you dig through the functionality it provides.

Lerp is shorthand for “Linear intERPolation” and by providing a start value, end value, and percentage from 0 to 1 it will give back the appropriate value in between. So in our case, to calculate the current frame we can use a start value of 0, and end value of 10.999 (close enough to 11 without getting there) and the percentage comes from the time since the start of the jump divided by the total jump time. Feeding this into Lerp gives us the current frame. Also look at SmoothStep which interpolates between two values using a curve that is generally more pleasing to the eye when doing movement.

If we’re not jumping we do the same thing we were doing before to handle the running animation.

And in the Draw method we can replace the spriteBatch.Draw for the player with this:

if (!jumping)
    spriteBatch.Draw(player, new Vector2(80, 300 + offsetY), new Rectangle(playerCurrentFrame * player.Height, 0, player.Height, player.Height),
        Color.White, 0, Vector2.Zero, 1, SpriteEffects.FlipHorizontally, 0);
    spriteBatch.Draw(playerJump, new Vector2(80, 300 + offsetY), new Rectangle(playerCurrentFrame * playerJump.Height, 0, playerJump.Height, playerJump.Height),
        Color.White, 0, Vector2.Zero, 1, SpriteEffects.FlipHorizontally, 0);

If we’re not jumping we draw exactly as we were drawing before for the running animation, otherwise we need to draw the jumping animation and draw the correct frame.

Now if you tap the screen the player should jump like this:


The code as of the end if this step is here:

Next time we’ll look at adding sound effects.