State management

Think about it. If we want our game to pause, then we have to set some kind of flag that tells the game that we want it to take a break. We could set up a Boolean:

bool m_isPaused;

We would set m_isPaused to true if the game is paused, and set it to false if the game is running.

The problem with this approach is that there are a lot of special cases that we may run into in a real game. At any time the game might be:

  • Starting
  • Ending
  • Running
  • Paused

These are just some example of game states. A game state is a particular mode that requires special handling. As there can be so many states, we usually create a state manager to keep track of the state we are currently in.

Creating a state manager

The simplest version of a state manager begins with an enum that defines all of the game states. Open RoboRacer.cpp and add the following code just under the include statements:

enum GameState
{
  GS_Running,
  GS_Paused
};

Then go to the variable declarations block and add the following line:

GameState m_gameState;

To keep things simple, we are going to define two states: running and paused. A larger game will have many more states.

Enums have a big advantage over Boolean variables. First, their purpose is generally clearer. Saying that the game state is GS_Paused or GS_Running is clearer than if we just had set a Boolean to true or false.

The other advantage is that enums can have more than two values. If we need to add another state to our game, it is as simple as adding another value to our GameState enum list.

Our game will start in the running state, so add the following line of code to the StartGame function:

m_gameState = GS_Running;

Pausing the game

Think about it for a minute. What do we want to do when the game is paused? We still want to see things on the screen, so that means that we still want to make all of our Render calls. However, we don't want things to change position or animate. We also don't want to process game input, though we do need to handle UI input.

All of this should have you thinking about the update calls. We want to block updates to everything except the UI. Modify the Update function in RoboRacer.cpp so that it contains the following code:

void Update(const float p_deltaTime)
{
  inputManager->Update(p_deltaTime);
  ProcessInput(p_deltaTime);
  
  if (m_gameState == GS_Running)
  {
    background->Update(p_deltaTime);
    robot_left->Update(p_deltaTime);
    robot_right->Update(p_deltaTime);
    robot_left_strip->Update(p_deltaTime);
    robot_right_strip->Update(p_deltaTime);
    
    pauseButton->Update(p_deltaTime);
    resumeButton->Update(p_deltaTime);
  }
}

Notice that we will only process the sprite updates if the game state is GS_Running.

We are going to get ready to accept mouse input. First, we are going to setup a timer. Add the following code in the variable declarations of RoboRacer2d.cpp:

float uiTimer;
const float UI_THRESHOLD = 0.2f;

Then add the line of code below to StartGame:

 uiTimer = 0.0f;

The time will be used to add a small delay to mouse input. Without the delay, each click on the mouse would be registered several times instead of a single time.

We still need to handle input, but not all input. Go to the ProcessInput function in RoboRacer.cpp and make the following changes:

void ProcessInput(const float p_deltaTime)
{
 Input::Command command = inputManager->GetCommand();
 if (m_gameState == GS_Paused) command = Input::Command::CM_UI;

 uiTimer += p_deltaTime;
 if (uiTimer > UI_THRESHOLD)
 {
  uiTimer = 0.0f;
  switch (command)
  {
  case Input::Command::CM_STOP:
   player->SetVelocity(0.0f);
   background->SetVelocity(0.0f);
   break;

  case Input::Command::CM_LEFT:
   if (player == robot_right)
   {
    robot_right->IsActive(false);
    robot_right->IsVisible(false);
    robot_left->SetPosition(robot_right->GetPosition());
   }

   player = robot_left;
   player->IsActive(true);
   player->IsVisible(true);
   player->SetVelocity(-50.0f);
   background->SetVelocity(50.0f);
   break;

  case Input::Command::CM_RIGHT:
   if (player == robot_left)
   {
    robot_left->IsActive(false);
    robot_left->IsVisible(false);
    robot_right->SetPosition(robot_left->GetPosition());
   }

   player = robot_right;
   player->IsActive(true);
   player->IsVisible(true);
   player->SetVelocity(50.0f);
   background->SetVelocity(-50.0f);
   break;

  case Input::Command::CM_UP:
   player->Jump(Sprite::SpriteState::UP);
   break;

  case Input::Command::CM_DOWN:
   player->Jump(Sprite::SpriteState::DOWN);
   break;

  case Input::Command::CM_QUIT:
   PostQuitMessage(0);
   break;

  case Input::Command::CM_UI:
   if (pauseButton->IsClicked())
   {
    pauseButton->IsClicked(false);
    pauseButton->IsVisible(false);
    pauseButton->IsActive(false);

    resumeButton->IsVisible(true);
    resumeButton->IsActive(true);
    m_gameState = GS_Paused;
   }

   if (resumeButton->IsClicked())
   {
    resumeButton->IsClicked(false);
    resumeButton->IsVisible(false);
    resumeButton->IsActive(false);

    pauseButton->IsVisible(true);
    pauseButton->IsActive(true);
    m_gameState = GS_Running;
   }
  }
 }
  command = Input::Command::CM_INVALID;
}

Take a look at the second line. It sets the command to CM_UI if the game is paused. This means that only UI commands will be processed while the game is paused. A hack? Perhaps, but it gets the job done!

We only have two more changes to make. When the pause button is clicked, we need to change the game state to GS_Paused, and when the resume button is clicked, we need to change the game state to GS_Running. Those changes have already been made in the CS_UI case in the preceding code!

When you run the program now, you will see that the game pauses when you click the pause button. When you click the resume button, everything picks up again.

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

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