Posts
  • File → New Project

    Building a Language Learning Game (QuizGame)

    • 1 Comments

    This week we’ll be starting a new game for the Windows Phone. We’ll be using Silverlight this time around, and building a language learning game. The idea behind the game is to make use of a database of words and meanings, pick a word, and give four possible definitions. I actually originally built the first version of this game in XNA, and when I redid it in Silverlight, the implementation ended up being much easier. This week, we’ll start with a new Silverlight project, and walk through what that gives you. We’ll also look at a source of graphics we’ll be using for the game.

    To begin with, load up Visual Studio 2010 Express for Windows Phone, and choose File –> New Project, and under the Silverlight for Windows Phone templates, choose Windows Phone Application. In the Name field, type QuizGame, then click OK. You’ll get a new solution containing a single project with a few files. The basic structure a Silverlight application is a set of XAML files, containing markup that defines the user interface, and .cs files with code behind. In the MainPage.xaml file, you’ll get a visual desginer with a few elements in it. The layout is done through a Grid element containing a StackPanel with two TextBlocks. When you run the application, the emulator will pop up and display the application, looking something like the following:

    QuizGame

    The numbers on the right of the page is a frame rate counter. You can turn it on or off using the following line in the App.xaml.cs file’s constructor.

    Enabling the Frame Rate
    1. Application.Current.Host.Settings.EnableFrameRateCounter = true;

    The other important files in the project are the ApplicationIcon.png file, the icon that will appear in the list of apps on the phone, Background.png, the image to display when your application is "pinned" to the Start screen, and SplashScreenImage.jpg, the splash screen that will be displayed when you launch the application.

    We’ll be getting deeper into the XAML next week when we lay out the user interface. This week, we’ll finish off by looking at the source of images we’ll be using. There’s a great resource over at http://www.lostgarden.com/ where an artist occasionally posts sets of graphics like the one we will be using. The post in question is Danc's Miraculously Flexible Game Prototyping Tiles.

    Here’s an example of the type of graphics we’ll be using.

    Character Cat GirlCharacter Princess GirlSpeechBubble

    StarGem GreenGem OrangeHeart

    See you next week, when we’ll set up the game interface.

  • 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

    Spawning More Enemies (TriangleShooter)

    • 0 Comments

    Time for my favorite part of Monday – Another article improving my TriangleShooter game! Last week, I promised you that I would spawn some more enemies, and that I would add in some other enemy types to make it more visually appealing. The great news is, both of those things are pretty easy. We’ll get started with the code base from last week, so you can grab that from my SkyDrive share.

    We can start by adding in some additional enemy types. To begin with, we’ll drag and drop the rest of the Enemies from the Enemies file from SkyDrive into the Content project with the first enemy. We’ll then create some variables to hold the textures.

    Textures for Enemy
    1. Texture2D txEnemy3;
    2. Texture2D txEnemy4;
    3. Texture2D txEnemy5;
    4. Texture2D txEnemy6;

    We’ll then load them from the content project in the LoadContent method.

    Loading the Enemy Textures
    1. txEnemy3 = Content.Load<Texture2D>("Enemy-3");
    2. txEnemy4 = Content.Load<Texture2D>("Enemy-4");
    3. txEnemy5 = Content.Load<Texture2D>("Enemy-5");
    4. txEnemy6 = Content.Load<Texture2D>("Enemy-6");

    We can try this out by changing the lines further down in LoadContent to make use of the new textures.

    Creating Diverse Enemies
    1. enemies.Add(new Enemy() { Avatar = txEnemy3, Position = new Vector2(600f, 200f), Rotation = 0f, Speed = 3f });
    2. enemies.Add(new Enemy() { Avatar = txEnemy4, Position = new Vector2(600f, 300f), Rotation = 0f, Speed = 3.5f });
    3. enemies.Add(new Enemy() { Avatar = txEnemy5, Position = new Vector2(600f, 400f), Rotation = 0f, Speed = 4f });

    image

    Next up, we need to start spawning some more enemies. What we’re looking to do here is to bring in new enemies every so often. The faster you spawn the enemies, the more difficult the game would be, so you can play with the amount of time between spawns. In order to track them, though, we’ll need a variable to track how long we have left until the next spawn. Since we’ve got some different enemy types, we may as well add in some randomization, so we’ll create a variable to handle randomness as well. As always, we’ll drop the variable up at the top of the game file.

    Spawn Timer and Randomization
    1. TimeSpan timeToSpawn;
    2.  
    3. Random random;

    And then we initialize them down in Initalize()

    Initializing Spawn and Random
    1. timeToSpawn = new TimeSpan(0, 0, 0, 0, 1000);
    2.  
    3. random = new Random();

    This sets up timeToSpawn as one second. We’ll count down until this reaches zero, then spawn another enemy. Luckily, there’s a easy way to get elapsed game time in the Update Method with the gameTime argument. We’ll subtract the elapsed game time from the timeToSpawn variable, then check to see if it is less than or equal to zero. If it is, we create a new enemy and add it to the enemies list. To add variety, we can pick a random edge, and spawn the enemy somewhere along it so that it doesn’t just always come from the same spot. Finally, we’ll reset the timeToSpawn so that we can spawn another. All of this will go in the Update method after we check the enemy collision.

    Spawning More Enemies
    1. timeToSpawn -= gameTime.ElapsedGameTime;
    2.  
    3. if (timeToSpawn < TimeSpan.Zero)
    4. {
    5.     Vector2 spawnPosition;
    6.     switch (random.Next(4))
    7.     {
    8.         case 0:
    9.             spawnPosition = new Vector2(0, random.Next(480));
    10.             break;
    11.         case 1:
    12.             spawnPosition = new Vector2(800, random.Next(480));
    13.             break;
    14.         case 2:
    15.             spawnPosition = new Vector2(random.Next(800), 0);
    16.             break;
    17.         case 3:
    18.             spawnPosition = new Vector2(random.Next(800), 480);
    19.             break;
    20.         default:
    21.             spawnPosition = Vector2.Zero;
    22.             break;
    23.     }
    24.  
    25.     switch (random.Next(4))
    26.     {
    27.         case 0:
    28.             enemies.Add(new Enemy() { Position = spawnPosition, Rotation = 0f, Avatar = txEnemy3, Speed = 3f });
    29.             break;
    30.         case 1:
    31.             enemies.Add(new Enemy() { Position = spawnPosition, Rotation = 0f, Avatar = txEnemy4, Speed = 3.5f });
    32.             break;
    33.         case 2:
    34.             enemies.Add(new Enemy() { Position = spawnPosition, Rotation = 0f, Avatar = txEnemy5, Speed = 4f });
    35.             break;
    36.         case 3:
    37.             enemies.Add(new Enemy() { Position = spawnPosition, Rotation = 0f, Avatar = txEnemy6, Speed = 4.5f });
    38.             break;
    39.         default:
    40.             break;
    41.     }
    42.  
    43.     timeToSpawn = new TimeSpan(0, 0, 0, 0, random.Next(500) + 250);
    44. }

    image

    There’s just so many of them!

    Next week, we’ll add in shooting! Until this point, we were just a triangle moving around, but now we’ll finally be a triangle that shoots!

    Download the latest version of the source code.

  • File → New Project

    Adding Shooting (TriangleShooter)

    • 0 Comments

    Let’s make TriangleShooter a better game. This time around, we’ll be putting the shooter in TriangleShooter, by adding in the ability for the player to shoot. We’ll be starting with last week’s code, so you can go over and grab that from my SkyDrive share.

    I tried a few different ways of making the player shoot in my development of the game, and the best one I have found so far is to have you always laying out a steady stream of shots. Towards the end of the series, I’ll be doing some play mechanics tweaking, and we can investigate some of the other ways, but that’s how I’m going to do things in this article.

    If you’re going to shoot, you need a bullet. You can get my highly stylized, square bullet from the SkyDrive share. To begin with, download that, then add it into the TriangleShooterContent project by dragging it and dropping it in there. We’ll also need a class to handle the bullet, which is going to look familiar. Create a new class in the TriangleShooter project named Bullet.cs, and set the content to the following:

    Bullet.cs
    1. using Microsoft.Xna.Framework;
    2. using Microsoft.Xna.Framework.Graphics;
    3.  
    4. namespace TriangleShooter
    5. {
    6.     class Bullet
    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. }

    We’ll set up the variables at the top of the game class to hold the texture, the list of Bullets, and to monitor the passing time to shoot, along with a constant to tweak the timer a bit.

    Variables for Bullets
    1. const int ShootDelay = 250;
    2. TimeSpan timeToShoot;
    3.  
    4. List<Bullet> bullets;
    5. Texture2D txBullet;

    In the Initialize method, we set up the list for the bullets, and set the time to shoot from the constant value we set up.

    Initialize
    1. bullets = new List<Bullet>();
    2.  
    3. timeToShoot = new TimeSpan(0, 0, 0, 0, ShootDelay);

    And add some code into the LoadContent method to load in the texture.

    LoadContent
    1. txBullet = Content.Load<Texture2D>("Bullet");

    And now we’ve got everything we need. We only have the Update and Draw to go. The Update is more interesting, so let’s start there.

    Update
    1. timeToShoot -= gameTime.ElapsedGameTime;
    2.  
    3. if (timeToShoot <= TimeSpan.Zero)
    4. {
    5.     timeToShoot = new TimeSpan(0, 0, 0, 0, ShootDelay);
    6.     bullets.Add(new Bullet() { Avatar = txBullet, Position = new Vector2(player.Position.X + player.Avatar.Width * (float)Math.Cos(player.Rotation), player.Position.Y + player.Avatar.Height * (float)Math.Sin(player.Rotation)), Rotation = player.Rotation, Speed = 15f });
    7.  
    8. }
    9.  
    10. foreach (Bullet b in bullets.ToList())
    11. {
    12.     b.Position = new Vector2(b.Position.X + b.Speed * (float)Math.Cos(b.Rotation), b.Position.Y + b.Speed * (float)Math.Sin(b.Rotation));
    13.  
    14.     if (!graphics.GraphicsDevice.Viewport.Bounds.Contains(new Point((int)b.Position.X, (int)b.Position.Y)))
    15.     {
    16.         bullets.Remove(b);
    17.     }
    18.     else
    19.     {
    20.         foreach (Enemy enemy in enemies.ToList())
    21.         {
    22.             if (new Rectangle((int)enemy.Position.X - enemy.Avatar.Width / 2, (int)enemy.Position.Y - enemy.Avatar.Height / 2, enemy.Avatar.Width, enemy.Avatar.Height).Contains((int)b.Position.X, (int)b.Position.Y))
    23.             {
    24.                 bullets.Remove(b);
    25.                 enemies.Remove(enemy);
    26.                 break;
    27.             }
    28.         }
    29.     }
    30. }

    The first part of the code is similar to what we do to spawn enemies. We subtract the elapsed time from the remaining time, and if the time is less than zero, we spawn a new bullet, setting it’s position to the front of the player and the rotation to the players rotation. We then update all existing bullets. Because we may need to remove them from the master list, we make a copy in the foreach by using the .ToList() function. We move them in the direction based on their rotation, and see if they have left the bounds of the screen. If they have, we remove them from the update list. To optimize things here, we should do something like create a list of decommissioned bullets, and reuse them rather than creating new ones each time, but we won’t do that this time around. If they are within the bounds of the screen, we check collision with each of the enemies, again using the .ToList() function because we might need to remove items. If they collide, we remove both the bullet, and the enemy that collided with it.

    This is all we need to do, but we do need to draw the bullets. Luckily, this is a simple process, just add a few lines into Draw.

    Draw
    1. foreach (Bullet b in bullets)
    2. {
    3.     spriteBatch.Draw(b.Avatar, b.Position, null, Color.White, b.Rotation, new Vector2(b.Avatar.Width / 2, b.Avatar.Height / 2), 1f, SpriteEffects.None, 0f);
    4. }

    And now we have shooting!

    image

    Next week, we’ll do some refactoring to make the code easier to work with, and see if we can add in some death behavior.

    Download the latest version of the source code.

  • File → New Project

    Refactoring and Death Behavior (TriangleShooter)

    • 0 Comments

    This time around, we’ll be doing a bit of refactoring to make the code easier to deal with, and adding in some death behavior. To begin with, we’ll need to start with the code from last week, so go ahead and pick that up from the SkyDrive share.

    To begin with, let’s update the Initialize and LoadContent methods, and move some of the setup into a new method called NewGame. This will allow us to reset the game back to it’s initial state more easily, such as when you get killed by running into an enemy.

    Initialize
    1. protected override void Initialize()
    2. {
    3.     random = new Random();
    4.  
    5.     base.Initialize();
    6. }

    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.     txBullet = Content.Load<Texture2D>("Bullet");
    8.     txEnemy3 = Content.Load<Texture2D>("Enemy-3");
    9.     txEnemy4 = Content.Load<Texture2D>("Enemy-4");
    10.     txEnemy5 = Content.Load<Texture2D>("Enemy-5");
    11.     txEnemy6 = Content.Load<Texture2D>("Enemy-6");
    12.  
    13.     NewGame();
    14. }

    NewGame
    1. private void NewGame()
    2. {
    3.     triangleColor = Color.White;
    4.  
    5.     timeToSpawn = new TimeSpan(0, 0, 0, 0, 1000);
    6.     timeToShoot = new TimeSpan(0, 0, 0, 0, ShootDelay);
    7.  
    8.     player = new Player() { Avatar = txPlayer, Position = Vector2.Zero, Rotation = 0f };
    9.     bullets = new List<Bullet>();
    10.  
    11.     enemies = new List<Enemy>();
    12.     enemies.Add(new Enemy() { Avatar = txEnemy3, Position = new Vector2(600f, 200f), Rotation = 0f, Speed = 3f });
    13.     enemies.Add(new Enemy() { Avatar = txEnemy4, Position = new Vector2(600f, 300f), Rotation = 0f, Speed = 3.5f });
    14.     enemies.Add(new Enemy() { Avatar = txEnemy5, Position = new Vector2(600f, 400f), Rotation = 0f, Speed = 4f });
    15. }

    If we update the collision detection, we can call NewGame now if the collision happens, which means we can reset the game with ease. Also, while I was setting this up, I found a bug in the code based around the origin of the drawing and the rectangle we were using for collision. The updated code in the Update method looks like the following:

    Updated Collision Detection
    1. if (new Rectangle((int)enemy.Position.X - enemy.Avatar.Width / 2, (int)enemy.Position.Y - enemy.Avatar.Height / 2, enemy.Avatar.Width, enemy.Avatar.Height).Contains((int)player.Position.X, (int)player.Position.Y))
    2. {
    3.     triangleColor = Color.Red;
    4.     NewGame();
    5. }

    Now, when you die, the game resets. It might make more sense to freeze the game, and wait for the player to choose to continue before resetting the game. To do that, let’s create a new variable to say whether the player is alive or not. Let’s call it isPlayerDead

    isPlayerDead
    1. bool isPlayerDead;

    And initialize it to false in the NewGame method

    Initializing isPlayerDead
    1. isPlayerDead = false;

    Then check if the player is dead in the Update method

    Update
    1. if (isPlayerDead)
    2. {
    3.     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
    4.     {
    5.         NewGame();
    6.     }
    7. }
    8. else
    9. {
    10.     // Everything else in Update
    11. }

    This means that the Collision detection from above needs to be updated once more to set the boolean rather than calling NewGame directly.

    Collision Detection
    1. if (new Rectangle((int)enemy.Position.X - enemy.Avatar.Width / 2, (int)enemy.Position.Y - enemy.Avatar.Height / 2, enemy.Avatar.Width, enemy.Avatar.Height).Contains((int)player.Position.X, (int)player.Position.Y))
    2. {
    3.     triangleColor = Color.Red;
    4.     isPlayerDead = true;
    5. }

    And we’ve now got a game that actually ends when you die, and let’s you start a new game. Sounds like we’re ready for scoring. We’ll add that in next week. See you then.

    Download the latest version of the source code.

Page 3 of 8 (37 items) 12345»