Level countdown

The next feature that will be added in your game is the countdown at the beginning of each level. It should present numbers, decreased every second, until zero is reached. However, during the countdown period, the player should still be able to steer the rocket, as well as start and stop the additional engine. In this section, you will learn how to implement such a feature and use the translations mechanism.

Concept

The countdown feature should be activated only when the game is in the STARTING state. As soon as the countdown is finished, the state should be changed to PLAYING. During both stages, the rocket is flying towards the planet, as presented in the following diagram:

Concept

The countdown presents a text using a specified font. In this case, Pacaya with size 120 is chosen. You can generate the sprite font by the following command:

MakeSpriteFont.exe "Pacaya" countdown.spritefont /FontSize:120
/CharacterRegion:0x0000-0x0180

Here, you use an additional option named CharacterRegion. It allows to specify a range of characters which should be available in the sprite font. In this case, it is required to indicate that Polish characters should be included.

Tip

If the sprite font does not contain the given character, an exception is thrown while drawing the text. Thus, especially in localized applications, you should ensure that the sprite font is prepared with the correct character region.

The font file should be saved as countdown.spritefont in the Assets directory of the SpaceAim3DComp project and included into it. You should not forget to set the Content property to Yes, as described during creation of the first .spritefont file.

Implementation

An implementation of the countdown feature requires an additional class, named Countdown, as well as some small modifications in the GameRenderer class. All necessary changes are described in this section, and important parts of code are presented.

Constants

At the beginning, add a few constant values to the Constants.h file:

#define SA3D_COUNTDOWN_DELAY 1.0f
#define SA3D_COUNTDOWN_NUMBER 3
#define SA3D_COUNTDOWN_FONT_FILE L"Assets\countdown.spritefont"

The first constant (SA3D_COUNTDOWN_DELAY) represents a period of time (in seconds) which should elapse to decrease the counter. The second indicates a maximum number used for the countdown. A value equal to three means that the countdown feature will present the 3, 2, and 1 numbers. The last constant value (SA3D_COUNTDOWN_FONT_FILE) stores a path to the sprite font file.

Countdown class

The main part of the countdown implementation is located in the Countdown class. It initializes required resources, updates countdown number, and draws it on the screen. The definition of the Countdown class is as follows:

class Countdown
{
public:
  void Prepare(ID3D11Device *device);
  void Update(float time);
  void Draw(SpriteBatch *spriteBatch, wstring levelTranslation, 
    float height, float width, int level, float scaleX, 
    float scaleY);
  void SetStartTime(float time) { m_startTime = time; }
  void SetCountdownNumber(int nr) { m_countdownNumber = nr; }
  float GetStartTime() { return m_startTime; }
  int GetCountdownNumber() { return m_countdownNumber; }
private:
  unique_ptr<SpriteFont> m_font;
  float m_startTime;
  int m_countdownNumber;
};

At the beginning, include string, SpriteBatch.h, SpriteFont.h, and Constants.h files. Then, add using directives for std and DirectX namespaces.

The Countdown class has seven public methods, and four of them are defined inline. The SetStartTime method allows to set a time, which represents a moment when the countdown is started or when the current number is set. It is changed every time when the countdown number is decreased. The second inline method (SetCountdownNumber) just updates the number that is presented on the screen. Two last methods return the start time (GetStartTime) and the current number (GetCountdownNumber).

The class also contains three private fields. The first represents the font (m_font). The others are used to store the start time and the countdown number.

The Prepare method initializes the font required to draw the content (the countdown number and the level number). A name of the file is taken from the constant value, as shown in the following code:

void Countdown::Prepare(ID3D11Device *device)
{
  m_font = unique_ptr<SpriteFont>(
    new SpriteFont(device, SA3D_COUNTDOWN_FONT_FILE));
}

The Update method changes the countdown number and start time depending on the total time of the game. Its code is as follows:

void Countdown::Update(float time)
{
  if (m_startTime == 0) { m_startTime = time; }
  if (m_countdownNumber > 0 
    && time - m_startTime > SA3D_COUNTDOWN_DELAY)
  {
    m_startTime = time;
    m_countdownNumber--;
  }
}

At the beginning, the start time is set if it has not been already set. It means that the countdown display should be restarted and should present the highest possible countdown number.

The following part of this method checks whether the counter is higher than zero and a suitable period of time elapsed since the last number modification. In such a case, the start time is updated and the countdown number is decreased.

The Draw method draws both the level number and the countdown number on the screen. It uses the DrawString method and places texts in proper locations on the screen, as shown in the following block of code:

