Basic UI Elements in Unity.
Objective: Use of the Unity UI to project game data for the player.
UI stands for User Interface. It is an overlay of objects which display information to the Player (such as health, ammo count, score, and lives) or allows the Player to interact with the game by selecting defined objects (menus and options). It is very easy and intuitive to use in Unity.
In this article, I will show you how to set up a score UI element, and then we’ll use the UI Manager to update the score display when enemy objects are destroyed.
Of note, this article builds on my current 2D game project that I’m creating under the guidance of GameDevHQ.
Let’s begin by creating a UI Canvas. Select Create, UI, Text. This will add a Canvas that stores all UI elements such as the Text we just created and allows us to visually organize the data we wish to present to the Player.
The example above uses the UI Text. I’ve opted to use Text — TextMeshPro. By selecting Text — TextMeshPro (TMP), it adds flexibility, gives us more defined details, and makes it easier to customize and upgrade our fonts. Doing so will prompt you to import TMP elements.
Creating the UI Text will also create an EventSystem which is what allows us to interact with the UI such as hovering over objects, pushing buttons, etc. Without the EventSystem, you can’t interact with the UI objects, so don’t remove it from your Scene.
By zooming out in your Scene window, you will see a large white rectangle. This is your UI Canvas, and within that canvas are stored and organized all of your UI elements.
Rename the new text component in your Canvas as Score_text. The “_text” convention will allow you to quickly identify the object as a text component, and you should a similar process when creating other UI elements. Adjust the font color and size to your liking. In my case, I’ve set the color to white and the size to 20.
You then have these anchors which allow you to position your UI elements within the Game view, where they will remain and scale properly depending on which platform your game is being played with. So with the Score_text selected, click on the Anchor Presets box, select Top Right location, adjust the UI elements in your Scene view, then adjust the padding of your Pos X and Pos Y to -100 and -50. Once this is set, no matter how you shrink your Game view, the Score_text UI element will remain anchored and visible in the top right corner. Finally, change your Score_text to read SCORE: 00000 (or SCORE: 0 if you prefer).
To ensure your text scales with the screen size, select the Canvas in your Hierarchy, go to the Canvas Scaler (Script) in the Inspector, change the UI Scale Mode to Scale With Screen Size. This will keep everything in proportion on different platforms.
The UI Manager is responsible to update all things relevant to the UI or on-screen display. It is simply an empty game object to which we attach a script that will in turn control all the behavior that will update the UI when our code tells it to.
To create the UI Manager, create an empty game object, reset its position to (0,0,0), name it UI_Manager, then create a new C# script called UIManager and attach it to the UI_Manager.
Another option is to NOT create a UI Manager game object, but rather simply attach the UIManager C# script to the Canvas, since all of the UI is managed within the Canvas. Once this is done, open the script for editing.
This is the option I took, which is why later in the article, I will be looking on the Canvas, and not the UI_Manager for the UIManager script component.
It’s important to note that in order to use and modify UI elements, your UIManager script needs to add using Unityengine.UI, and if using TextMeshPro, you also need to add using TMPro.
Let’s start thinking about our logic to update the score. What is “Score”? It’s an integer value. Who does it belong to? The PlayerScript. So let’s begin by adding a private variable of type int to the PlayerScript and call it _score. Make it [SerializeField] in order to view it in the Inspector.
Next step is to figure out when will we add to our score? It should happen when you destroy a enemy game object with a laser shot from the Player in your game. In my case, this collision between the enemy game object and the Player laser shot is detected in the Enemy script. Below is the method which, upon detection of a collision between the enemy ship and an object with the tag “LaserPlayer”, destroys both the laser shot and the enemy ship.
When the enemy is destroyed, we need to communicate between scripts and call a function in the PlayerScript to add a value to the private int _score variable. Once this is done, we then need to communicate with the UIManager script and instruct it to modify the Score_text to reflect the updated score.
There are a few things we need to do. Let’s start with creating a method to increment our score. Since I’m thinking of potential expansion, I don’t want to hard code a number into the function, so in the PlayerScript, I added the following:
This will take whatever the value of “points” is (based on the type of enemy destroyed), and will add it to the _score value.
So far so good. In the Enemy script, we need to declare a private reference to the PlayerScript. (see below)
Then, in the void Start(), we need to find that reference to the Player object, and get the PlayerScript component of the Player. This is followed by a null check. (see below)
Finally, in the process of detecting the collisions, we do another null check, and if not null, we run the AddScore() with the int points set to a value of 10 (this is the hard-coded point value of this particular enemy game object). The changes to the Enemy script should look like this:
Now, at this point in the game, you should be detecting the collisions between your Player laser shot and the enemy ships, and every time a collision occurs, your _score value should be incrementing by 10 points.
We are now ready to update the UI with this information and project a visible score tally to the user. The first thing to do is set up communication between the PlayerScript and the UIManager script in order to send the updated score to the UI Text (or UI TextMeshPro).
We then need to return into the PlayerScript and amend our AddScore method to include that reference to the UI Manager, call its UpdateScore method, and replace the UI Score_text data with the latest interger held in the _score variable.
The final step is to finish the code in the UIManager script. Create a private Text (or TMP_Text) and call it _scoreText. Make it a [SerializeField] so you may drop the Score_text from the Canvas into the _scoreText field in the Inspector.
In the Start(), set the initial value of _scoreText.text to 0. This will ensure that when the game begins, the UI Score_text is initialized with a value of 0. You then need to create the public void UpdateScore(int playerScore) method. You need to understand here that the (int playerScore) in the UIManager script gets replaced by the (_score) value from the AddScore() from the PlayerScript (AddScore was fetching (int points) from the Enemy script).
Run the game to see the results:
There you have it. A little lengthy but hopefully some will find it useful. If I’ve managed to royally screw you up, feel free to reach out to me for clarification. Thanks for reading :)