In this section, we will create a word guessing game with Unity, with the following features:
- A word will be picked at random from an existing list.
- The letters of the word will be hidden.
- The players will try to guess each letter by pressing a letter on their keyboard.
- Once a letter has been discovered it will then be displayed onscreen.
- The player has a limited number of attempts to guess the word.
So, after completing this tutorial, you will be able to:
- Read words from a text file.
- Pick a random word.
- Process and assess the letters pressed by the player.
- Display the letters that were correctly guessed by the player.
- Track and display the score.
- Check when the player has used too many guesses.
Figure 1: The final game
Creating the interface for the game
So, in this section, we will start to create the core of the word guessing game; it will consist of text fields initially blank, and located in the middle of the screen.
So, let’s get started:
- Please launch Unity and create a new Project (File | New Project).
Figure 2: Creating a new project
- In the new window, you can specify the name of your project, its location, as well as the 2D mode (as this game will be in 2D).
Figure 3: Specifying the name and location for your project
- Once this is done, you can click on the button called Create project (located at the bottom of the window) and a new project should open.
- Once this is done, you can check that the 2D mode is activated, based on the 2D logo located in the top right-corner of the Scene view, as illustrated in the next figure.
Figure 4: Activating the 2D mode
First, we will remove the background image for our Scene. If you look at your Game view, it may look like the following figure.
Figure 5: The initial background
If it is the case, then please do the following:
- From the top menu, select: Window | Lighting.
- Then delete the Default Skybox that is set for the attribute called SkyBox (i.e., click on the attribute to the right of the label Skybox and press DELETE on your keyboard).
Figure 6: Lighting properties
- Once this is done, your Game view should look like the following.
Figure 7: The Game view after deleting the SkyBox
We will now create a text field that will be used for the letters to be guessed.
- From the top menu, please select GameObject | UI | Text. This will create a UI Text object called text, along with a Canvas object.
- Please rename this text object letter.
Figure 8: Creating a new letter
Select this object (i.e., letter) in the Hierarchy, and using the Inspector window, please set its attributes as follows:
- For the component Rect Transform: Position = (0,0,0); Width = 100 and Height = 100.
- For the component Text: Font-size = 80; Color = white; Text = please empty the text.
- For the component Text: vertical alignment = center; horizontal alignment = middle.
Once this is done, we will create a prefab from this object, so that we can instantiate it later on (i.e., create objects based on this prefab).
- Please drag and drop the object letter from the Hierarchy window to the Project window.
- This will create a new prefab called letter.
Next, we will create a gameManager object; this object will be in charge of setting the layout for the game and processing the user’s entries; in other words, it will be responsible for running and managing the game.
- Please create a new empty object (GameObject | Create Empty).
- Rename this new object gameManager.
Next, we will create a script that will be attached to the gameManager object; this script will be in charge of running the game (e.g., displaying letters, processing user inputs, etc.).
- From the Project window, select Create | C# Script.
- Rename the new script GameManager.
- You can then open this script.
In this script, we will display several letters in the middle of the screen.
- Please add this code at the beginning of the class.
public GameObject letter;
In the previous code we create a new public variable called letter; it will be accessible from the Inspector since it is public, and it will be set (or initialized) with the letter prefab that we have created earlier. This variable will be used to generate new letters based on that template (i.e., prefab).
- Please check that the code is error free in the Console window.
- Drag and drop the script GameManager from the Project window on the object called gameManager located in the Hierarchy.
Figure 9: Adding a script to the game manager
- Once this is done, you can select the object called gameManager in the Hierarchy.
- Using the Inspector window, you will see this object now includes a new component called GameManager with an empty field called letter.
Figure 10: A new component added to the game manager
- Please drag and drop the prefab called letter from the Project window to this empty field in the Inspector window, as described in the next figure.
Figure 11: Initializing the letter variable with a prefab
Once this is done, we can start to write a function that will create the new letters.
- Please open the script GameManager.
- Add the following code at the end of the class (just after the function Update).
void initLetters() { int nbletters = 5; for (int i = 0; i < nbletters; i++) { Vector3 newPosition; newPosition = new Vector3 (transform.position.x + (i * 100), transform.position.y, transform.position.z); GameObject l = (GameObject)Instantiate (letter, newPosition, Quaternion.identity); l.name = "letter" + (i + 1); l.transform.SetParent(GameObject.Find ("Canvas").transform); } }
In the previous code:
- We define that we will display five letters using the variable nbLetters; this number is arbitrary, for the time being, so that we can ensure that we can display letters onscreen; this number of letters will, of course, vary later on, based on the length of the word to be guessed.
- We then create a loop that will loop five times (once for each letter).
- In each iteration, we define the position of the new letter using the variable newPosition.
- This position of the letter is calculated by combining the position of the object gameManager that is linked to this script (i.e., transform.position) plus the size of the letter (i.e., 100; this size/width was set-up earlier-on with the Inspector using the width attribute) multiplied by the variable i; so the position of the first letter on the x-axis will be transform.position + 0, the second one will be at the x position transform.position + 100, and so on.
- We instantiate a letter and also set its name.
- Finally, we set the parent of this new object to be the object called Canvas; this is because, as a UI Text object, this object needs to be associated to a canvas in order to be displayed onscreen; this is usually done by default as you create a new UI Text object with the editor in Unity; however, this needs to be done manually as this object is created and added to the Scene from a script.
Finally, please add the following code to the Start function.
initLetters();
As you play the Scene, you should see that new letters have been created in the Hierarchy.
Figure 12: The newly-created letters
If you double-click on one of them (e.g., letter1) in the Hierarchy, you should see where they are located and their layout in the Scene view.
Figure 13: The layout of the letters
Now, because the position of the letters is based on the position of the game manager, you may notice, as for the previous figure, that the letters are not centered properly. So we need to ensure that these letters are properly aligned vertically and horizontally. For this purpose, we will do the following:
- Create an empty text object located in the middle of the screen.
- Base the position of each letter on this object.
- Ensure that all letters are now properly aligned.
So let’s proceed:
- Please create a new UI Text object (GameObject | UI | Text) and rename it centerOfScreen.
- Select this object in the Hierarchy.
- Using the Inspector, in the component called RectTransform, change its position to PosX=0 and PosY=0; you can leave the other attributes as they are.
Figure 14: Changing the position attributes
Because this object is a UI object, setting its position to (0,0) will guarantee that it will be displayed in the center of the screen; this is because the coordinate of the UI object (for the component RectTransform) are based on the view/camera. So PosX=0 and PosY=0, in this case, corresponds to the center of the screen; using an empty object would have been different as the coordinates would be world coordinates and not related to the screen/view.
- Using the Inspector, in the component called Text, delete the default text, so that this UI Text is effectively an empty field.
Once this is done, we can modify the code in the script GameManager to center our letters.
- Please open the script GameManager.
- Add the following code to the start of the class (new code in bold).
public GameObject letter;
public GameObject cen;
void Start () { cen = GameObject.Find ("centerOfScreen"); initLetters (); }
- In the previous code, we declare a new variable called cen that will be used to refer to the object centerOfScreen.
- In the function initLetter that we have created earlier-on, please modify this line
newPosition = new Vector3 (transform.position.x + (i * 100), transform.position.y, transform.position.z);
… with this code…
newPosition = new Vector3 (cen.transform.position.x + (i * 100), cen.transform.position.y, cen.transform.position.z);
In the previous code, we mow base the position of our letter on the center of the screen.
- Please save your script, and check that it is error-free.
- Play the Scene, and you should see, in the Scene view, that the letters are now aligned vertically; however, they are slightly offset horizontally.
Figure 15: Aligning the letters
So there is a last change that we can include in our script, so that each letter is centered around the center of the screen; this will consist in offsetting the position of each letter based on the center of the screen as follows, so that the middle of the word matches with the center of the screen.
- Please open the script GameManager and, in the function called initLetter, replace this line:
newPosition = new Vector3 (cen.transform.position.x + (i * 100), cen.transform.position.y, cen.transform.position.z);
with this line…
newPosition = new Vector3 (cen.transform.position.x + ((i-nbletters/2.0f) *100), cen.transform.position.y, cen.transform.position.z);
In the previous code, we offset the position of each letter based on the center of the screen; so the x-coordinate of the first letter will be -250, the x-coordinate of the second letter will be -150, and so on.
You can save your script, play the Scene, and look at the Scene view; you should see that the letters are now properly aligned, as illustrated on the next figure.
Figure 16: The letters are now properly aligned
If you would like to see what the letters would look like, you can, while the game is playing, you can select each newly-created letter in the Hierarchy and modify its Text attribute in the Inspector (using the component Text), as illustrated in the next figure.
Figure 17: Changing the text of each letter at run-time
Detecting and processing user input
Perfect. So at this stage, we have a basic interface for our game, and we can display letters onscreen. So, in this section we will implement the main features of the game, that is:
- Create a new word to be guessed.
- Count the number of letters in this word.
- Display corresponding empty text fields.
- Wait for the user to press a key (i.e., a letter) on the keyboard.
- Detect the key that the user has pressed.
- Display the corresponding letters in the word to be guessed onscreen.
So let’s start.
- Please open the script called GameManager.
- Add the following code at the start of the class (new code in bold).
public GameObject letter; public GameObject cen; private string wordToGuess = ""; private int lengthOfWordToGuess; char [] lettersToGuess; bool [] lettersGuessed;
In the previous code:
- We declare four new variables.
- wordToGuess will be used to store the word to be guessed.
- lengthOfWordToBeGuessed will store the number of letters in this word.
- lettersToGuess is an array of char (i.e., characters) including every single letter from the word to be discovered by the player.
- letterGuessed is an array of Boolean variables used to determine which of the letters in the word to guess were actually guessed correctly by the player.
Next, we will create a function that will be used to initialize the game.
- Please add the following function to the class.
void initGame() { wordToGuess = "Elephant"; lengthOfWordToGuess = wordToGuess.Length; wordToGuess = wordToGuess.ToUpper (); lettersToGuess = new char[lengthOfWordToGuess]; lettersGuessed = new bool [lengthOfWordToGuess]; lettersToGuess = wordToGuess.ToCharArray (); }
In the previous code:
- We declare a function called initGame.
- In this function, we initialize the variable wordToGuess; this will be the word Elephant for the time being.
- We then capitalize all letters in this word; as we will see later in this chapter, this will make it easier to match the letter typed by the user (which usually is upper-case) and the letters in the word to guess.
- We then initialize the array called lettersToGuess and lettersGuessed.
Note that for Boolean variables, the default values, if they have not been initialized, is false. As a result, all variables in the array called lettersGuessed will initially be set to false (by default).
- Finally, we initialize the array called lettersToGuess so that each character within corresponds to the letters in the word to guess; for this, we convert the word to guess to an array of characters, which is then saved into the array called lettersToGuess.
Once this function has been created, we will need to process the user’s input; for this purpose, we will create a function that will do the following:
- Detect the letter that was pressed by the player on the keyboard.
- Check if this letter is part of the word to guess.
- In this case, check if this letter has not already been guessed by the player.
- In this case, display the corresponding letter onscreen.
Let’s write the corresponding code.
- Please add the following function to the script GameManager:
void checkKeyboard() { if (Input.GetKeyDown(KeyCode.A)) { for (int i=0; i < lengthOfWordToGuess; i++) { if (!lettersGuessed [i]) { if (lettersToGuess [i] == 'A') { lettersGuessed [i] = true; GameObject.Find("letter"+(i+1)).GetComponent().text = "A"; } } } } }
In the previous code:
- We declare the function called checkKeyBoard.
- We then create a loop that goes through all the letters of the word to be guessed; this is done from the first letter (i.e., at the index 0) to the last one.
- We check if this letter has already been guessed.
- If it is not the case, we check if this letter is A.
- If this is the case, we then indicate that this letter (i.e., the letter A) was found.
- We then display the corresponding letter onscreen.
Last but not least, we just need to be able to call these two functions to initialize the game and to also process the user’s inputs.
- Please add the following code to the Start function (new code in bold).
void Start () { cen = GameObject.Find ("centerOfScreen"); initGame (); initLetters ();
}
Please make sure that the function initGame is called before initLetters (as illustrated in the previous code) in the Start function; this is because, as we will see later, the function initLetters will use some of the information that has been set in the function initGame (i.e., the number of letters). So in order for our game to work correctly, the function initGame should be called before initLetters.
- Please add the following code to the Update function.
void Update () { checkKeyboard (); }
The last change we need to add now is linked to the number of letters to be displayed; as it is, the number is set to 5 by default; however, we need to change this in the function called initLetters, so that the number of UI Text objects that corresponds to the letters in the word to be guessed reflects the length of the word that we have just created.
- Please modify the function initLetters as follows (new code in bold).
void initLetters() { <b>int nbletters = lengthOfWordToGuess;</b>
So at this stage, we have all the necessary functions to start our game; so you can save the script, check that it is error free and play the Scene. As you play the Scene, if you press the A key on the keyboard, the letter A should also be displayed onscreen, as it is part of the word Elephant.
Figure 18: Detecting the key pressed
So, this is working properly, and we could easily add more code to detect the other keys; this would involve using the code included in the function checkKeyboard, and copying/pasting it 25 times to be able to detect the other 25 keys/letters, using the syntax Input.GetKeyDown, once for each key. So the code could look as follows:
if (Input.GetKeyDown(KeyCode.A)) { ... } if (Input.GetKeyDown(KeyCode.B)) { ... } if (Input.GetKeyDown(KeyCode.Z)) { ... }
Now, this would be working perfectly, however, this would also involve a lot of repetitions (copying 24 times the same code); so to make the code more efficient, we will use a slightly different way of detecting the key pressed by the player. This method will involve the following:
- Check if a key was pressed.
- Check if this key is a letter.
- Process as previously to check whether this letter is part of the word to be guessed.
Please create a new function called checkKeyBoard2, as follows:
void checkKeyboard2() { if (Input.anyKeyDown) { char letterPressed = Input.inputString.ToCharArray () [0]; int letterPressedAsInt = System.Convert.ToInt32 (letterPressed); if (letterPressedAsInt >= 97 && letterPressed <= 122) { for (int i=0; i < lengthOfWordToGuess; i++) { if (!lettersGuessed [i]) { letterPressed = System.Char.ToUpper (letterPressed); if (lettersToGuess [i] == letterPressed) { lettersGuessed [i] = true; GameObject.Find("letter"+(i+1)).GetComponent().text = letterPressed.ToString(); } } } } } }
In the previous code:
- We detect whether a key has been pressed using the keyword Input.anykeyDown.
- If this is the case, we save the key (i.e., the letter) that was pressed into the variable called letterPressed. For this, given that any key pressed on the keyboard is stored as a string, we need to convert this string value to a character; the character recorded in the variable letterPressed will effectively be the first character of the string that corresponds to the key pressed by the player.
- Once this is done, we convert the letter pressed (i.e., character) to an integer value.
- We then check, using the integer value associated with the key pressed, that the key is a letter; this corresponds to an integer value between 97 and 122.
- Once this final check is complete, we do the exact same as we have done earlier in the function checkBoard that we have created previously (i.e., this is the same code).
The last thing we need to do is to call the function chekBoard2 instead of the function checkBoard by amending the Update function as follows (new code in bold):
void Update () { <b> //checkKeyboard ();</b> <b> checkKeyboard2 ();</b> }
That’s it!
Once this is done, please save your code, check that it is error-free and test the Scene. As you press the keys E, L, P and A, you should see that they now appear onscreen.
Figure 19: Detecting all the keys pressed
Choosing random words
At this stage, the game works properly and the letters that the player has guessed are displayed onscreen; this being said, it would be great to add more challenge by selecting the word to guess at random from a list of pre-defined words. So in the next section, we will learn to do just that; we will start by choosing a word from an array, and then from a text file that you will be able to update yourself without any additional coding.
So let’s get started.
- Please open the script GameManager.
- Add the following code at the beginning of the class.
private string [] wordsToGuess = new string [] {"car", "elephant","autocar" };
- In the previous code, we declare an array of string variables and we add three words to it.
- Add the following code to the function initGame (new code in bold).
//wordToGuess = "Elephant"; int randomNumber = Random.Range (0, wordsToGuess.Length - 1); wordToGuess = wordsToGuess [randomNumber];
In the previous code:
- We comment the previous code.
- We create a random number that will range from 0 to the length of the array – 1. So in our case, because we have three elements in this array, this random number will range from 0 to 2 (i.e., from 0 to 3-1).
- We then set the variable called wordToGuess to one of the words included in the array called wordsToGuess; this word will be picked at random based on the variable called randomNumber.
You can now save you script, and check that it is error-free.
There is a last thing that we could do; because the word is chosen at random, the player will not have any idea of the length of this word; so to give an indication of the number of letters to be guessed, we could display questions marks, for all the letters to be guessed, onscreen as follows:
- Please select the prefab called letter from the Project window.
- Using the Inspector, and the component called Text change its text to ?, as described in the next figure.
Figure 20: Changing the default character for letters
- Lastly, we can also deactivate the object called letter that is in the Hierarchy.
Figure 21: Deactivating the letter object
You can now play the Scene, and you should see questions marks where letters need to be guessed.
Figure 22: Displaying question marks
Want to learn more about Puzzle Games: try this!