Now that you have a good introductory foundation on creating an application interface, it is time to move on to this chapter's game project. The Speed Typing game will be created using the same five steps covered in previous chapter projects. When you are finished, you will have created a game that uses many of the techniques outlined in this chapter in one complete application.
The Speed Typing game runs in a single window. It is composed of one form and the 12 controls listed in Table 3.10.
Control Type | Control Name | Description |
---|---|---|
Label | lblInstructions | Instructions on how to play the game |
Label | lblSourceText | The Textbox field containing the current sentence that the player must copy |
Label | lblEntryText | The Textbox field where the player must type the matching sentence |
TextBox | txtDisplay | TextBox control that contains the source text string to copy |
TextBox | txtEntry | TextBox control that contains the player's typed input |
Button | btnGo | Button control that the player uses to start the game |
Button | btnDone | Button control that the player clicks when finished typing a sentence |
Button | btnExit | Button control that the player clicks to end the game |
Timer | tmrControl | Timer control that controls the amount of available time to type In each sentence |
ToolTip | tipControl | ToolTip control that allows the game to display ToolTip messages |
StatusStrip | stsControl | StatusStrip control that holds the StatusLabel control |
StatusLabel | stsLabel | StatusLabel control that is used to display messages as the game runs |
To start developing the Speed Typing game, you must first open a new project in Visual C++ by following these steps:
If you have not already done so, launch Visual C++ 2005 Express and then click on File and select New Project. The New Project dialog box appears.
Select the Windows Forms Application template.
Type The Speed Typing Game as the name of your new application in the Name field located at the bottom of the New Project window.
Click on OK to close the New Project dialog box.
Next, begin by adding the necessary elements that will make up the game's user interface. The following list describes the steps you need to follow to set up the application's GUI:
Expand the form so that it can accommodate a wide text box.
Add the ToolTip control to your form. By default, Visual C++ assigns the control a default name of toolTip1. It is displayed at the bottom of the Form Designer window in the Component Tray.
Add the Timer control to your form. By default, it is assigned a name of timer1 and is displayed in the Component Tray.
Add a TextBox control to the form. Its default name is textBox1. Expand and reposition it in the middle of the form.
Add a second TextBox control to the form. Its default name is textBox2. Expand and reposition it under the previous TextBox control.
Add a Label control to the upper-left corner of the form. Its default name is label1.
Add a second Label control to the form just above the first TextBox control. Its default name is label2.
Add a third Label control to the form just above the second TextBox control. Its default name is label3.
Add a Button control to the lower-left corner of the form. Its default name is button1.
Add a second Button control to the lower-right side of the form. Its default name is button2.
Add a third Button control to the upper-right corner of the form. Its default name is button3.
Add a StatusStrip control to your form. Its default name is statusStrip1.
Click once on the StatusStrip control and select the StatusLabel control from the drop-down list. By default, its name is toolStripStatusLabel1.
The controls need to be customized. As in the previous chapter games, you can customize the controls using the Properties window. You can also customize the control names so that the name appears in the code that Visual C++ generates. By customizing the control names, your code becomes more logical and organized.
The way that you name code depends on the naming convention you choose to follow. A naming convention is a consistent way of spelling out the important aspects of your code, such as events that your application needs to respond to or the way that program statements reference data. Naming conventions make programs easier to read and understand because the spelling and formatting provide reliable hints as to how the code works.
Before I explain the naming convention I will be using in future chapters, you need to understand a few rules about what sort of names Visual C++ accepts. These rules include the following:
Names must begin with either an alphabetic character or the underscore character (_).
Names can only consist of alphabetic characters, numbers, and the underscore character (_).
Names cannot match Visual C++ keywords.
Following a naming convention is often considered a mark of professionalism because in professional environments programmers often have to read and understand each other's code. Many different naming conventions are available for you to follow, each of which has its proponents and critics. To get used to using a naming convention, you should first become comfortable with being consistent about how you spell the names of controls, objects, or other custom coding statements that you write in your program. Try, for example, to make them descriptive and clear in terms of function and purpose.
Programmers often follow a consistent spelling in identifying variables. You have several patterns to choose from. One that I have used for quite some time and will use throughout the examples in this book is called camelCase. This style of naming variables is popular and derives from the "hump" that capitalizing words in the middle creates. The format for camel-Case involves using two or more words, run together, often with some words within the name shortened by common abbreviations.
Working with the names that Visual C++ assigns to form controls can be challenging to remember, especially if you add a lot of controls to your application. So instead of generic names, I'll strive to assign a descriptive name to form elements that describes their function. For example, I might assign a name of btnExit to a button that is responsible for exiting an application If clicked. Or I might assign a name of txtInputName to a TextBox control intended to collect a user's name.
Let's start customizing the form and all the controls that you have added to it. Begin by modifying Form1, changing the properties listed in Table 3.11.
Property | Value |
---|---|
FormBorderStyle | FixedSingle |
Icon | app.ico [1] |
StartPosition | CenterScreen |
Text | Speed Typing |
[1]You can find I con. ico by default in the debug directory of any project you create. |
Next, set the Name property of the ToolTip control to tipControl. Then change the Name property of the Timer control to tmrControl and set its Interval property to 1000.
Then modify the properties belonging to the two TextBox controls, as shown in Table 3.12.
Control | Property | Value |
---|---|---|
textBox1 | Name | txtDisplay |
ReadOnly | True | |
ToolTip | Displays source text string | |
textBox2 | Name | txtEntry |
ReadOnly | True | |
ToolTip | Type your text here |
Next change the properties belonging to the three Label controls, as shown in Table 3.13.
Control | Property | Value |
---|---|---|
label1 | Name | lblInstructions |
Text | Click Go to begin. You have 15 seconds to type the text displayed in the Source Text field exactly as shown. | |
label2 | Name | lblSourceText |
Text | Source Text: | |
label3 | Name | lblEntryText |
Text | Enter Text Here: |
Modify the properties belonging to the three Button controls, as shown in Table 3.14.
Control | Property | Value |
---|---|---|
button1 | Name | btnGo |
Text | Go | |
ToolTip | Display new text string | |
button2 | Name | btnDone |
Enabled | False | |
Text | Done | |
ToolTip | Check typing | |
button3 | Name | btnExit |
Text | Exit | |
ToolTip | Exit game |
Then modify the Name property of your StatusStrip control to stsControl. Finally, change the Name property of the toolStripStatusLabel control within the tool strip to stsLabel. These are all the control property modifications that you must make for the Speed Typing game.
Now it is time to begin coding. To give the Speed Typing game life, you need to add code in five places, including the btnGo, btnDone, btnExit, and tmrControl controls. In addition, you need to add a few lines of code to define some variables.
Trap | It's a bit too early to start discussing the statements that make up the Visual C++ programming language as of yet. You might not be able to understand everything that you'll see, but at this stage, you only need to key in what you see and follow along. Starting with Chapter 5, "Storing and Retrieving Data in Memory," we will be covering the specifics of what you see in greater detail so that by the time you return to this chapter, you will fully understand what is going on. |
For starters, select the Code option from the View menu. The Code Editor appears. You see many lines that Visual C++ has generated for your application automatically. Don't worry if you don't understand what the lines mean at this point—the code will become clearer as you learn more.
Trick | Although it might seem somewhat unintelligible at this point, much of the code that you see is the result of the settings you've chosen in Design view. Almost all the Design view settings must have some code equivalent. This code that Visual C++ automatically generates actually turns your Design view settings into the behavior that drives your application. |
At present, you should be at the top of the form's code, at a line that reads as follows:
namespace TheSpeedTypingGame
Scroll down and locate the section that reads like this:
private: /// <summary> /// Required designer variable. /// </summary>
Right below this section, add the following lines (which have been highlighted in bold):
private: /// <summary> /// Required designer variable. /// </summary> Int16 intWrong; // Tracks number of strikes Int16 intCount; // Tracks number of tries Int16 intTimer; // Tracks elapsed time
These three statements define the variables that the game uses to track the number of strikes made by the player, the number of tries the player has made, and the amount of time that has elapsed for each turn. But before the program can use these values, the values must be initialized. One place to do this is in the Form1_Load event. To place the appropriate statements, switch back to the Form Designer and double-click the form. Your cursor is positioned at code that looks similar to the following example:
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { }
To allow the application to keep track of the game information it needs, add the lines shown in bold in the next listing.
private: System::Void _Form1_Load(System::Object^ sender, System::EventArgs^ e) { //Set beginning data values intWrong = 0; intCount = 0; intTimer = 0; }
Next, return to the Form Designer and double-click on the btnGo button. This switches you back to the Code Editor, where the following new code has been added:
private: System::Void btnGo_Click(System::Object^ sender, System::EventArgs^ e) { }
This code identifies the program statements that run when the user clicks on the btnGo button. At the moment, it is empty and does nothing. To add code to this portion of your application, enter the programming statements shown next between these two statements:
private: System::Void btnGo_Click(System::Object^ sender, System::EventArgs^ e) { // Display game sentences according to level if( intCount == 0 ) txtDisplay->Text = "Once upon a time there " "were three little pigs."; if( intCount == 1 ) txtDisplay->Text = "In days gone by times " "were hard but the people were strong."; if( intCount == 2 ) txtDisplay->Text = "Once in awhile something " "special happens even to the worst of people."; if( intCount == 3 ) txtDisplay->Text = "When injustice rears its " "ugly head, it is the duty of all good " "citizens to object."; if( intCount == 4 ) txtDisplay->Text = "It has been said that in " "the end there can only be one. Let that " "one be Mighty Molly!"; btnDone->Enabled = true; // Activate Done button btnGo->Enabled = false; // Disable Go button // Allow text to be entered txtEntry->ReadOnly = false; // Activate timer tmrControl->Enabled = true; intTimer = 0; txtEntry->Focus(); }
The first five statements beginning with the Visual C++ keyword if test what level the game is currently running at (0-4) and display the correct text in the txtDisplay control. For example, if this is the player's first turn, then "Once upon a time there were three little pigs" is shown.
The next two statements enable the btnDone button and disable the btnGo button. Next, the txtEntry control's Readonly property is set equal to True to allow the player to begin typing text into it. Then the tmrControl is enabled and the value of intTimer is set equal to 0. This begins the 15-second countdown sequence. Finally, the Focus() method causes the cursor to be placed automatically in the txtEntry field each time it's encountered.
Trick | The Focus method determines in code which control receives focus. This effectively sets the current active control. |
Return to the Form Designer window and double-click on the btnDone button. This switches you back to the Code Editor, where code for the btnDone control's click event has been added, as shown next.
private: System::Void btnDone_Click(System::Object^ sender, System::EventArgs^ e) { }
Add the following statements between these two statements:
//Clear Out Status Bar Text stsControl->Text = ""; // Deactivate timer so it doesn't keep going tmrControl->Enabled = false;
The first statement, as you can see from the comments, clears out any text that might be displayed in the StatusStrip. The second statement disables the game's Timer control to prevent it from counting down and eventually declaring a strike at 15 seconds.
Next, add the following statements just beneath the previous statements:
// Make sure player entered something if( String::IsNullOrEmpty( txtEntry->Text ) ) { // Show error MessageBox::Show( "Error: You must enter something!" ); // Reset game txtDisplay->Text = ""; btnDone->Enabled = false; btnGo->Enabled = true; txtEntry->ReadOnly = true; intTimer = 0; btnGo->Focus(); return; }
This block of code checks to ensure that the player has typed something into the txtEntry field. If the user has entered anything, the indented statements contained within the opening { and closing } braces execute, displaying an error message and resetting all the controls back to their initial settings. Note the use of the return statement just before the final brace. The return statement tells Visual C++ to stop processing any of the remaining statements in the btnDone control's click event. This is appropriate because it's useless to perform further processing if the player has not typed anything.
Next, add the following statement just beneath the previous statements:
if( String::Compare( txtEntry->Text, txtDisplay->Text ) == 0 ) { // Handle correct answer MessageBox::Show( "Match! You typed the string in " " correctly!" ); intCount += 1; intTimer = 0; } else { // Handle incorrect answer intWrong += 1; MessageBox::Show( String::Concat( "Strike ", intWrong.ToString(), "! You made at least", " " one typo." ) ); intTimer = 0; }
These statements compare what the player has entered with the string to be copied. The two pieces of text must match exactly. The code starts by determining if the text string currently stored in the txtEntry control compares to the text string stored in the txtDisplay control. If a match exists, the MessageBox::Show method is used to inform the player, and the game adds 1 to the number of turns of levels the player has completed and resets the variable used by the Timer control (to track the number of seconds that a turn has lasted) back to 0. However, if the text stored in the two controls does not match, an error message is displayed, and the game adds 1 to the number of strikes made by the player before resetting the intTimer variable back to 0.
Now add the following statements just beneath the previous statements:
// Get set up for next level txtEntry->Text = ""; txtDisplay->Text = ""; btnDone->Enabled = false; btnGo->Enabled = true; txtEntry->ReadOnly = true; intTimer = 0; btnGo->Focus();
These statements reset the form's controls to their starting settings and shift the focus back to the btnGo control. This allows the player to attempt to match the sentences again.
Add the following statements just beneath the previous statements:
// Handle three strikes if( intWrong == 3 ) { // Inform player he's a beginner if( intCount < 2 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Beginner. " "Please play again!" ); intCount = 0; intWrong = 0; return; } // Inform player he's Intermediate if( intCount < 4 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Intermediate. " "Please play again!" ); intCount = 0; intWrong = 0; return; } // Inform player he's advanced if( intCount < 5 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Advanced. " "Please play again!" ); intCount = 0; intWrong = 0; return; }
This code appears a little more complicated, but it is really only performing tests and displaying messages as a result. Each time the player clicks on the btnDone button, the game tests whether the player has struck out or if all five levels have been completed. These statements toward the end determine whether the player won the game and, if so, display the correct reward message. If, on the other hand, the player earned three strikes, the game assigns a skill level rating according to his score.
Now it's time to add a really simple code statement to the btnExit control, as shown next:
private: System::Void btnExit_Click(System::Object^ sender, System::EventArgs^ e) { this->Close(); }
The last control that the game needs is the Timer control. Switch back to the Design view and then double-click on the tmrControl control. This returns you to the Code Editor, where the following new code has been added:
private: System::Void tmrControl_Tick(Systern::Object^ sender, System::EventArgs^ e) { }
Modify this section of your application by adding the following statements inside these statements:
// Update timer value intTimer += 1; Int16 intTimeRemain = 15 - intTimer; stsLabel->Text = "Seconds remaining: "; stsLabel->Text = String::Concat( stsLabel, intTimeRemain.ToString() ); // Handle running out of time if( intTimer == 15 ) { intWrong += 1; tmrControl->Enabled = false; stsLabel->Text = ""; MessageBox::Show( String::Concat( "Strike ", intWrong.ToString(), " - Time is up! Please", " try again." ) ); // Reset game txtEntry->Text = ""; txtDisplay->Text = ""; btnDone->Enabled = false; btnGo->Enabled = true; txtEntry->ReadOnly = true; intTimer = 0: btnGo->Focus(); // Handle three strikes if( intWrong == 3 ) { // Inform player he's a beginner if( intCount < 2 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Beginner. " "Please play again!" ); intCount = 0; intWrong = 0; return; } // Inform player he's Intermediate if( intCount < 4 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Intermediate. " "Please play again!" ); intCount = 0; intWrong = 0; return; } // Inform player he's advanced if( intCount < 5 ) { MessageBox::Show( "Game Over! Your " "typing skill level is: Advanced. " "Please play again!" ); intCount = 0; intWrong = 0; return; } } // Player is an expert! if( intCount >= 5 ) { intCount = 0; intWrong = 0; MessageBox::Show( "Game Complete. Your " "typing skill level is: Expert! '" "Please play again!" ); } }
The first statement adds 1 to the variable used by the Timer control to track the number of seconds that the current play has lasted. The next helps keep a running count of how much time the player has left. After that, the sentence to become the status label is assigned its first value: Seconds remaining. With its value set, the next statement builds the text statement by adding different sentences. The result is shown in the Status Strip.
Each second that the game runs, the Timer control executes automatically. The statements following those related to the Status Strip statement check to see if 15 seconds have elapsed. If so, a strike is declared on the player by adding 1 to the variable used to track the number of failed attempts. The Timer control then disables itself, clears out any text displayed on the Status Strip, and uses the MessageBox::Show method to tell the player that time has expired.
The rest of the code that you added to the tmrControl control's click event determines whether the conditions have been met for game over. They are nearly identical to the ones you added to the btnDone control's click event.
Now it's time to test the application. Press F5 to launch it. If errors occur, go back and double-check the code statements that you added to make sure you did not make typos.