Making the bullets fly

We will make the bullets usable with the following six steps:

  1. Add the necessary include directive for the Bullet class.
  2. Add some control variables and an array to hold some Bullet instances.
  3. Handle the player pressing R to reload.
  4. Handle the player pressing the left mouse button to fire a bullet.
  5. Update all bullets that are in flight, in each frame.
  6. Draw the bullets that are in flight, in each frame.

Including the Bullet class

Add the include directive to make the Bullet class available:

#include "stdafx.h" 
#include <SFML/Graphics.hpp> 
#include "ZombieArena.h" 
#include "Player.h" 
#include "TextureHolder.h" 
#include "Bullet.h"
using namespace sf;

Let's move on to the next step.

Control variables and the bullet array

Here are some variables to keep track of bullets, clip sizes, bullets spare/remaining, bullets in the clip, the current rate of fire (starting at one per second), and the time when the last bullet was fired.

Add the highlighted code and we can move on to seeing all these variables in action in the rest of this section:

// Prepare for a horde of zombies 
int numZombies; 
int numZombiesAlive; 
Zombie* zombies = NULL; 
 
// 100 bullets should do
Bullet bullets[100];
int currentBullet = 0;
int bulletsSpare = 24;
int bulletsInClip = 6;
int clipSize = 6;
float fireRate = 1;
// When was the fire button last pressed?
Time lastPressed; 
 
// The main game loop 
while (window.isOpen())

Next, let's handle what happens when the player presses the R keyboard key, which is used for reloading a clip.

Reloading the gun

Now we handle the player input related to shooting bullets. First we will handle pressing the R key to reload the gun. We do so with an SFML event.

Add the code shown highlighted in the following code block. It is shown with lots of context to make sure the code goes in the right place. Study the code and then we can talk about it:

// Handle events 
Event event; 
while (window.pollEvent(event)) 
{ 
   if (event.type == Event::KeyPressed) 
   { 
      // Pause a game while playing 
      if (event.key.code == Keyboard::Return && 
         state == State::PLAYING) 
      { 
         state = State::PAUSED; 
      } 
 
      // Restart while paused 
      else if (event.key.code == Keyboard::Return && 
         state == State::PAUSED) 
      { 
         state = State::PLAYING; 
         // Reset the clock so there isn't a frame jump 
         clock.restart(); 
      } 
 
      // Start a new game while in GAME_OVER state 
      else if (event.key.code == Keyboard::Return && 
         state == State::GAME_OVER) 
      { 
         state = State::LEVELING_UP; 
      } 
 
      if (state == State::PLAYING) 
      { 
        // Reloading
        if (event.key.code == Keyboard::R)
        {
           if (bulletsSpare >= clipSize)
           {
             // Plenty of bullets. Reload.
             bulletsInClip = clipSize;
             bulletsSpare -= clipSize;
           }
           else if (bulletsSpare > 0)
           {
             // Only few bullets left
             bulletsInClip = bulletsSpare;
             bulletsSpare = 0;
           }
           else
           {
             // More here soon?!
           }
       } 
      } 
 
   } 
}// End event polling

The previous code is nested within the event handling part of the game loop (while(window.pollEvent)), within the block that only executes when the game is actually being played (if(state == State::Playing)). It is obvious that we don't want the player reloading when the game has finished or is paused and wrapping the new code as described achieves this.

In the new code itself, the first thing we do is test for the R key being pressed, with if (event.key.code == Keyboard::R). Once we have detected that the R key was pressed the remaining code is executed. Here is the structure of the ifelse if, and else blocks:

if(bulletsSpare >= clipSize) 
   ... 
else if(bulletsSpare > 0) 
   ... 
else 
   ...

The previous structure allows us to handle three possible scenarios.

  • The player has pressed R and they have more bullets spare than the clip can take. In this scenario, the clip is refilled and the number of spare bullets is reduced.
  • The player has some spare bullets but not enough to fill the clip completely. In this scenario, the clip is filled with as many spare bullets as the player has and the number of spare bullets is set to zero.
  • The player has pressed R but they have no spare bullets at all. For this scenario, we don't actually need to alter the variables. However, we will play a sound effect here when we implement our sound in Chapter 11: Sound Effects, File I/O, and Finishing the Game,so we leave the empty else block ready.

Let's actually shoot a bullet at last.

Shooting a bullet

