Unity Dev Blog: Ease of Building UI Elements in Unity
From the outside looking in.
We’ve all seen how video games share information with the player through small elements placed near the screens edges out of the way. Just visible enough to be useful, but not so intrusive that they inhibit game play.
In Destiny 2 for example (my go to xBox game btw), you can see which weapon is active, it’s ammo count, elemental type if it has one, and the current availability of your “super”. Vital information indeed, but you do not want it to block your field of vision.
“How do we do this in Unity?” you ask. (Please ask so I do not feel silly. Doesn’t have to be very loud. Just whisper it if you are somewhere where people might look at you funny for talking out loud.)
I’m glad you asked! Unity makes it extremely easy to create a user interface (UI) or game HUD (heads-up-display). It is very much like putting a decal on your car’s windshield. While you drive, all sorts of events take place outside and within your field of vision, but the windshield (and hence the decal) are relatively fixed. You can glance at your “Plum Crazy” decal and giggle with glee at any time.
In Unity, we can create a Canvas to put our ‘decals’ on. For example, in the Space Shooter 2D game I think we need (at minimum) a UI with the following information: current number of lives, points/score, and settings/exit button. Some nice to haves might be while power-up you have and how much it is left before it expires. And eventually I’d like to have an ammo counter or laser temperature readout. We have LOTS of options!
So here is how this works. In the Hierarchy window, create a new UI/Canvas. **note that I renamed mine from Canvas to UI Canvas** Then add a UI/Text object. It defaults to the text New Text. I only changed the color for better visualization in the image above.
If this is your first time seeing a Canvas in the Unity editor… first off thanks for trusting this article enough to try to learn from it and second, notice how it is SO BIG compared to the background image asset in the Scene view. This is a little disorienting at first, but think of the big rectangular Canvas (looks like the outline of a gray rectangle) as being a decal on the backside of your monitor screen. Everything on the Canvas will stay where you put it (unless you move or hide it within your code). See how the text box is in both views?
Obviously it cannot stay in the center of the screen. Well, it could but that would be silly. So let’s move it and ‘anchor’ it to the upper left corner, set the font size, default text, etc., and voila…
That looks much better. Anchoring the text object to the top left corner will make sure it stays there even when the screen size/aspect ratio changes.
Also — and this is important — we want the text box to scale with the windows size. To do this, change the Canvas’ UI Scale Mode setting in the inspector to “Scale With Screen Size”. This way it does not matter what size screen the game is played on, Unity will scale the text box (and anything else we add to the Canvas) to match.
Now all we have to do is create the code to update the score text. Here we can create a UI manager to handle all of the UI elements we are planning to implement. But first, let’s add in a scoring system.
Here are the steps:
- Laser hits enemy.
- Enemy explodes (I promise the explosion animations are coming!)
- Player gets +10 points and updates score display.
Easy enough. However, the enemy script needs to communicate with the player script to let them know that can add that 10 points to the score. In the Start() function of the enemy script we can go ahead and grab a reference to the Player so that it only needs to happen one time.
Now, when the collision with the Laser is triggered, we can call the function in the player script to change the score. Let’s send it to the console for testing…
Awesome! That seems to work perfectly. Not let’s make it update the UI Score text. First we’ll set it to zero. Each time the score changes, the text needs to be updated. Unity does not know to do this if we do not create the code to do it.
For the player script to communicate with the UIManager script, we need to get a reference to it (remember it is always best to null check):
(from player start function)
Now instead of getting a message in the console, we can ask the UIManager to update the score by passing the new score to the function in the other script:
(from player script)
(from the UIManager script)
Let’s see if that did the trick…
Bingo, bango, boingo! Works as planned. We get 10 points for each enemy shot we shoot. If you watch closely, I destroyed and enemy by colliding with them and do not receive points. We are only making the call if the game object with the tag of “Laser” collides with the enemy.
Next we need to create a “YOU DIED” / GAME OVER screen. (Yes. now I’m purposefully putting off the explosion animations in an attempt to get you to keep coming back for more. Also, I promise to pick a layout and color combo for the little code snippets and stick to it. Do you have a preference? If so let me know! So join me tomorrow for more work on the user interface.
See you there!