XNA GameStudio 2.0
Firing Bullets

When we create the effect of bullets being fired up the screen, we have to think about quite a lot. Not only do we have to load the content and draw the bullets on the screen, we also have to keep track of wherever they need to be drawn throughout the entire journey of the bullet from firing to destruction.

Stage 1 - Fields

In order to make it so that the player has to press the key each time they want to fire a bullet, we need to keep track of the previous keyboard state. This means that we only fire a bullet if the CTRL key is down having previously been up.

We create the bullets as an array of GameObjects and use a constant (that we can adjust later) to determine the maximum number of bullets that can be on the screen at any one time.

Add the following fields to the main game class.

KeyboardState previousKBState;
GameObject[] bullets;
const int maxBullets = 5;

Stage 2 - Load Content & Set Starting Values

The following statements need to be added to the LoadContent() method of the main game class. We use a for loop to set up all of our bullets in one go.

bullets = new GameObject[maxBullets];
for (int i = 0; i < maxBullets; i++)
{
bullets[i] = new GameObject(Content.Load<Texture2D>("Sprites\\cannonball"));
}

Stage 3 - Handle KeyBoard Input

We add the code for processing the keyboard input to the HandleInput() method that we wrote to control the movement.

if (kbState.IsKeyDown(Keys.LeftControl) && previousKBState.IsKeyUp(Keys.LeftControl))
{
   FireBullet();
}
previousKBState = kbState;

We haven't written the FireBullet() method yet. Running the project at this point would generate an error.

At the end of each game loop, we set the previous keyboard state value to the one we found in that game loop - that way it is ready to mean something on the next game loop.

Stage 4 - Firing The Bullets

To fire the bullets we first need to check that we have some left that aren't already on the screen. The alive property is what we want to look at. We loop through the array of bullets until we find one that isn't already on the screen. By changing its alive property to true, we get it ready to be drawn on the screen. There is a bit of kerfuffle in deciding where to start the bullet from so it looks like it emerges from the ship. If you need to change the velocity of the bullet, this is the place to do so.

private void FireBullet()
{
   foreach (GameObject b in bullets)
   {
      if (!b.alive)
      {
         b.alive = true;
         b.position.Y = ship.position.Y;
         b.position.X = ship.position.X + (ship.sprite.Width / 2) - (b.sprite.Width / 2);
         b.velocity = new Vector2(0,-5.0f);
         return;
      }
   }
}

Stage 5 - Updating The Bullets

We have to make sure that the bullets move up the screen. To do this, we will need another method. The UpdateBullets() method is shown below.

private void UpdateBullets()
{
   foreach (GameObject b in bullets)
   {
      if (b.alive)
      {
         b.position += b.velocity;
         Rectangle viewPortRect = new Rectangle(0, 0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);
         if (!viewPortRect.Contains(new Point((int)b.position.X, (int)b.position.Y)))
         {
            b.alive = false;
         }
      }
   }
}

The lines that refer to the Viewport are to make sure that the bullet remains on screen. Later we will have to add further code to check for a collision between the bullets and the enemy ships.

To make sure that this code is executed during the game loop, add the following statement to the Update() method.

UpdateBullets()

Stage 6 - Draw The Bullets

The last job for us is to make sure that any bullets that are alive are drawn on the screen in their correct positions. We do this, as you would expect, in the Draw() method.

foreach (GameObject b in bullets)
{
   if (b.alive)
   {
      spriteBatch.Draw(b.sprite, b.position, Color.White);
   }
}

At this point you need to read back through the work you have done so far and make sure that things are working as you would expect them to. It is also a good time to make adjustments to the speed of movement.

You should be able to work out from the code you have added so far and from the First Game instructions how to add a decent background - that might be worth doing now.