December, 2010

Posts
  • File → New Project

    Using Multi-Touch for Movement (TriangleShooter)

    • 2 Comments

    Welcome to the second post in the series building TriangleShooter. In this post, I will be going over drawing a sprite to the screen, then moving it around using multi-touch. In the last post, I described what you got from File –> New Project, so we didn’t have any code as such as our starting point. This means that we’ll be starting from a new project named TriangleShooter. To make this happen, open “Microsoft Visual Studio 2010 Express for Windows Phone” from the start menu and selecting File –> New Project. From the New Project dialog that comes up, choose XNA Game Studio 4.0, pick Windows Phone Game (4.0), change the name at the bottom of the dialog to TriangleShooter and click OK.

    image

    This will get you the solution we went over last time, including two projects, TriangleShooter and TriangleShooterContent. We need to add a graphic that we will draw to the screen, so we’ll start by adding that into the Content project. I’ve added the Triangle graphic to SkyDrive, so you can grab that now. Once you’ve got that, we’ll add it into the project by right clicking on the content project, and choosing Add –> Existing Item…

    image

    Navigate to wherever you extracted the Triangle.png file, select it, and click Add. It should pop up under the Content Project. You can also drag and drop into the content folder if you prefer that. At this point, let’s take a look at some of the properties of the newly added Triangle.png file. To do this, right click on the file in the solution explorer and select properties. This should pop up the properties window beneath the solution explorer.

    image

    By default, a few things are set here based on the file type. The most important one to look at is the Asset Name. That is how you will refer to the file in code when we load it. Also important are the Content Importer and Processor. These determine how the file will be treated inside of the application. In this case, it is being handled by the Texture pipeline, but you could make use of a custom importer and processor, something I’ll take a look at in a future post.

    So now that we’ve got the file included in the content project, we’ll need to draw it. To begin with, we’ll create a variable to store the texture. The type will be Texture2D, and we’ll name it triangle. Place the following code inside of the Game1 class beneath the SpriteBatch declaration, so it looks like this:

    Adding a variable for triangle
    1. public class Game1 : Microsoft.Xna.Framework.Game
    2. {
    3.     GraphicsDeviceManager graphics;
    4.     SpriteBatch spriteBatch;
    5.  
    6.     Texture2D triangle;

    Next, we need to load this content into the variable. Since we’re loading content, we’ll use the LoadContent method. Inside of LoadContent, we need to make use of the templated Content.Load method, bringing over a Texture2D with an asset name of Triangle, and put that into triangle.

    Loading triangle
    1. protected override void LoadContent()
    2. {
    3.     // Create a new SpriteBatch, which can be used to draw textures.
    4.     spriteBatch = new SpriteBatch(GraphicsDevice);
    5.  
    6.     triangle = Content.Load<Texture2D>("Triangle");
    7. }

    Finally, we’ll need to draw it. Naturally, we’ll do this in the Draw method. We’ll make use of the SpriteBatch, which allows you to queue up a bunch of draw commands for efficiency. It does this by requiring a call to Begin at the beginning, and End when you’re done. Between those calls come all of your drawing code. The SpriteBatch’s Draw function is highly overloaded. We’ll use the overload that takes a Texture2D, a Vector2, and a Color. The Texture2D is what you want to draw, the Vector2 is where you want the top left corner of the graphic to be, and the Color is a tint applied to the graphic. In this case, we’ll draw the triangle, draw it in the top left of the display, and tint it white, which won’t change the color.

    Drawing the Triangle
    1. protected override void Draw(GameTime gameTime)
    2. {
    3.     GraphicsDevice.Clear(Color.CornflowerBlue);
    4.  
    5.     spriteBatch.Begin();
    6.     spriteBatch.Draw(triangle, Vector2.Zero, Color.White);
    7.     spriteBatch.End();
    8.  
    9.     base.Draw(gameTime);
    10. }

    If you run the code now, this is what you’ll see:

    image

    Looks good! There are a couple of changes we should make before going any further, though. First of all, I want this application to be full screen. To do this, I’ll add a line of code to the constructor, telling it that I want the graphics to be displayed in full screen mode.

    Switching to Full Screen
    1. public Game1()
    2. {
    3.     graphics = new GraphicsDeviceManager(this);
    4.     Content.RootDirectory = "Content";
    5.  
    6.     graphics.IsFullScreen = true;
    7.  
    8.     // Frame rate is 30 fps by default for Windows Phone.
    9.     TargetElapsedTime = TimeSpan.FromTicks(333333);
    10. }

    Secondly, let’s change the background color to black. It adds a more spacey feel to the game. We’ll just change the line of code that clears the screen down in the Draw method to clear using Color.Black instead of Color.CornflowerBlue.

    Clearing the Screen with Black
    1. protected override void Draw(GameTime gameTime)
    2. {
    3.     GraphicsDevice.Clear(Color.Black);

    Now when you run the program, it looks a bit better:

    image

    For the last bit of this post, I’ll show you how to make the Triangle move around using multi-touch. We’ll start by adding another variable to the class to track position, called position. It will be of type Vector2, to hold the top left of the graphic. We’ll put it directly after the declaration of the triangle, like so:

    Adding the position Variable
    1. Texture2D triangle;
    2. Vector2 position;

    We then need to give it a default value. Since we’re not actually loading it from anywhere, it won’t go into LoadContent, instead it goes into the Initialize method.

    Initializing position
    1. protected override void Initialize()
    2. {
    3.     position = Vector2.Zero;
    4.  
    5.     base.Initialize();
    6. }

    Then, we modify the line of code that draws the triangle to make use of position, replacing Vector2.Zero with position.

    Using position in Draw
    1. spriteBatch.Begin();
    2. spriteBatch.Draw(triangle, position, Color.White);
    3. spriteBatch.End();

    If we run now, we get the same behavior as we did before. This makes sense, because we’re not actually updating the position anywhere. To update the variable, we’ll want to add some code into the Update method. Specifically, we’ll go through the active touch points and set position to their position. In the next post, I’ll go into a method of finding out which touch point is the primary (first) touch point, and only updating using it, but for now, we’ll just update it using whatever touch point gets there. To do this, we’ll just loop through each of the touch points using a foreach loop and set the position, as so:

    Updating the position
    1. protected override void Update(GameTime gameTime)
    2. {
    3.     // Allows the game to exit
    4.     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
    5.         this.Exit();
    6.  
    7.     foreach (TouchLocation tl in TouchPanel.GetState())
    8.     {
    9.         position = tl.Position;
    10.     }
    11.  
    12.     base.Update(gameTime);
    13. }

    And that’s it! Run the program, and you can move the triangle around with your finger (or mouse, in the emulator). If you have a multitouch display or a Windows Phone 7, you can see what happens when you put more than one finger on the screen. Essentially, it updates to each of the touch points, which means whichever was the last one down will be where the triangle goes.

    image

    Download the completed source code

  • File → New Project

    Advanced Movement with Multi-Touch (TriangleShooter)

    • 0 Comments

    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

  • File → New Project

    Basic Collision (TriangleShooter)

    • 0 Comments

    Continuing from last week, in this article I’ll be walking through some basic collision. Again, I’ll be starting with the code base from last week, so you can go and grab that over at my SkyDrive share.

    To get started, we’ll need something to collide with. I’ve created some enemies for use with the game, which you can download from SkyDrive. I’ll start with Enemy-6.png, the one that looks like this:

    Enemy-6

    To do this, add it into the TriangleShooterContent project by right-clicking the project in the Solution Explorer and choosing Add –> Existing Item, and selecting Enemy-6.png. We’ll then need to create a variable to hold the texture, and a variable to track it’s position. In a later article, we’ll add some basic AI to control it, but for now, it will be static.

    Variable Declaration
    1. Texture2D triangle;
    2. Vector2 position;
    3. float rotation;
    4.  
    5. Texture2D enemy;
    6. Vector2 enemyPosition;

    Initialize Position
    1. protected override void Initialize()
    2. {
    3.     position = Vector2.Zero;
    4.     enemyPosition = new Vector2(600f, 200f);
    5.  
    6.     base.Initialize();
    7. }

     

    Loading Content
    1. protected override void LoadContent()
    2. {
    3.     // Create a new SpriteBatch, which can be used to draw textures.
    4.     spriteBatch = new SpriteBatch(GraphicsDevice);
    5.  
    6.     triangle = Content.Load<Texture2D>("Triangle");
    7.     enemy = Content.Load<Texture2D>("Enemy-6");
    8. }

    We’ll also need to add a line to the Draw method to display the enemy.

    Drawing the Enemy
    1. protected override void Draw(GameTime gameTime)
    2. {
    3.     GraphicsDevice.Clear(Color.Black);
    4.  
    5.     spriteBatch.Begin();
    6.     spriteBatch.Draw(triangle, position, null, Color.White, rotation, new Vector2(0, triangle.Height / 2), 1f, SpriteEffects.None, 0f);
    7.     spriteBatch.Draw(enemy, enemyPosition, Color.White);
    8.     spriteBatch.End();
    9.  
    10.     base.Draw(gameTime);
    11. }

    If we run the application, we now can see that there is an enemy with which we can collide.

    image

    But how can we tell if the collision has happened? How about we change the color of the triangle to red? To do this, we’ll have to add a variable to track the color, and set it to red when the collision happens.

    Adding a Color Variable
    1. Texture2D triangle;
    2. Vector2 position;
    3. float rotation;
    4. Color triangleColor;

    Initializing the Color
    1. protected override void Initialize()
    2. {
    3.     position = Vector2.Zero;
    4.     enemyPosition = new Vector2(600f, 200f);
    5.     triangleColor = Color.White;
    6.  
    7.     base.Initialize();
    8. }

    Drawing Including the Color
    1. protected override void Draw(GameTime gameTime)
    2. {
    3.     GraphicsDevice.Clear(Color.Black);
    4.  
    5.     spriteBatch.Begin();
    6.     spriteBatch.Draw(triangle, position, null, triangleColor, rotation, new Vector2(0, triangle.Height / 2), 1f, SpriteEffects.None, 0f);
    7.     spriteBatch.Draw(enemy, enemyPosition, Color.White);
    8.     spriteBatch.End();
    9.  
    10.     base.Draw(gameTime);
    11. }

    Now we’re ready for the collision. We’ll take care of it in the Update method. To check for collision, we have a few options. The easiest would be either rectangle intersection or testing a rectangle to see if it contains a point. In this case, I’ll be using the Rectangle.Contains. The Contains method takes a Point, while our position is a Vector2, so we’ll have to do a bit of casting. We’ll also have to construct a rectangle from the position and texture we have in order to find out if it contains the point. The lines that need to be added into the Update method are as follows:

    Checking Collision by Contains
    1. if (new Rectangle((int)enemyPosition.X, (int)enemyPosition.Y, enemy.Width, enemy.Height).Contains((int)position.X, (int)position.Y))
    2. {
    3.     triangleColor = Color.Red;
    4. }

    And now, when we run the application and run into the enemy, we get the following:

    image

    Next week, we’ll break everything out into classes, and maybe even add some AI to the enemies.

    Download the latest version of the source code,

Page 1 of 1 (3 items)