Adventures in GameDev with GameDevHQ! Day 25, Ease of building UI Elements in Unity.
After implementing the powerup mechanics, we are basically done with the game! But it’s not quite over yet, we still need to implement a few things before we can say it’s finished. Primarily the User Interface or UI. Sure, you’re blasting ships, but how many points are you making? How many ships do you have left? Where do you find this information? Unity has a special space for such information, it’s called the canvas and the canvas holds all of Unity's UI elements. From title screens to Start Buttons and Game Over screens. If there’s information to be shared, the Canvas is the place to put it!
So you want to make a UI for your game. How do you do it specifically? Just head on over to the hierarchy, right-click, and select create UI/Canvas. However, since we want to add a score to our game, we’ll add a Text object and Unity will automatically create the rest of the necessary UI items like Canvas for it.
Usually, we want the score to be placed in the upper right. We can anchor the text object by using the rect transform tool and telling it to anchor it there. then for whatever reason, the UI has to scale, for maybe a different screen on a mobile phone vs a monitor screen which may have a different resolution, it will scale appropriately.
Setting the font size to 40, changing it to white, and putting it in the upper right corner should do the trick. Connecting it to the game is very easy as well! Just create a script called UIManager and we’ll attach it to the Canvas and from there, we’ll create a space to attach the Score text.
You can then drag the Score_Text GameObject into the variable holder via the Unity interface:
We can now directly access this Text object! Let’s do that by initializing the score with 0.
_scoreText holds the actual Text object, but we still need to tell Unity to change the text so we add a “.text” to the variable and then send it the text you want to change via the equals sign. Why add .text? The Text object also contains other information like .font, .size, .Color. etc.
Let’s take a quick look at the Unity manual to see just how many components a Text object has:
That’s quite a bit! so If you’re wondering why you need to add .text to your Text object, it’s because it has many components under its hood, and you need to specify .text to get it actually running with text.
Ok, back to initializing _scoreText. We scripted:
_scoreText.text = “Score: “+0;
Let’s see what happens when we run the game:
just one more thing: I forgot to set the canvas scaling so let’s fix that.
Selecting the canvas, let’s look at the canvas scalar, it’s set to Constant Pixel Size.
Let’s change that to Scale with Screen Size and where it says Match, enter .5
.5 will keep all UI elements the same size on the screen no matter how you scale it.
Let’s see it in action:
But how do we actually get the score working in the game? Let’s say we want to assign 10 points to each enemy hit. Or better yet, let’s make a small public function in the Player that will add a score determined by the enemy object and update it. First, we’ll set up the actual function:
So who would access this function? The most obvious place would be from the enemy when it gets destroyed, so let’s go into the Enemy scripts OnTriggerEnter:
So the enemy is hit by a laser, the laser gets destroyed(other.gameObject), we access the player through a reference using GetComponent and call the AddScore function giving it 10 and finally, we destroy the enemy ship (this.gameObject) So let’s see the players AddScore code:
Pretty easy to follow: We’ve sent 10 points to AddScore, now that score is added to the main _score variable, and we send that main _score variable to the _uiManagers UpdateScore function which as you remember, all it does is send the score to the Score_Text object! Let’s watch it go!
We do a similar thing with the ship's lives. This time, we’ll put the three-ship lives in the upper left corner.
We do this by adding an image to the UI.
After adding the image to the canvas, we found the three lives ship graphic and placed it inside. It’s a little stretched so we ticked the preserve aspect button to make it widen out and make it look like it should.
In the UI script, we create another handle for it.
Next, we’ll create a sprite holder that we’ll be exchanging the Lives_img with. Because we have 4 different images for this graphic, one for 3 lives, 2 lives, 1 life, and no lives. we’ll create an array to hold the sprites.
First, let’s drag the Lives_img into the Lives Img field.
Great! Now we’ll be able to switch sprite images but first, let’s prepare the Lives_Sprites area. First type 4 since we’ll be dragging 4 images into it. You’ll see the Inspector prepare 4 spots for them:
Next, we’ll drag the lives graphics in. 3 lives go into element 3, 2 lives go into element 2, and you can guess the rest. You’ll see why the placement is important in just a moment.
Now we’ll create a function in the UIManager script called UpdateLives:
So let’s break this down:
the UpdateLives function is called and is sent an integer of how many lives are left. Let’s say the number is 1.
the image in the corners sprite is assigned a new sprite, which is an array, and as the ship graphic in the array matches the slot, when we assign that slot to the graphic, you’re going to have the correct amount of ships on screen!
So where would we call this function from? Probably from the player damage where he loses a life, so let’s do that.
I’ve commented out parts we went over before so we can just focus on the important bits. So the enemy damages us, if we don’t have any shields active, then we lose a life and we access the UIManager scripts UpdateLives and we send it our current amount of lives left. As an example, we’re playing a new game with 3 lives and we just lost one. We’re going to call the UpdateLives with 2 as the parameter sent.
so 2 is passed into the function via the currentLives variable, which means the UI sprite will be replaced with the _lives_Sprites which has, you guessed it…2 ships!
So let’s see this work of logical beauty in action!
I was surprised at how amazingly easy this implementation of a lives system turned out and at first, I was concerned about learning the UI system since I’ve heard it can be difficult, but it seems with the proper training, it’s pretty straightforward!
Tomorrow we’ll implement a cool retro-style flashing game over the screen using the same UI system.