Rocket display

The next stage of developing your game is related to adding the rocket display. It presents the number of the current level (on the left), the score (in the middle), and the number of remaining rockets (on the right). In this section, you will learn how to create texture in the .dds format, draw it using the sprite batch mechanism, prepare the sprite font, and display some text on the screen.

Concept

The rocket display should be shown on the whole screen only if the game is in the STARTING or PLAYING state. It needs to support all available screen resolutions, regardless of the aspect ratio. The number of current level should be aligned to the left, the number of remaining rockets should be aligned to the right, while the score should be aligned to the center.

It is worth mentioning that coordinates in 2D graphics in landscape mode are organized as presented in the following diagram. The (0,0) point is located in the top-left corner, and at this point, the rocket display background image should be placed:

Concept

The background of the rocket display will be drawn as a texture, prepared in DDS format (Microsoft DirectDraw Surface). The texture can be understood simply as a bitmap that can be placed in some specific location on the screen. In this case, additional text is rendered on this image using the bitmap-based font.

Texture

The texture in DDS format can be created, for example, in Paint.NET software, which can be downloaded from http://www.getpaint.net/.

Let's start by creating a bitmap with size 2048 x 1024 pixels (powers of two). However, the rocket display background is smaller and has dimensions 1366 x 768 pixels. Such a resolution is not supported by the Windows Phone 8 platform, however, it allows you to use the same image for all three available resolutions. In case of the 16:9 ratio, the whole image (but rescaled) will be shown. If the user has a device with screen resolution in 15:9 ratio, stripes from both sides will be removed, but the most important part of the image will be drawn.

The Paint.NET window with the rocket display image is shown in the following screenshot:

Texture

After creation of the bitmap, it can be saved as the display.dds file. Then, you should copy it to the Assets directory, which already contains the asteroid.obj and planet.obj files.

At the end, include the file in the SpaceAim3DComp project by choosing the Include In Project option from the context menu of the .dds file. Earlier, you need to ensure that the Show All Files option is checked in the Solution Explorer window.

Font

The rocket display requires text which presents a number of the current level, remaining rockets, and the score. To be able to draw some text on the screen, you will prepare a sprite font that is based on the bitmap.

Fortunately, the DirectXTK library contains an additional project called MakeSpriteFont, which makes it possible to easily create sprite fonts for the files available in the operating system. To generate them, you need to build this project. At the beginning, launch a new instance of the Microsoft Visual Studio Express 2012 for Windows Phone.

Then, open a project by choosing the File and Open Project options from the menu, and selecting the MakeSpriteFont.csproj file from the MakeSpriteFont directory located in the main DirectXTK catalog. Then, rebuild the solution, by choosing the Build and Rebuild Solution options from the menu. If the project is built successfully, the MakeSpriteFont.exe file is created automatically inside the binDebug directory in the project's folder. Copy a path to the Debug directory and run the command line by pressing the Windows button + R and typing cmd. Then, navigate to the folder whose path you have in the clipboard, and generate the display.spritefont file by the following command:

MakeSpriteFont.exe "Pacaya" display.spritefont /FontSize:70

Here, Pacaya is a name of the font and 70 is its size.

Tip

