Posts
  • 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

    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.

Page 8 of 8 (37 items) «45678