void Countdown::Draw(SpriteBatch *spriteBatch, 
  wstring levelTranslation, float height, float width, 
  int level, float scaleX, float scaleY)
{
  wstring levelString = levelTranslation + L" " + to_wstring(level);
  const wchar_t *levelText = levelString.c_str();
  XMFLOAT3 levelTextSize;
  XMStoreFloat3(&levelTextSize, m_font->MeasureString(levelText));
  int levelX = (int)((SA3D_MAX_SCREEN_WIDTH-levelTextSize.x) / 2);
  m_font->DrawString(spriteBatch, levelText, 
    XMFLOAT2(levelX * scaleX, 200 * scaleY), Colors::LightGray, 
    0, XMFLOAT2(0,0), scaleX);
  wstring countdownString = to_wstring(m_countdownNumber);
  const wchar_t *countdownText = countdownString.c_str();
  XMFLOAT3 countdownTextSize;
  XMStoreFloat3(&countdownTextSize, 
    m_font->MeasureString(countdownText));
  int countdownX = (int)((SA3D_MAX_SCREEN_WIDTH 
    - countdownTextSize.x) / 2);
  m_font->DrawString(spriteBatch, countdownText, 
    XMFLOAT2(countdownX * scaleX, 325 * scaleY), 
    Colors::White, 0, XMFLOAT2(0,0), scaleX);
}

GameRenderer class

Some additional modifications need to be made in the GameRenderer class. They are necessary to update the countdown feature and draw it on the screen correctly.

First of all, you need to include the Countdown.h and LocalizedStrings.h files, as well as create a new private field, as presented in the following line of code:

Countdown m_countdown;

In the LoadLevel method, set the current countdown number to the maximum available value, represented by the SA3D_COUNTDOWN_NUMBER constant. Then, reset the level start time. This means that in the next Update call (on the Countdown instance) the countdown will start. The code is as follows:

void GameRenderer::LoadLevel()
{ (...)
  m_countdown.SetCountdownNumber(SA3D_COUNTDOWN_NUMBER);
  m_countdown.SetStartTime(0);
}

In the case of the CreateWindowSizeDependentResources method, you just call the Prepare method to initialize the font required by the countdown feature:

void GameRenderer::CreateWindowSizeDependentResources()
{
  Direct3DBase::CreateWindowSizeDependentResources();
  m_rocketDisplay.Prepare(m_d3dDevice.Get());
  m_countdown.Prepare(m_d3dDevice.Get()); (...)
}

In the UpdateStarting method, call Update on the CountdownDisplay instance, passing the total game time as parameter. Then, check whether the current countdown number reaches zero. In such a situation, change the current state to PLAYING. It prevents the game from drawing the countdown feature until next level is loaded, or the same level is restarted. The code is as follows:

void GameRenderer::UpdateStarting(float timeTotal,float timeDelta)
{
  UpdatePlaying(timeTotal, timeDelta);
  m_countdown.Update(timeTotal);
  if (m_countdown.GetCountdownNumber() == 0)
    { m_game.SetState(SA3D_STATE_PLAYING); }
}

The last modification is required in the RenderStarting method. Here, you draw the countdown feature, using localized strings. It is worth mentioning that you can easily obtain a translation by calling the Get static method of the LocalizedStrings class, as presented in the following code snippet:

void GameRenderer::RenderStarting()
{
  RenderPlaying();
  wstring levelTranslation = LocalizedStrings::Get(L"Level");
  m_countdown.Draw(m_sbDrawing.get(), levelTranslation, 
    m_renderTargetSize.Height, m_renderTargetSize.Width, 
    m_game.GetLevel(), m_scaleX, m_scaleY);
}

Direct3DInterop class

As you could see, the localized Level string is required to draw the countdown. It should be automatically provided in the valid language, consistent with the current language used by the player. Thus, you will read the language code in the managed part of the game and pass it to the native one.

At the beginning, you should include the LocalizedStrings.h header file in the Direct3DInterop.h file. Then, add a new public property, just before the WindowBounds definition, as presented in the following code:

property Platform::String^ LanguageCode
{
  void set(Platform::String^ code)
    { LocalizedStrings::Load(code->Data()); }
}

The LanguageCode property has only the set accessor. Thus, whenever you set its value, the Load static method (of the LocalizedStrings class) is called. However, it requires the wstring type as a parameter, but the type of code argument is Platform::String^. Fortunately, you can get the pointer to constant wchar_t data by calling the Data method.

The remaining modifications are necessary in the managed part, thus you should rebuild the project to ensure that no build errors exist in the native part.

GamePage class

The last change is required in the GamePage.xaml.cs file. Here, you should assign a value to the LanguageCode property of the Direct3DInterop class. You can obtain the current language code using the CultureInfo class, as presented in the following code snippet:

private void DrawingSurface_Loaded(object sender,RoutedEventArgs e)
{
  if (m_d3dInterop == null)
  { (...)
    m_d3dInterop.LanguageCode = CultureInfo.CurrentUICulture.Name;
  }
}

Let's launch the game and see the countdown. You can steer the rocket, avoid all asteroids, and reach the planet. In such a situation, the game loads the second level, and you see the countdown with information that you are on the second level.

It is worth mentioning that the mechanism supports localization and works fine on devices with all three available resolutions, as shown in the following screenshot:

GamePage class

Tip

The author strongly encourages you to experiment with 2D graphics on your own. For instance, you could equip your game with additional feature that presents basic instructions how to play, at the beginning of the first level. Thus, the player will not need to read the help and can be easily introduced into the game by additional captions and images, for example, showing the steering concept.

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

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