Continuing on last week’s article, this week I’ll be showing you an updated way of handling movement using multi-touch. I’ll be starting with the code base from last week, so you can go ahead and get started by downloading that from my SkyDrive share.

At the end of last week, we had a triangle that was being drawn based on a Vector2 called position. position was being updated based on the TouchPanel state. This means that wherever you touch, that’s where the triangle goes. For some games, this is what we want. But what if you wanted to build a game where you had to navigate through a maze. You start on one side, touch the exit, and bang, you win. Instead, you might want to move towards the touch point. That’s what we’ll be doing here.

To begin, we need to calculate the difference between where we are now, and where we want to be. To do this, we just subtract the current position from the touch position. Let’s create a new variable in Update called direction, and set it equal to the difference. If we added position and direction, of course, we get the touch position, so changing the update we had to the following will give you the same results we saw earlier.

Refactoring Using Direction
  1. foreach (TouchLocation tl in TouchPanel.GetState())
  2. {
  3.     Vector2 direction = tl.Position - position;
  4.     position += direction;
  5. }

We don’t want to move directly to the new location, though, just in that direction. To do this, you can normalize the vector, which means that you create a new vector that points in the same direction as the original, but has a length of one. Once you’ve done that, we get the behavior we wanted.

Normalizing the Direction
  1. foreach (TouchLocation tl in TouchPanel.GetState())
  2. {
  3.     Vector2 direction = tl.Position - position;
  4.  
  5.     direction.Normalize();
  6.  
  7.     position += direction;
  8. }

You’ll notice, though, that you move very slowly. To increase the rate of movement, you can multiply the normalized vector.

Increasing the Movement Rate
  1. foreach (TouchLocation tl in TouchPanel.GetState())
  2. {
  3.     Vector2 direction = tl.Position - position;
  4.  
  5.     direction.Normalize();
  6.  
  7.     position += direction * 10;
  8. }

If you have a multi-touch capable computer, or if you have a Windows Phone, you can see that putting more than a single finger on the screen makes the triangle move towards both of them. Ideally, we’ll want to set up a primary touch point, and only move towards that. Luckily, touch points are identified uniquely with an int called Id. If we set up an int to track it, let’s call it movementId, we can determine whether a touch point is it, and only move if it is.

We’ll define movementId up with the other variables:

Adding movementId Variable
  1. Texture2D triangle;
  2. Vector2 position;
  3.  
  4. int movementId = 0;

And then modify Update down below. We’ll set the movementId on Pressed, and reset it on Released. If we didn’t reset it on Released, it would work once, then never again. It’s actually interesting to debug and check movementId each time you press on the touch panel. Each time, it increments by one. I’ve also added some code in here to prevent the wiggle you might see as the triangle jumps back and forth over the touch location. Here’s the updated code:

Updating Based on movementId
  1. foreach (TouchLocation tl in TouchPanel.GetState())
  2. {
  3.     if (tl.State == TouchLocationState.Pressed)
  4.     {
  5.         if (movementId == 0)
  6.         {
  7.             movementId = tl.Id;
  8.         }
  9.     }
  10.  
  11.     if (tl.Id == movementId)
  12.     {
  13.         Vector2 direction = tl.Position - position;
  14.  
  15.         if (direction.LengthSquared() > 100)
  16.         {
  17.             direction.Normalize();
  18.  
  19.             position += direction * 10;
  20.         }
  21.     }
  22.  
  23.     if (tl.State == TouchLocationState.Released)
  24.     {
  25.         if (tl.Id == movementId)
  26.         {
  27.             movementId = 0;
  28.         }
  29.     }
  30. }

Now only the first finger down will cause movement.

You’ll notice that the triangle still just points in one direction though. That’s easy enough to address. The SpriteBatch Draw method has an overload that includes an argument of type float called rotation. If we set one of those up, we can pass that over. We’ll start by setting up a variable called rotation:

Adding rotation Variable
  1. Texture2D triangle;
  2. Vector2 position;
  3. float rotation;

Then modify Update to get the rotation. We have a Vector2 we called direction, which gives us an X and a Y we can use to find an angle. If you look back at your time in trigonometry, you can remember SOH CAH TOA, Some Old Hippy Caught Another Hippy Tripping On Acid, Sine Opposite Hypotenuse, Cosine Adjacent Hypotenuse, Tangent Opposite Adjacent. Since we have X and Y, Adjacent and Opposite, we want to use Tangent. So tan(theta) = y/x, that means theta = arctan(y/x). We have to be careful here for two reasons. First of all, if X is zero, that’s division by zero, and that’s a problem. Second of all, there is a well known problem in finding out the origin quadrant of the original angle, basically, this means you don’t know the sign. I was given a solution to this when I was giving a presentation at one of the Windows Phone launch events in the Atan2 method. It basically handles all of that for you. So with that in mind, the Update code becomes:

Getting rotation
  1. foreach (TouchLocation tl in TouchPanel.GetState())
  2. {
  3.     if (tl.State == TouchLocationState.Pressed)
  4.     {
  5.         if (movementId == 0)
  6.         {
  7.             movementId = tl.Id;
  8.         }
  9.     }
  10.  
  11.     if (tl.Id == movementId)
  12.     {
  13.         Vector2 direction = tl.Position - position;
  14.  
  15.         if (direction.LengthSquared() > 100)
  16.         {
  17.             direction.Normalize();
  18.  
  19.             rotation = (float)(Math.Atan2(direction.Y, direction.X));
  20.  
  21.             position += direction * 10;
  22.         }
  23.     }
  24.  
  25.     if (tl.State == TouchLocationState.Released)
  26.     {
  27.         if (tl.Id == movementId)
  28.         {
  29.             movementId = 0;
  30.         }
  31.     }
  32. }

And finally, we have to modify the Draw function to make use of the new rotation information. The arguments it asks for are:

  • The Texture2D, triangle
  • A Vector2, position
  • A source rectangle, which will determine which portion of the texture to draw, null means the whole thing
  • A Color to tint the Texture2D, white will give you the unmodified texture
  • A float for rotation, which we pass the calculated value
  • A Vector2 that it will treat as the origin about which the texture will be rotated. In this case, I like it to be in the center of the back of the triangle
  • A scale multiplier. 1 will maintain the original size. 0 would reduce it to nothing.
  • SpriteEffects, which would allow you to flip the texture vertically or horizontally
  • A float for layerDepth, the Z-order. Since I’m only drawing one thing, 0 will work.

 

Drawing Using rotation
  1. spriteBatch.Begin();
  2. spriteBatch.Draw(triangle, position, null, Color.White, rotation, new Vector2(0, triangle.Height / 2), 1f, SpriteEffects.None, 0f);
  3. spriteBatch.End();

And we’re good to go. Next week, I’ll show how collision works.

Download the latest version of the source code