Last part for highscores – A method to allow players who have achieved a new high score to enter their names. This probably should have been a simple screen containing two elements an input field and a enter button. However, for whatever reason I decided to make things a little more complex – Adding a virtual keyboard.
The front end for the virtual keyboard is based on the example provided in the Unity Samples: UI package – The back end I put together myself – With the bulk of the code for this section setup for managing user input. It can currently be controlled either by mouse or keyboard – Expanding to allow controller input ‘should’ be fairly straight forward.
The player doesn’t need to use the virtual keyboard to enter their name – They can always click on the input field and type their names directly – So there’s also a little code to ensure that only valid characters are accepted and that the maximum length is not exceeded should this approach be used.
To try and explain how I’ve put this together, I’ve split the description into two sections;
- Front End: GUI.
- Back End: Code
1. Front End: GUI
HighscoreAdd – an empty component which groups together all the new highscore UI elements.
HighScoreTextTop
A text component, used as the title of the screen
Window – Contains the input field and keyboard keys – The bulk of new highscore components.
Inputfield
Displays the players name. Contents can be filled either by using the keyboard keys or by direct input. I set the character limit to 15 so that highscore names fit nicely into the highscore table. Although I only want to allow the player to enter alphanumeric I have the input type set as standard – Managing the input in code- the method InputFieldUpdate is called ‘on value changed‘ which checks the character types, only accepting valid characters.’One Edit End‘ calls a second script method ClickDone – This allows the player to press enter after tying in their name to add the new high score – rather than forcing them to click the done button.
Vertical Group – Is the start of the on-screen keyboard. It’s a component that itself just contains a Vertical Layout Group (script) and has as a child the Grid Component.
Grid – Another component setup primarily to manage the shape of the keyboard, is has two layout components attached Grid Layout Group (script) and LayoutElement (script). As children it has 40 buttons – one for each key.
GridCell-X – Forty of these, one for each key. They are setup in pretty much exactly the same way as all the other buttons in the game. An Animator shrinks the button slightly when clicked, and audioSource & event trigger produce a click sound when the key is selected and a relevant method is called On Click – ClickAlphanumeric method is called for the alphanumeric keys, with individual methods for the space (clickSpace), delete (ClickDelete), caps (ClickCaps) and enter (ClickDone) keys.
2. Back End: Code
The backbone of the keyboard is a script called HighscoreAdd. This is primarily a functional script, primarily containing methods for handling key presses and ensuring only acceptable (Alphanumeric) values are used for the highscore name.
If the player clicks on an alphanumeric keyboard key, it calls the ClickAlphanumeric method.
public void ClickAlphaNumeric(string inValue) { if (inputField.text.Length < inputField.characterLimit) inputField.text += inValue; UpdateIndex(inValue); }
This method first checks to see if the input has reached maximum allowed characters as set in the input field, if there’s space, it adds the new character. Before calling UpdateIndex – a method which changes the colour of the button selected – To make the keyboard feel a little more interactive.
public void ClickSpace(Text inValue) { if (inputField.text.Length < inputField.characterLimit) inputField.text += " "; UpdateIndex(inValue.text); }
ClickDelete removes the last character entered by assigning the players name text with a substring of itself, starting from position 0 and ending at length-1;
public void ClickDelete(Text inValue) { if (inputField.text.Length > 0) inputField.text = inputField.text.Substring(0, inputField.text.Length - 1); UpdateIndex(inValue.text); }
ClickCaps switches characters between upper and lower case. When the player adds a letter by clicking a button, it adds the value of the buttons text/label to the players name. So when switching between upper and lower case, this method loops through each alphabetic button in turn and sets its label to an upper or lower case version of itself. This has the added effect of visually displaying the current case. I also change the colour of the caps button to indicate it’s state – currently red for uppercase and blue for lower case.
public void ClickCaps(Text inValue) { useCaps = !useCaps; UpdateIndex(inValue.text); if (useCaps) { lastColor = Color.red; for (int counter = 0; counter < buttons.Length; counter++) buttons[counter].GetComponentInChildren().text = buttons[counter].GetComponentInChildren().text.ToUpper(); } else { lastColor = Color.blue; for (int counter = 0; counter < buttons.Length; counter++) buttons[counter].GetComponentInChildren().text = buttons[counter].GetComponentInChildren().text.ToLower(); } }
ClickDone is run when either the DONE button is clicked or the player presses the return key while the input field is focused. This function first checks to see if the player has added a name – If there is no value, or only spaces have been added the generic player name ‘A Nony Moose’ is used instead. Once a player name is set, it passes it to the SetHighScore method in the gameController script to be added to the list of highscores.
public void ClickDone(Text inValue) { //--- GameObject gameControllerObject = GameObject.FindGameObjectWithTag("GameController"); if (gameControllerObject != null) { GameController gameController = gameControllerObject.GetComponent(); if (inputField.text == "" || OnlySpaces(inputField.text)) inputField.text = "A Nony Moose"; gameController.SetHighScore(inputField.text); } //--- }
UpdateIndex method is called by all Button functions to ensure the game knows which was the last button pressed, and to update the button highlight appropriately (By calling UpdateButtonIndex);
void UpdateIndex(string inValue) { for (int counter = 0; counter < buttons.Length; counter++) if (inValue.ToUpper() == buttons[counter].GetComponentInChildren().text.ToUpper()) { index = counter; UpdateButtonIndex(); break; } }
UpdateButtonIndex stores the index value of the last button clicked. It also stores the last colour of the button (its unpressed colour), before changing it’s current colour to show its selection (currently grey) – The reason the last colour is stored, despite the majority of keys being white is because the caps and done keys have their own colours – We need to ensure they are correctly returned to these colours once these keys are correctly deselected;
void UpdateButtonIndex() { if (index = buttons.Length) index = buttons.Length - 1; //if (index != lastIndex) { buttons[lastIndex].image.color = lastColor; lastColor = buttons[index].image.color; buttons[index].image.color = Color.grey; lastIndex = index; } }
InputCheck, called from the update method, runs every loop. It allows the keyboard to be controller via the arrow and enter keys. Arrows to move around the keyboard – Enter to select the key and add the character to the players name. This input method is only valid if the input field isn’t currently selected – This is because the input field runs the ClickDone method if the return key is pressed;
bool lastFocused = false; public void InputCheck(bool checkReturn) { if (inputField.isFocused) { } else if (Input.GetKeyDown(KeyCode.LeftArrow)) { if (index >= 0 && index % columnLength > 0) { index--; UpdateButtonIndex(); } } else if (Input.GetKeyDown(KeyCode.RightArrow)) { if (index == -1 || (index + 1) % columnLength > 0) { index++; UpdateButtonIndex(); } } else if (Input.GetKeyDown(KeyCode.UpArrow)) { if (index >= columnLength) { index -= columnLength; UpdateButtonIndex(); } } else if (Input.GetKeyDown(KeyCode.DownArrow)) { if (index != -1 && index + 1 <= buttons.Length - columnLength) { index += columnLength; UpdateButtonIndex(); } } else if ((Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space))) { if (index = buttons.Length) index = buttons.Length - 1; lastIndex = index; if (!checkReturn) { var pointer = new PointerEventData(EventSystem.current); ExecuteEvents.Execute(buttons[index].gameObject, pointer, ExecuteEvents.submitHandler); buttons[index].GetComponentInChildren().Play(); } } }
Finally; the InputFieldMethod, run every time a character is added by the player directly into the inputfield (by clicking on the inputfield component and typing using the computers keyboard, rather than the virtual keyboard). It checks to see if the last character added character is valid (alphanumeric or a space) – If it isn’t, it deletes the character, if it is it returns without making any changes;
public void InputFieldUpdate() { if (inputField.text.Length > 0) { char chr = inputField.text[inputField.text.Length - 1]; if ((chr 'z') && (chr 'Z') && (chr '9') && chr != ' ') { inputField.text = inputField.text.Substring(0, inputField.text.Length - 1); } } }
Grab a copy of the project here
Next post: 1.8 Obstacles
Last post: 1.7d Highscores (Display)
2 Replies to “Adventures in Unity – 1.7e Highscores (Add)”