Finishing Up with the game


Finishing Up

Figure 9.1 has some text being rendered that you haven't added yet. Obviously, you want the user to know the current state of the game and how close he is to winning (or losing)! You need to declare a new font variable to render this text, as well as one for the quit screen you just created:
// The main game font
private Direct3D.Font gameFont = null;
// The UI Screen for quitting
private QuitScreen quitScreen = null;

Naturally, you must also create these objects. In your OnCreateDevice method directly before the OnResetDevice call, add the following code to create these objects:
gameFont = new Direct3D.Font(device, new System.Drawing.Font(this.Font.Name,
                                                             28.0f));
// Create the screen for the UI Screen
quitScreen = new QuitScreen(device, buttonTexture, screenWidth, screenHeight);
quitScreen.Cancel += new EventHandler(OnQuitScreenCancel);
quitScreen.QuitGame += new EventHandler(OnQuitScreenQuit);

The quit screen object hooks the two events and uses these event handlers:
private void OnQuitScreenCancel(object sender, EventArgs e)
{
    // The game is paused, they clicked no, unpause
   sampleFramework.Pause(false, false);
    isQuitMenuShowing = false;
}
private void OnQuitScreenQuit(object sender, EventArgs e)
{
    // The game is paused, they want to quit, quit to main screen
    isMainMenuShowing = true;
    isQuitMenuShowing = false;
   sampleFramework.Pause(false, false);
    // Reset the level back to the first
    levelNumber = 1;
}

When the quit screen is showing, you obviously don't want the user to be punished, so you pause the timer during this time. If she clicks the No button and does not want to quit, all you need to do is restart the timer and reset the variable. However, if she clicks the Yes button and does want to quit, not only do you do these things, but you also reset the level back to one and return to the main menu screen. This move allows the player to restart a new game.
You need to ensure that these objects are cleaned up correctly, so add the cleanup code to your OnDestroyDevice method:
if (quitScreen != null)
{
    quitScreen.Dispose();
}
// Clean up the game font
if (gameFont != null)
{
    // Clean up the game font
    gameFont.Dispose();
    gameFont = null;
}

You might think that you're done creating these objects, but the font still needs a bit more work. Just like the debug font you created in an earlier chapter, the font needs to respond to a device being lost or reset. Add this call to the end of OnResetDevice:
// Reset the game font
gameFont.OnResetDevice();

Add this code to the end of OnLostDevice:
if (gameFont != null)
{
    // Make sure the game font cleans up resources
    gameFont.OnLostDevice();
}

Now you have all the objects created, but you haven't updated the rendering yet. Add the code in Listing 9.12 to the end of your RenderGameScene to handle showing both the quit menu and the user statistics.
Listing 9.12. Rendering Stats and Quit User Screen
if (isQuitMenuShowing)
{
    quitScreen.Draw();
}
if (isLoadingLevel)
{
    string loadingText = string.Format(
        "Try Level {0}\r\nAre you ready?", levelNumber);
    if (!isQuitMenuShowing)
    {
        gameFont.DrawText(null, loadingText, this.ClientRectangle,
            DrawTextFormat.Center | DrawTextFormat.VerticalCenter |
            DrawTextFormat.NoClip, Color.WhiteSmoke);
    }
}
else
{
    // if there is a level happening, update some stats
    if (currentLevel != null)
    {
        // Draw current state
        string blockInfo = string.Format("Blocks Remaining {0} - Correct {1}",
            currentLevel.NumberBlocks - currentLevel.NumberCorrectBlocks,
            currentLevel.NumberCorrectBlocks);

        Color statColor = Color.White;
        // Change the color once you've gotten more than halfway complete
        if ((currentLevel.NumberBlocks - currentLevel.NumberCorrectBlocks)
            > currentLevel.NumberCorrectBlocks)
        {
            statColor = Color.Turquoise;
        }

        gameFont.DrawText(null, blockInfo, new Rectangle(0, screenHeight - 60,
            ClientRectangle.Width, ClientRectangle.Height),
            DrawTextFormat.Center | DrawTextFormat.NoClip,
            statColor);
        if (!currentLevel.IsGameOver)
        {
            string gameState = string.Format("Moves: {0} (Max: {1})\r\nTime
                                              Remaining: {2}",
                currentLevel.TotalMoves, currentLevel.MaximumMoves,
                currentLevel.TimeRemaining);

            gameFont.DrawText(null, gameState, new Rectangle(0, 30,
                ClientRectangle.Width, ClientRectangle.Height),
                DrawTextFormat.Center | DrawTextFormat.NoClip,
                Color.Yellow);
        }
        else
        {
            string gameState;
            Color c;
            if (currentLevel.IsGameWon)
            {
                gameState = string.Format("Congratulations! You win this level!");
                c = Color.White;
            }
            else
            {
                gameState = string.Format("Game Over!!");
                c = Color.Red;
            }
            gameFont.DrawText(null, gameState, new Rectangle(0, 30,
                ClientRectangle.Width, ClientRectangle.Height),
                DrawTextFormat.Center | DrawTextFormat.NoClip,
                c);
        }
    }
}

