Posts
  • File → New Project

    Adding Multiple Enemies with AI (TriangleShooter)

    • 1 Comments

    Another week, another article bringing us one step further in the creation of TriangleShooter. Last week, we did a bit of refactoring and broke Player and Enemy out into classes. At the time, this didn’t change anything in how the application worked, but this week, we’ll take advantage of it by adding in some basic AI, and then showing how easily you can scale out to more enemies. We’re going to be starting with the project we created worked on last week, so you can grab that from the SkyDrive Share.

    The first thing we can do is make the enemy seem a bit more impressive. He was just sitting out there while we flew all around him just making fun of his inability to move. Well, let’s change that. We’ll begin by some basic animation. We’re not going into sprite frames or anything like that, a rotation should do. In the update method, we’ll add a line to update the enemy’s rotation. Let’s drop it in right before the collision detection. Rotation is handled as a float, in radians. That means that when the rotation is equal to 2*pi, it will be a single rotation. Since rotation is cyclical, and bounded between 0 and 2*pi, we can make use of the modulus operator to make sure that it doesn’t eventually exceed the boundary of what a float can handle.

    Rotating the Enemy
    1. enemy.Rotation += ((float)Math.PI / 30f) % ((float)Math.PI * 2);

    We’ll need to update the Draw function as well, to make use of the newly updated rotation. We’ll use the same format as we did for the player. The main difference will be that the origin of rotation for the enemy will be the center.

    Drawing the Rotated Enemy
    1. spriteBatch.Draw(enemy.Avatar, enemy.Position, null, Color.White, enemy.Rotation, new Vector2(enemy.Avatar.Width, enemy.Avatar.Height) / 2, 1f, SpriteEffects.None, 0f);

    If you run the application now, it works much like it did at the end of the last article, but now the enemy rotates. That simple change makes the visuals of the game quite a bit more interesting.  It also gives us a great view into how easy it is going to be to add in some additional AI.

    image

    Let’s jump right into that. We’ve already seen how easy it is to make the player move towards a touch point. Let’s do the same thing with the enemy, and set the destination to the player. That’s the basis for follow AI. Add the following lines to the Update method right after the line that updates the rotation.

    Follow AI
    1. Vector2 enemyDirection = player.Position - enemy.Position;
    2.  
    3. if (enemyDirection.LengthSquared() > enemy.Speed * enemy.Speed)
    4. {
    5.     enemyDirection.Normalize();
    6.  
    7.     enemy.Position += enemyDirection * enemy.Speed;
    8. }

    That’s it. We run the application, and now the enemy follows you around the screen. Programming is really easy, when you think about it.

    image

    The next step is to add more enemies. This is actually a lot easier than you might think. First, let’s look at how we are currently creating instances of the Enemy class.

    Creating an Enemy
    1. enemy = new Enemy();
    2. enemy.Avatar = txEnemy6;
    3. enemy.Position = new Vector2(600f, 200f);
    4. enemy.Rotation = 0f;

    As you can see, it takes four lines of code, and we aren’t even setting speed at this point. There are a couple of ways to condense this down, including creating a constructor, and using Object Initialization. In this case, let’s go ahead and use Object Initialization, which means that we will use the default constructor, then assign the values directly after in braces. I prefer this method in this case because when I read through the code, it is more explicit. You may prefer to use a constructor, which works just fine by me. Using Object Initialization, the previous code changes to the following:

    Using Object Initialization
    1. enemy = new Enemy() { Avatar = txEnemy6, Position = new Vector2(600f, 200f), Rotation = 0f, Speed =  3f};

    You’ll notice that I added Speed in there while I was at it.

    With this setup, it’s much easier to instantiate a full new Enemy with just a single line of code.

    We had been using a variable called enemy defined throughout the class. To allow for more enemies, we’ll make use of a generic collection called List. Replace the line towards the top of game1 defining enemy

    Single Enemy
    1. Enemy enemy;

    with a List of Enemy

    List of Enemy
    1. List<Enemy> enemies;

    and initialize it in the Initialize method.

    Initializing enemies
    1. enemies = new List<Enemy>();

    in the LoadContent method, replace the line

    enemy initialization
    1. enemy = new Enemy() { Avatar = txEnemy6, Position = new Vector2(600f, 200f), Rotation = 0f, Speed = 3f };

    with the following few lines. This will create three separate enemies, each with a different speed.

    Adding enemies
    1. enemies.Add(new Enemy() { Avatar = txEnemy6, Position = new Vector2(600f, 200f), Rotation = 0f, Speed = 3f });
    2. enemies.Add(new Enemy() { Avatar = txEnemy6, Position = new Vector2(600f, 300f), Rotation = 0f, Speed = 3.5f });
    3. enemies.Add(new Enemy() { Avatar = txEnemy6, Position = new Vector2(600f, 400f), Rotation = 0f, Speed = 4f });

    the Update method needs to add a foreach loop around the area we’re using enemy. It will look like this

    Updating each Enemy
    1. foreach (Enemy enemy in enemies)
    2. {
    3.     enemy.Rotation += ((float)Math.PI / 30f) % ((float)Math.PI * 2);
    4.  
    5.     Vector2 enemyDirection = player.Position - enemy.Position;
    6.  
    7.     if (enemyDirection.LengthSquared() > enemy.Speed * enemy.Speed)
    8.     {
    9.         enemyDirection.Normalize();
    10.  
    11.         enemy.Position += enemyDirection * enemy.Speed;
    12.     }
    13.  
    14.     if (new Rectangle((int)enemy.Position.X, (int)enemy.Position.Y, txEnemy6.Width, txEnemy6.Height).Contains((int)player.Position.X, (int)player.Position.Y))
    15.     {
    16.         triangleColor = Color.Red;
    17.     }
    18. }

    and a foreach around the enemy in Draw

    Drawing each Enemy
    1. foreach (Enemy enemy in enemies)
    2. {
    3.     spriteBatch.Draw(enemy.Avatar, enemy.Position, null, Color.White, enemy.Rotation, new Vector2(enemy.Avatar.Width, enemy.Avatar.Height) / 2, 1f, SpriteEffects.None, 0f);
    4. }

    And again, that’s all. A couple more simple changes, and we’ve added support for as many enemies as we want.

    image

    Next week, we’ll add in some spawning code, and add in the rest of the enemy graphics to add in some variety.

    Download the latest version of the source code.

  • File → New Project

    Breaking the Player and Enemy out into Classes (TriangleShooter)

    • 0 Comments

    This week, I’m going to go over how to break out Player and Enemy into classes, which will make it easier to handle multiple enemies, collision, AI, that sort of thing. We’ll be starting things off with the project from last week, so you can go grab that now.

    If you take a look at how we’ve been doing things, you’ll notice that I have been using only the file game1.cs, which was automatically generated for me. When I added an enemy, in order to track its position, I created a variable called enemyPosition, because I was already using position for my player. If I wanted to add another enemy, I would have to do something like enemyTwoPosition or enemy2Position, and the next enemy after that would have to be enemy3Position, and it would just be a pain to have to keep track of all of those and copy all of the attributes for each new instance. It would also be a pain to have to check every instance for their collision, and to have to draw each one. If I changed something in one, I’d have to change it in all of them, and missing one of them could result in a bug that might not be noticed right away, when it would be easiest to fix. That’s why I’m going to break out the player and enemy into their own classes. I’m not going to go into subclassing, although there are a good number of shared attributes that would make it make sense to do so.

    Let’s take a look at what we have, first.

    Current Player and Enemy
    1. Texture2D triangle;
    2. Vector2 position;
    3. float rotation;
    4. Color triangleColor;
    5.  
    6. Texture2D enemy;
    7. Vector2 enemyPosition;

    We only have a texture and a position for the enemy, because it was static in the last article. It would make sense to add rotation to it, as well as a speed variable that will allow us to have different classes of enemies with different speeds. To begin, we’ll need to create a new class. Right click on the TriangleShooter project in the Solution Explorer, and choose Add –> Class. When it asks for a filename, type in Player.cs. Add another class, and name this one Enemy.cs. The contents of the two files is below:

    Player.cs
    1. using Microsoft.Xna.Framework;
    2. using Microsoft.Xna.Framework.Graphics;
    3.  
    4. namespace TriangleShooter
    5. {
    6.     class Player
    7.     {
    8.         public Texture2D Avatar { get; set; }
    9.         public Vector2 Position { get; set; }
    10.         public float Rotation { get; set; }
    11.         public float Speed { get; set; }
    12.     }
    13. }

    Enemy.cs
    1. using Microsoft.Xna.Framework;
    2. using Microsoft.Xna.Framework.Graphics;
    3.  
    4. namespace TriangleShooter
    5. {
    6.     class Enemy
    7.     {
    8.         public Texture2D Avatar { get; set; }
    9.         public Vector2 Position { get; set; }
    10.         public float Rotation { get; set; }
    11.         public float Speed { get; set; }
    12.     }
    13. }

    Essentially, they are the same thing. That’s why I said that you could do well with subclassing, but we’ll keep it like this. I also added Speed onto Player, which will be useful a bit later in the series when I get into fine-tuning your game to make it more fun to play. To take advantage of the new classes, we’ll make a few changes to the game1.cs file. Namely, we’ll modify the variable declaration, the Initalize method, LoadContent, Update, and Draw. We’re just refactoring here. No changes to how the game plays are made in this modification.

    Variable Declaration
    1. GraphicsDeviceManager graphics;
    2. SpriteBatch spriteBatch;
    3.  
    4. Player player;
    5. Texture2D txPlayer;
    6.  
    7. Enemy enemy;
    8. Texture2D txEnemy6;
    9.  
    10. Color triangleColor;
    11.  
    12. int movementId = 0;

    We move most of the initialization until after LoadContent, since we need to load the textures.

    Initialize
    1. protected override void Initialize()
    2. {
    3.     triangleColor = Color.White;
    4.  
    5.     base.Initialize();
    6. }

    Here is where we set up the Player and Enemy instances.

    LoadContent
    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.     txPlayer = Content.Load<Texture2D>("Triangle");
    7.     txEnemy6 = Content.Load<Texture2D>("Enemy-6");
    8.  
    9.     player = new Player();
    10.     player.Avatar = txPlayer;
    11.     player.Position = Vector2.Zero;
    12.     player.Rotation = 0f;
    13.  
    14.     enemy = new Enemy();
    15.     enemy.Avatar = txEnemy6;
    16.     enemy.Position = new Vector2(600f, 200f);
    17.     enemy.Rotation = 0f;
    18. }

    In the Update method, we switch everything over to use the classes.

    Update
    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.         if (tl.State == TouchLocationState.Pressed)
    10.         {
    11.             if (movementId == 0)
    12.             {
    13.                 movementId = tl.Id;
    14.             }
    15.         }
    16.  
    17.         if (tl.Id == movementId)
    18.         {
    19.             Vector2 direction = tl.Position - player.Position;
    20.  
    21.             if (direction.LengthSquared() > 100)
    22.             {
    23.                 direction.Normalize();
    24.  
    25.                 player.Rotation = (float)(Math.Atan2(direction.Y, direction.X));
    26.  
    27.                 player.Position += direction * 10;
    28.             }
    29.         }
    30.  
    31.         if (tl.State == TouchLocationState.Released)
    32.         {
    33.             if (tl.Id == movementId)
    34.             {
    35.                 movementId = 0;
    36.             }
    37.         }
    38.     }
    39.  
    40.     if (new Rectangle((int)enemy.Position.X, (int)enemy.Position.Y, txEnemy6.Width, txEnemy6.Height).Contains((int)player.Position.X, (int)player.Position.Y))
    41.     {
    42.         triangleColor = Color.Red;
    43.     }
    44.  
    45.     base.Update(gameTime);
    46. }

    And the same with Draw

    Draw
    1. protected override void Draw(GameTime gameTime)
    2. {
    3.     GraphicsDevice.Clear(Color.Black);
    4.  
    5.     spriteBatch.Begin();
    6.     spriteBatch.Draw(player.Avatar, player.Position, null, triangleColor, player.Rotation, new Vector2(0, txPlayer.Height / 2), 1f, SpriteEffects.None, 0f);
    7.     spriteBatch.Draw(enemy.Avatar, enemy.Position, Color.White);
    8.     spriteBatch.End();
    9.  
    10.     base.Draw(gameTime);
    11. }

    This sets us up perfectly to begin adding additional enemies into the game. Next week, I’ll go ahead and do that, and add some basic AI to the enemies.

    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,

  • 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

    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

Page 4 of 8 (37 items) «23456»