Next we can handle the left mouse button being clicked to actually fire a bullet. Add the highlighted code and study it carefully:

   if (Keyboard::isKeyPressed(Keyboard::D)) 
   { 
      player.moveRight(); 
   } 
   else 
   { 
      player.stopRight(); 
   } 
 
   // Fire a bullet
   if (Mouse::isButtonPressed(sf::Mouse::Left))
   {
     if (gameTimeTotal.asMilliseconds()
        - lastPressed.asMilliseconds()
        > 1000 / fireRate && bulletsInClip > 0)
     {

        // Pass the center of the player
        // and the center of the crosshair
        // to the shoot function
        bullets[currentBullet].shoot(
           player.getCenter().x, player.getCenter().y,
           mouseWorldPosition.x, mouseWorldPosition.y);

        currentBullet++;
        if (currentBullet > 99)
        {
           currentBullet = 0;
        }
        lastPressed = gameTimeTotal;
        bulletsInClip--;
     }
   }// End fire a bullet
}// End WASD while playing

All of the previous code is wrapped in an if statement, which executes whenever the left mouse button is pressed, if (Mouse::isButtonPressed(sf::Mouse::Left)). Note that the code will execute repeatedly, even if the player just holds down the button. The code we will go through now controls the rate of fire.

In the previous code, we then check whether the total time elapsed in the game (gameTimeTotal), minus the time the player last shot a bullet (lastPressed), is greater than 1000 divided by the current rate of fire, and that the player has at least one bullet in the clip. We use 1000 because this is the number of milliseconds in a second.

If this test is successful, the code that actually fires a bullet is executed. Shooting a bullet is easy because we did all the hard work in the Bullet class. We simply call shoot on the current bullet from the bullets array. We pass in the player's and crosshair's current horizontal and vertical locations. The bullet will be configured and set in flight by the code in the shoot function of the Bullet class.

All we have to do is keep track of the array of bullets. First we increment the currentBullet variable. Then we check to see whether  we fired the last bullet (99) with the statement if (currentBullet > 99). If it was the last bullet, we set currentBullet to zero. If it wasn't the last bullet, then the next bullet is ready to go whenever the rate of fire permits it and the player presses the left mouse button.

Finally, for the previous code, we store the time that the bullet was fired in lastPressed and decrement bulletsInClip.

Now we can update every bullet, each frame.

Updating the bullets each frame

Add the highlighted code to loop through the bullets array, check whether the bullet is in flight, and if it is, call its update function:

   // Loop through each Zombie and update them 
   for (int i = 0; i < numZombies; i++) 
   { 
      if (zombies[i].isAlive()) 
      { 
         zombies[i].update(dt.asSeconds(), playerPosition); 
      } 
   } 
 
   // Update any bullets that are in-flight
   for (int i = 0; i < 100; i++)
   {
     if (bullets[i].isInFlight())
     {
        bullets[i].update(dtAsSeconds);
     }
   }
}// End updating the scene

And lastly, we can draw all the bullets.

Drawing the bullets each frame

Add the highlighted code to loop through the bullets array, check whether the bullet is in flight and if it is, draw it:

/* 
 ************** 
 Draw the scene 
 ************** 
 */ 
 
if (state == State::PLAYING) 
{ 
   window.clear(); 
 
   // set the mainView to be displayed in the window 
   // And draw everything related to it 
   window.setView(mainView); 
 
   // Draw the background 
   window.draw(background, &textureBackground); 
 
   // Draw the zombies 
   for (int i = 0; i < numZombies; i++) 
   { 
      window.draw(zombies[i].getSprite()); 
   } 
 
   for (int i = 0; i < 100; i++)
   {
     if (bullets[i].isInFlight())
     {
        window.draw(bullets[i].getShape());
     }
   } 
 
   // Draw the player 
   window.draw(player.getSprite()); 
}

Run the game to try out the bullets. Notice you can fire six shots before you need to press R to reload. The obvious things that are missing are some visual indicator of the number of bullets in the clip and the number of spare bullets. Another problem is that the player can very quickly run out of bullets, especially as the bullets have no stopping power whatsoever. They fly straight through the zombies. Add to this that the player is expected to aim at a mouse pointer instead of a precision crosshair, and it is plain we have work to do.

In the next chapter, we will give visual feedback through a HUD. We will replace the mouse cursor with a crosshair next and then spawn some pickups to replenish bullets and health after that. Finally, in this chapter, we will handle collision detection to make the bullets and the zombies do damage and make the player able to actually get the pickups.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.40.129