Game levels

In the current version of the game, you can steer the rocket, start and stop the additional engine, and move in the game world. In this part, you will create a class used for storing game data, including the level number, the score, and the number of remaining rockets. You will also prepare the logic for loading the level with a given number, as well as increasing the score when the rocket is flying towards the target planet.

Implementation

An implementation requires to add a few constants, create the Game class, as well as make some modifications in the GameRenderer class.

Constants

At the beginning, you add some constant values regarding the game:

#define SA3D_NEW_ROCKET_BONUS 100000
#define SA3D_REACH_PLANET_BONUS_BASE 1000
#define SA3D_ROCKETS_NUMBER 3
#define SA3D_MIN_SPEED 1.0f
#define SA3D_MAX_SPEED 50.0f
#define SA3D_MAX_ASTEROIDS_PER_LAYER 10

They indicate a required number of points for getting a bonus rocket (SA3D_NEW_ROCKET_BONUS), a number of bonus points for reaching the planet (SA3D_REACH_PLANET_BONUS_BASE, it will be multiplied by the level number), a default number of rockets (SA3D_ROCKETS_NUMBER), a minimum and maximum rocket speed (SA3D_MIN_SPEED and SA3D_MAX_SPEED), as well as a maximum number of asteroids with the same z coordinate, that is also referred as a maximum number of asteroids per layer.

Game class

The Game class is the main place where data related to the game state is stored. It also contains a few methods that are called, for example, when the rocket reaches the planet or crashes. The definition is as follows:

class Game
{
public:
  Game();
  int GetLevel() { return m_level; }
  int GetScore() { return (int)m_score; }
  int GetRocketsNumber() { return m_rocketsNumber; }
  int GetDistanceToPlanet() { return 100 + m_level * 10; }
  float GetSpeedFactor() { return min(SA3D_MIN_SPEED +
    0.5f * m_level, SA3D_MAX_SPEED); }
  int GetAsteroidsPerLayer() { return min(max(m_level / 4, 1),
    SA3D_MAX_ASTEROIDS_PER_LAYER); }
  void IncreaseScore(float distance);
  void ReachPlanet();
  void CrashWithAsteroid();
  void Restart();
  void AddBonusRocket(float newPoints);
private:
  int m_level;
  float m_score;
  int m_rocketsNumber;
};

At the beginning, you should not forget to include the Constants.h file. The Game class contains a few methods that return values of private fields, representing the number of level (GetLevel), the score (GetScore), and the number of remaining rockets (GetRocketsNumber). Other methods perform calculations that are related to the current level and are used to prepare the game world. For instance, the GetDistanceToPlanet method returns a value indicating how far is the target planet from the rocket. Of course, with consecutive levels, this distance increases. Another example is the GetSpeedFactor method, which indicates a speed factor that is used to increase the difficulty during higher levels. One of the other methods (GetAsteroidsPerLayer) returns a number of asteroids that have the same z coordinate.

The following graphs indicate how various properties of a level (namely, the number of asteroids per layer, the speed factor, and the distance to the planet) change while increasing the level number:

Game class

The IncreaseScore method is called while the rocket is flying towards the planet and increases the score by a value depending on the distance covered. The ReachPlanet and CrashWithAsteroid methods are called when the rocket reaches the target planet or when it crashes. The Restart method just resets game data to defaults, while AddBonusRocket increases the number of rockets by the bonus one, if applicable.

The game data are stored in three private fields, representing the number of current level (m_level), the score (m_score), and the number of rockets (m_rocketsNumber).

An implementation of the methods from the Game class is prepared in the Game.cpp file. In case of the constructor, just the Restart method is called. As mentioned earlier, it resets game data to default values. Thus, you can start the game from the first level. The code of the constructor is as follows:

Game::Game() { Restart(); }

The IncreaseScore method is called when the rocket flies for some distance. It should increase the score according to the formula score = score + distance * level. Here, you calculate a number of new points that will be added, check whether the bonus rocket is available, as well as increase the score, as presented in the following code snippet:

void Game::IncreaseScore(float distance)
{
  float newPoints = distance * m_level;
  AddBonusRocket(newPoints);
  m_score += newPoints;
}

Actions performed when the rocket reaches the target planet are defined in the ReachPlanet method. Here, you calculate an additional bonus (multiplied by the current level number), check whether the bonus rocket is applicable, update the score, and increase the level number. The suitable code is presented in the following block:

void Game::ReachPlanet()
{
  int newPoints = SA3D_REACH_PLANET_BONUS_BASE * m_level;
  AddBonusRocket((float)newPoints);
  m_score += newPoints;
  m_level++;
}

When the rocket crashes with any asteroid, the CrashWithAsteroid method is called. If it is possible, the number of rockets is decreased and the user can retake the same level, as shown as follows:

void Game::CrashWithAsteroid()
{
  if (m_rocketsNumber > 0) { m_rocketsNumber--; }
}

The Restart method sets default values for the current level number (to 1), the score (to 0), and the number of rockets (to the constant value):

