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.