As you see here, if the quit menu is showing, you draw that. After that, you really just need to update the player statistics. Before the level is loaded, you don't want to show any level-specific stats, so instead you simply draw some text notifying the player to get ready and center it in the client area.
If someone is actually playing a level, player statistics should be rendered instead. You always render the number of blocks that are correct and the number of blocks remaining in the level, at the bottom of the screen. Notice that when less than half of the blocks are correct, the text changes to turquoise, rather than the default white. If the game isn't over, the only statistics needed are the amount of time currently passed and the time remaining, which are rendered at the top of the screen. If the game is over, you simply render the appropriate message to the screen. If the player won the level, and there are more levels remaining, it is automatically loaded in the OnFrameMove method. If the player lost the level or is on the last level, he is required to quit.
Can you feel the anticipation? You have only one thing left to do: handle the user input in your game engine. For each of these methods, you already have code handling the main menu screen and the Select Loopy screen; you simply add the final else statement. In the OnMouseEvent method, make sure the quit screen is handled when the left button is not down:
else if (isQuitMenuShowing)
{
    quitScreen.OnMouseMove(x, y);
}

Also handle it when it is:
else if (isQuitMenuShowing)
{
    quitScreen.OnMouseClick(x, y);
}

It all ends with the keyboard input and the OnKeyEvent method:
else if (isQuitMenuShowing)
{
    quitScreen.OnKeyPress(key);
}
else
{
    // Allow you to quit at any time
    if (key == System.Windows.Forms.Keys.Escape)
    {
        ShowQuitMenu();
    }

    // Ignore any keystrokes while the fly through is displayed
    if (!isLoadingLevel)
    {
        // Otherwise, let the level handle the keystrokes
        currentLevel.OnKeyPress(key);
    }
}

As you see, this one is a little different because not only do you want the quit screen to handle the keyboard input, but you want the level to as well. If the quit screen isn't showing, and the key is escape, you should show the quit menu. Otherwise, if you are playing the level, pass the keystroke on. This part is the last thing that actually allows your player to move around the screen and win the levels. You can find the implementation for the ShowQuitMenu method here:
private void ShowQuitMenu()
{
    isQuitMenuShowing = !isQuitMenuShowing;
    if (isQuitMenuShowing)
    {
        // The game is now paused, the 'quit' message will be shown

        // Pause the timer as well
        sampleFramework.Pause(true, false);
    }
    else
    {
        // Restart the timer
        sampleFramework.Pause(false, false);
    }
}

It simply flips the Boolean value for determining whether the quit menu is showing and either pauses or restarts the timer, depending on the new value. Can you believe it? You're done!

Comments

Popular Posts