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();
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); }
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); } }
Comments