void Game::Restart()
{
  m_level = 1;
  m_score = 0;
  m_rocketsNumber = SA3D_ROCKETS_NUMBER;
}

The last method is named AddBonusRocket and contains a conditional expression, which checks whether the bonus rocket should be obtained by the user:

void Game::AddBonusRocket(float newPoints)
{
  float newScore = m_score + newPoints;
  if ((int)m_score % SA3D_NEW_ROCKET_BONUS >
    (int)newScore % SA3D_NEW_ROCKET_BONUS)
  { m_rocketsNumber++; }
}

GameRenderer class

Apart from the Game class, some modifications should be made in the GameRenderer class. By introducing them, you will use data stored and processed by the Game class.

At the beginning, you include the Game.h file and create two private methods (UpdateProjectionMatrix and LoadLevel), as well as a private field named m_game:

#include "Game.h" (...)
void UpdateProjectionMatrix();
void LoadLevel(); (...)
Game m_game;

The code of the UpdateProjectionMatrix method is shown in the following block:

void GameRenderer::UpdateProjectionMatrix()
{
  float aspectRatio = m_windowBounds.Width /m_windowBounds.Height;
  float fovAngleY = 70.0f * XM_PI / 180.0f;
  if (aspectRatio < 1.0f) { fovAngleY /= aspectRatio; }
  m_projectionMatrix = XMMatrixTranspose(
    XMMatrixPerspectiveFovRH(fovAngleY, aspectRatio,
      0.01f, 1000.0f));
  for_each(m_asteroids.begin(), m_asteroids.end(),
    [&](unique_ptr<Asteroid>& a) {
      a->UpdateProjectionMatrix(&m_projectionMatrix); });
  m_planet.UpdateProjectionMatrix(&m_projectionMatrix);
}

The implementation is extracted from CreateWindowSizeDependentResources, because it should be called every time when new asteroids are created, for instance, when the level increases. The operations performed in this method were described previously in this book.

The CreateWindowSizeDependentResources method is also modified. Instead of updating projection matrices for the planet and all asteroids, using instructions placed directly in this method, it calls the UpdateProjectionMatrix method:

void GameRenderer::CreateWindowSizeDependentResources()
{
  Direct3DBase::CreateWindowSizeDependentResources();
  UpdateProjectionMatrix();
}

The LoadLevel is a new private method, which is added to the GameRenderer class. It prepares the game world for the current level. Its code is as follows:

void GameRenderer::LoadLevel()
{
  m_planet.Initialize(m_game.GetDistanceToPlanet());
  m_planet.SetBuffers(m_modelPlanet.GetVertexBuffer(),
    m_modelPlanet.GetIndexBuffer(),
    m_modelPlanet.GetIndicesCount());
  m_asteroids.clear();
  for (int z = 0; z < m_game.GetDistanceToPlanet(); z++)
  {
    for (int i = 0; i < m_game.GetAsteroidsPerLayer(); i++)
    {
      unique_ptr<Asteroid> asteroid(new Asteroid());
      asteroid->Initialize(z);
      asteroid->SetBuffers(
        m_modelAsteroid.GetVertexBuffer(),
        m_modelAsteroid.GetIndexBuffer(),
        m_modelAsteroid.GetIndicesCount());
      m_asteroids.push_back(move(asteroid));
    }
  }
  reverse(m_asteroids.begin(), m_asteroids.end());
  m_rocket.Initialize();
  UpdateProjectionMatrix();
}

At the beginning, it initializes the planet and sets its buffers. Then, the method removes data of all existing asteroids and creates new ones, using data stored in the Game instance. A specified number of asteroids are added to each layer (the GetAsteroidsPerLayer method), and the number of layers is also dependent on the current level (GetDistanceToPlanet). At the end, the rocket is initialized and the projection matrix is set for all objects.

Some instructions from the CreateDeviceResources method are used inside the LoadLevel method. Thus, to avoid code duplication, the LoadLevel method is called inside the CreateDeviceResources method, just after loading models:

void GameRenderer::CreateDeviceResources()
{
  Direct3DBase::CreateDeviceResources();
  m_modelPlanet.SetColors(Planet::GetColors());
  m_modelPlanet.Load(SA3D_PLANET_MODEL_FILE, m_d3dDevice);
  m_modelAsteroid.SetColors(Asteroid::GetColors());
  m_modelAsteroid.Load(SA3D_ASTEROID_MODEL_FILE, m_d3dDevice);
  LoadLevel();
  auto loadVSTask = DX::ReadDataAsync(SA3D_VERTEX_SHADER_FILE);
  (...)
}

In the Update method (in GameRenderer), you need to calculate the distance covered by the rocket (returned by the Fly method) and call IncreaseScore on an instance of the Game class. Its current code is presented as follows:

void GameRenderer::Update(float timeTotal, float timeDelta)
{ (...)
  float distance = m_rocket.Fly(
    timeDelta * 1.5f * m_game.GetSpeedFactor());
  m_game.IncreaseScore(distance);
  UpdateViewMatrix(); (...)
}
..................Content has been hidden....................

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