You can use various fonts to generate sprite fonts. Here, Pacaya from the open font library (http://openfontlibrary.org/en/font/pacaya) is used. It is available at SIL Open Font License (OFL).

If the sprite font is generated successfully, you should see the following result in the command line:

Importing Pacaya
Cropping glyph borders
Packing glyphs into sprite sheet
Premultiplying alpha
Writing display.spritefont (CompressedMono format)

Next, copy the generated display.spritefont file to the Assets directory inside the SpaceAim3DComp project and include it into the project. You will also need to set the Content property to Yes, by choosing the Properties option from the context menu of the file, changing Configuration to All Configuration, Platform to All Platforms, and setting a suitable value for the Content property, as shown in the following screenshot:

Font

Implementation

An implementation of the rocket display is based on the additional class called RocketDisplay. It handles all operations related to drawing the texture and text. Also, a few small modifications need to be made in the GameRenderer class.

Constants

At the beginning, specify settings related to the rocket display as constant values in the Constants.h file:

#define SA3D_DISPLAY_TEXTURE_FILE L"Assets\display.dds"
#define SA3D_DISPLAY_TEXTURE_WIDTH 1366
#define SA3D_DISPLAY_FONT_FILE L"Assets\display.spritefont"
#define SA3D_DISPLAY_MARGIN_BOTTOM 115.0f
#define SA3D_DISPLAY_MARGIN_SIDE 175.0f
#define SA3D_MAX_SCREEN_HEIGHT 768.0f
#define SA3D_MAX_SCREEN_WIDTH 1280.0f

The values contain a path to the .dds file with the background image, its width, a path to the font file, as well as bottom and side (left and right) margins, which are used to specify a text location. The last two constant values define the maximum supported screen width and height. They are used to place and scale the graphics and text correctly on devices with various screen resolutions, even in different aspect ratios.

RocketDisplay class

The implementation of the rocket display is available in the RocketDisplay class, which prepares required resources (a texture and a font), as well as draws the display.

At the beginning, in the RocketDisplay.h file, you need to include the string, SpriteBatch.h, SpriteFont.h, DDSTextureLoader.h, and Constants.h header files. They are necessary for drawing 2D graphics and loading textures from DDS files. Then, add two using directives, related to the std and DirectX namespaces. The class contains two public methods, as shown in the following code:

class RocketDisplay
{
public:
  void Prepare(ID3D11Device *device);
  void Draw(SpriteBatch *spriteBatch, float height, float width, 
  int level, int score, int rockets, float scaleX, float scaleY);
private:
  unique_ptr<SpriteFont> m_font;
  ID3D11ShaderResourceView *m_texture;
};

The first method prepares resources, and the other draws the rocket panel based on data passed as parameters. Apart from methods, the class has two private fields that store pointers to the sprite font (m_font) and to the ID3D11ShaderResourceView instance, representing the texture (m_texture).

The Prepare method loads the texture from the .dds file, whose path is stored in the constant value (SA3D_DISPLAY_TEXTURE_FILE). This is possible by calling the CreateDDSTextureFromFile function, available in DirectXTK. The last part of the Prepare method loads font data, based on a name of its file, represented by the SA3D_DISPLAY_FONT_FILE constant. You create a new instance of the SpriteFont class, using the unique_ptr, thus you will use a smart pointer. The code of the Prepare method is as follows:

void RocketDisplay::Prepare(ID3D11Device *device)
{
  CreateDDSTextureFromFile(device,
    SA3D_DISPLAY_TEXTURE_FILE, nullptr, &m_texture, 
    MAXSSIZE_T);
  m_font = unique_ptr<SpriteFont>(new SpriteFont(device, 
    SA3D_DISPLAY_FONT_FILE));
}

The Draw method performs four operations—drawing the background image and three strings (the number of the level, remaining rockets, and the score). All actions are performed using the sprite batch, which is a mechanism that allows drawing 2D graphics on the screen. An instance of the SpriteBatch will be created in the GameRenderer class, before calling the Draw method on the RocketDisplay instance. Therefore, a pointer to SpriteBatch is passed as a parameter. The code of the Draw method is shown in the following block:

void RocketDisplay::Draw(SpriteBatch *spriteBatch, float height, 
  float width, int level, int score, 
  int rockets, float scaleX, float scaleY)
{
  float textureWidth = SA3D_MAX_SCREEN_HEIGHT * 
    SA3D_MAX_SCREEN_WIDTH / (height / scaleX);
  float margin = (SA3D_DISPLAY_TEXTURE_WIDTH - textureWidth) / 2;
  spriteBatch->Draw(m_texture, XMFLOAT2(-margin * scaleX, 0), 
    nullptr, Colors::White, 0, XMFLOAT2(0, 0), 
    scaleY, SpriteEffects_None);
  float yCoordinate = (SA3D_MAX_SCREEN_HEIGHT 
    - SA3D_DISPLAY_MARGIN_BOTTOM) * scaleY;
  float levelX = (SA3D_DISPLAY_MARGIN_SIDE - margin) * scaleX;
  wstring levelString = to_wstring(level);
  const wchar_t *levelText = levelString.c_str();
  m_font->DrawString(spriteBatch, levelText, 
    XMFLOAT2(levelX, yCoordinate), 
    Colors::LightGray, 0, XMFLOAT2(0,0), scaleX);
  wstring rocketsString = to_wstring(rockets);
  const wchar_t *rocketsText = rocketsString.c_str();
  XMFLOAT3 rocketsTextSize;
  XMStoreFloat3(&rocketsTextSize, 
    m_font->MeasureString(rocketsText));
  float rocketsX = (SA3D_MAX_SCREEN_WIDTH - 
    SA3D_DISPLAY_MARGIN_SIDE + margin - 
    rocketsTextSize.x) * scaleX;
  XMVECTOR rocketsColor = Colors::LightGray;
  if (rockets == 2) { rocketsColor = Colors::Yellow; }
  else if (rockets == 1) { rocketsColor = Colors::Red; }
  m_font->DrawString(spriteBatch, rocketsText, 
    XMFLOAT2(rocketsX, yCoordinate), 
    rocketsColor, 0, XMFLOAT2(0,0), scaleX);
  wstring scoreString = to_wstring(score);
  const wchar_t *scoreText = scoreString.c_str();
  XMFLOAT3 scoreTextSize;
  XMStoreFloat3(&scoreTextSize, 
    m_font->MeasureString(scoreText));
  float scoreX = ((SA3D_MAX_SCREEN_WIDTH - scoreTextSize.x) / 2) 
    * scaleX;
  m_font->DrawString(spriteBatch, scoreText, 
    XMFLOAT2(scoreX, yCoordinate), 
    Colors::DarkGray, 0, XMFLOAT2(0,0), scaleX);
}

At the beginning, you calculate the expected width of the texture. It can take two values, depending on the aspect ratio—1280 (15:9) and 1365.33 (16:9). As you remember, your texture has width equal to 1366 pixels. Thus, almost the whole image will be presented on devices with 16:9 aspect ratio. Otherwise, the stripes from both sides (left and right) will be removed, and the image will be centered on the screen. After calculating the expected texture width, you can easily obtain a value of margin, representing a width of a single strip on the left or on the right, which is hidden. By using the described approach, you can use the single texture for all resolutions supported by the Windows Phone 8 platform.

The scaleX and scaleY parameters are very important, because they make it possible to arrange the screen similarly in all supported resolutions. As presented in the following diagram, the width and height, as well as the scale are different in case of various resolutions. For this reason, to place the object in the middle of the screen, you cannot just put it in (600, 350) coordinates. In WXGA resolution, it will work fine, but in WVGA, the image will be drawn in the bottom-right corner. With the usage of scale, you can calculate a suitable location on the screen.

RocketDisplay class

However, in case of various aspect ratios, you need two scale factors—one for x coordinate, and the other for y. As you could see in the diagram, in case of 1280 x 720 resolution, these scale factors are different, thus such an approach gives you more flexibility than using just one value.

The Draw method of the SpriteBatch class takes a few parameters, including a pointer to ID3D11ShaderResourceView that represents the texture, a position where the texture should be drawn, its rotation, and scale. In this case, you want to draw the texture represented by the m_texture field, place its top-left corner in the (-margin * scaleX, 0) coordinates, not rotate, and rescale by the scaleY value passed as a parameter.

To draw the text on the screen, you can use the DrawString method of the SpriteFont class. It takes a few parameters including a pointer to the SpriteBatch instance used for drawing, a text that should be displayed, its location on the screen, color, rotation, or scale. It is important that this method takes the text as const wchar_t *, thus you need to call the c_str method on the wstring instance before calling the DrawString method. A location of the text is calculated using the scaleX and scaleY parameters.

The number of remaining rockets should be placed all the time aligned to the right and located next to the rocket image. It requires to calculate the x coordinate in a way presented in the following diagram (on the left), using the formula: x = sw–z–tw, where sw denotes the screen width, tw denotes text width, and z is equal to the predefined margin adjusted by the additional margin related to shown or hidden strips. That means that if the stripes are hidden (image is narrowed) you should limit the margin by a width of the single strip. Of course, a result should be multiplied by the scaleX parameter.

To draw all texts on the rocket panel, use the same y coordinate, as calculated earlier.

RocketDisplay class

It is worth mentioning that you can easily draw text in various colors. As presented in case of the number of remaining rockets, you can change the color depending on the number. If it is equal to two, use yellow (Colors::Yellow). When the user has only one remaining rocket it becomes red (Colors::Red). Otherwise, the light gray color is applied (Colors::LightGray).

To measure a text, you can call the MeasureString method on the SpriteFont instance that represents a font, which you want to use for measuring. This method returns an instance of XMVECTOR, that can be then converted into XMFLOAT3 by the XMStoreFloat3 method, as shown in the code.

The last text shows the player score. It should be displayed between the number of level and remaining rockets. For this reason, it should be placed in the middle of the screen all the time, with respect to the x coordinate. The y coordinate should have the same value as in case of other captions. The formula for calculating the x coordinate can be: x=(sw–tw)/2. It is also presented in the previous diagram (on the right).

GameRenderer class

Apart from implementation of the RocketDisplay class, you will need to make some small changes in GameRenderer. They are related to a presentation of the rocket display every time when the game is in the STARTING or PLAYING state. What is more, you should create an instance of the SpriteBatch class that is used to draw 2D graphics.

At the beginning, let's include the RocketDisplay.h and SpriteBatch.h header files. Then, you create four private fields, which represent the rocket display, sprite batch used for drawing, and scale factors.

RocketDisplay m_rocketDisplay;
unique_ptr<SpriteBatch> m_sbDrawing;
float m_scaleX;
float m_scaleY;

Next, modify the CreateWindowSizeDependentResources method in a way that makes it possible to initialize the rocket display, by calling the Prepare method. Also, create an instance of the SpriteBatch class using unique_ptr. At the end, a value of the m_scaleX field is calculated by dividing the screen width (obtained from the m_renderTargetSize field) by the maximum supported screen width (1280 in this case). Similarly, set a value of the m_scaleY field, as follows:

void GameRenderer::CreateWindowSizeDependentResources()
{
  Direct3DBase::CreateWindowSizeDependentResources();
  m_rocketDisplay.Prepare(m_d3dDevice.Get());
  m_sbDrawing = unique_ptr<SpriteBatch>(
    new SpriteBatch(m_d3dContext.Get()));
  m_scaleX = m_renderTargetSize.Width / SA3D_MAX_SCREEN_WIDTH;
  m_scaleY = m_renderTargetSize.Height / SA3D_MAX_SCREEN_HEIGHT;
  UpdateProjectionMatrix();
}

The SpriteBatch instance, created in the CreateWindowSizeDependentResources method, is used in the Render method, as shown in the following code:

void GameRenderer::Render()
{ (...)
  m_sbDrawing->Begin();
  switch (m_game.GetState()) { (...) }
  m_sbDrawing->End();
}

Here, you call the Begin and End methods. The first one is called just before the switch statement, and the other—when it ends. Between them, you can draw textures and texts, as presented in the RocketDisplay implementation.

The last required modification in the GameRenderer class involves the RenderPlaying method:

void GameRenderer::RenderPlaying()
{
  RenderPlanetAndAsteroids();
  m_rocketDisplay.Draw(m_sbDrawing.get(), 
    m_renderTargetSize.Height, m_renderTargetSize.Width, 
    m_game.GetLevel(), m_game.GetScore(), 
    m_game.GetRocketsNumber(), m_scaleX, m_scaleY);
}

Here, you call the Draw method (on the RocketDisplay instance) with a set of parameters, including game data (for example, the number of remaining rockets), screen resolution (width and height), and a pointer to the SpriteBatch instance. This method draws the rocket display background image, together with the number of the current level and remaining rockets, as well as the score.

Let's launch the game and see the display of the rocket as shown in the following screenshot. Currently, you know how to draw textures and texts on the screen, thus you can proceed to another part of this chapter where you will get to know how to support many languages in the native part.

GameRenderer class
..................Content has been hidden....................

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