Chapter 9: Creating a 2D Shop Interface and In-Game HUD

In this chapter, we will be paying attention to our shop interface and how we can improve it visually, as well as its functionality. The current shop works well, but we could make it support multiple screen ratios. We could also introduce Unity's Event system and Button components, as well as a few other new functionalities.

The other area we will be visiting in this chapter is the in-game Heads-Up Display (HUD). This is fairly common in games where we have the game's information displayed at a particular location on the screen. We will be displaying our player's lives and score and a mini-map to show where our enemies are. This can be seen in the following screenshot:

Figure 9.1 – Our in-game HUD

Figure 9.1 – Our in-game HUD

The other half of this chapter will be about improving the 2D visuals of our shop scene so that there are choices in terms of the upgrades we can buy and so that we can also expand the size of the shop dynamically. Also, your shop scene will support any landscape ratio, unlike what it did previously. The following screenshot shows what our shop looks like in different ratio sizes:

Figure 9.2 – The left side displays all buttons; the right side cuts off the button edges

Figure 9.2 – The left side displays all buttons; the right side cuts off the button edges

In the previous screenshot, notice that the 3:2 screen ratio cuts off some of the screen (you will especially notice this from each screen's selection grid spacing) compared to our 1920 x 1080 (16:9) screen ratio. By the end of this chapter, our shop scene will look like the one shown in the following screenshot, no matter what landscape ratio our game is in:

Figure 9.3 – Our in-game shop with a flexible UI display (no cutoff)

Figure 9.3 – Our in-game shop with a flexible UI display (no cutoff)

In this chapter, we will cover the following topics:

  • Setting up our HUD
  • Making our shop scene support alternative screen ratios
  • Applying and modifying our shop scripts

Let's start by reviewing the core exam skills that will be covered in this chapter.

Core exam skills covered in this chapter

The following are the core exam skills that will be covered in this chapter:

  • Working in the art pipeline:
    • Understand materials, textures, and shaders, and write scripts that interact with Unity's rendering API.
  • Developing application systems:
    • Interpret scripts for application interface flow such as menu systems, UI navigation, and application settings.
    • Interpret scripts for user-controlled customization such as character creators, inventories, storefronts, and in-app purchases.
    • Analyze scripts for user progression features such as scoring, leveling, and in-game economies utilizing technologies such as Unity Analytics and PlayerPrefs.
    • Analyze scripts for 2D overlays, such as HUDs, minimaps, and advertisements.
    • Identify scripts for saving and retrieving application and user data.
  • Programming for scene and environment design:
    • Identify methods for implementing Game Object instantiation, destruction, and management.
  • Optimizing for performance and platforms:
    • Identify optimizations to address requirements for specific build platforms and/or hardware configurations.
    • Determine common UI affordances and optimizations for XR platforms.
  • Working in professional software development teams:
    • Recognize techniques for structuring scripts for modularity, readability, and reusability.

Technical requirements

The project content for this chapter can be found at https://github.com/PacktPublishing/Unity-Certified-Programmer-Exam-Guide-Second-Edition/tree/main/Chapter_09

You can download the entirety of each chapter's project files at https://github.com/PacktPublishing/Unity-Certified-Programmer-Exam-Guide-Second-Edition.

All the content for this chapter is held in this chapter's unitypackage file, including a Complete folder that holds all of the work we'll carry out in this chapter.

Check out the following video to see the Code in Action: https://bit.ly/3LsxDWC.

Setting up our HUD

With side-scrolling shooter games, it is common for us to have some form of recording of how many lives the player has, what their score is, a time limit, power-ups, and more. We are going to apply a typical HUD to show a similar set of information. Knowing about HUDs is a requirement of the Unity Programmer Exam.

By the end of this section, we will have created a HUD for our game that will consist of the following:

  • Lives
  • Mini-map
  • Score

Before we add our HUD, we need to decide where it will sit on top of our game screen. As an example, we will pick a game so that we can briefly study how its HUD information is displayed.

We will be looking at a game called Super R-Type, which can be found at https://github.com/PacktPublishing/Unity-Certified-Programmer-Exam-Guide-Second-Edition/blob/main/Chapter_09/superRtype.jpg. Here, at the bottom of the screen, we can see that its HUD is made up of four parts, as follows:

  • Skill level
  • Lives
  • Power bar
  • Score

Behind these details is a black background so that the scene doesn't interfere when it comes to reading the HUD.

So, in this section, we'll start by declaring the HUD space and giving it a dark background. To do this, follow these instructions:

  1. In the Unity Editor, navigate to Assets/Scene in the Project window.
  2. Open the level1 scene.
  3. With level1 loaded, go to the Hierarchy window, right-click on the Canvas game object, and select UI | Image.
  4. A game object called Image will appear in the Hierarchy window as a child of the Canvas game object.

From the previous chapter, we should know that a game object containing an Image component must be a child of Canvas.

  1. Right-click the Image game object and select Rename from the dropdown. Name the game object background.

So far, we have created a game object that holds an Image component.

Now, let's move on and scale this game object into its correct placeplace so that it can be used as a background for our HUD. Follow these steps:

  1. With our background game object still selected, alter the Rect Transform settings in the Inspector window to the following:

t

Figure 9.4 – background Rect Transform property settings

Figure 9.4 – background Rect Transform property settings

Our background game object should be scaled to the same proportions and centered as a white bar at the bottom of the screen, as shown in the following screenshot:

Figure 9.5 – Game window showing the placement of our background game object

Figure 9.5 – Game window showing the placement of our background game object

Now, let's darken this background game object so that it blends in with our game.

  1. With the background game object still selected, in the Inspector window, click the Color field and change its color settings to R: 12, G: 13, B: 13, A: 210, as shown in the following screenshot:
Figure 9.6 – background color and alpha values

Figure 9.6 – background color and alpha values

The background game object has changed color from its default white to a dark, slightly transparent color.

The area for our HUD has been set. The following subsections will go through each segment of our HUD and explain how to create the following:

  • Display lives: We'll be adding Image components at the bottom left of the HUD for every life our player has from the GameManager script. Each life will be grouped neatly.
  • Display score: The script already keeps track of the player's score, so all we need to do is use a Text component to keep the information up to date.
  • Mini-map: The mini-map will work visually similar to a radar, where the player will be able to see the wave of enemy opponents approaching them. This mini-map will be made using a second camera at a wider angle and will only display colored dots instead of the actual ships themselves.

Now, we can begin filling the HUD with data that we have already made in our script, starting with the player's lives.

Displaying the player's lives

The player starts the game with three lives. The two typical ways of displaying the number of lives to the player are by displaying a number count or showing a little icon for each life they have. Let's go for the latter as we can use a couple of Unity components we haven't used before.

This section will also include some extra code that will be put into our GameManager script. This code will run a check to see how many lives the player has. With each life that's found, a game object will be created that holds an image.

Adding a Horizontal Layout Group component to our game object

All game object lives that will be created will be stored in a game object called lives. Let's continue working on the HUD and add the lives game object:

  1. In the Hierarchy window, right-click the Canvas game object and select Create empty from the drop-down list. A new empty game object will be created.
  2. Right-click the new game object and select Rename from the drop-down listRename the game object to lives.
  3. Next, position the lives game object by adding the Rect Transform properties shown in the following screenshot:
Figure 9.7 – lives Rect Transform property settings

Figure 9.7 – lives Rect Transform property settings

The last thing we need to do to the lives game object is to give it a Horizontal Layout Group component. This component will make it so that when we create an image to represent each life the player has, we'll display a spaceship image.

The Horizontal Layout Group component will put each spaceship image in a stacked order. To add this component, follow these steps:

  1. With the lives game object still selected, click the Add Component button in the Inspector window.
  2. The Add Component drop-down window will appear. Type Horizontal Layout Group until you see it on the list. When you do, select it.

Figure 9.8 shows the Horizontal Layout Group component when it's been added to the lives game object. We will need to alter some values of each life image so that they aren't too large.

  1. Change the Horizontal Layout Group property values to the ones shown in the following screenshot (you may need to click the arrow next to Padding to expand its content):
Figure 9.8 – Horizontal Layout Group property values

Figure 9.8 – Horizontal Layout Group property values

So far, we have created a game object called lives that will store and automatically order each player's ship image.

In the next section, we are going to create a game object that will house each player's ship image. As an example of what's to come in the next two sections, the following screenshot demonstrates our lives game object holding each life game object:

Figure 9.9 – Three lives spaced and in correct proportion

Figure 9.9 – Three lives spaced and in correct proportion

Now, let's move on to making a game object called life that will store a spaceship icon.

Creating images to represent our life count

In this section, we are going to create a game object that will hold an Image component that will be a symbol of the player ship. We will also be sizing it specifically so that it's uniform with the other lives it sits with.

Let's start by creating a game object that holds an Image component:

  1. In the Hierarchy window, right-click the Canvas game object and select UI and then Image from the drop-down list.
  2. Select the game object, right-click it, and select Rename from the drop-down list.
  3. Rename the newly created game object life.
  4. Add an image and color to the Image component, as shown in the following screenshot:
Figure 9.10 – Click the remote button to access which source image is used for the Image component

Figure 9.10 – Click the remote button to access which source image is used for the Image component

  1. To add a source image to the life game object, click the remote button to the side of Source Image (denoted by an arrow in the previous screenshot).
  2. In the drop-down list, start typing life until you see it appear, and then click it.

Our ship icon should look like the one shown in the following screenshot. If it doesn't, it may have a Default texture type and will need to be changed to a Sprite. We covered how to change this in Chapter 5, Creating a Shop Scene for Our Game:

Figure 9.11 – Our ship sprite life

Figure 9.11 – Our ship sprite life

I'm going to change the color of the icon slightly as it's possibly a bit too distracting for the player.

  1. With the life game object still selected, click the Color field, and change the color settings to an aqua gray (R: 153, G: 177, B: 177, A: 255).
  2. Make sure to tick the Preserve Aspect box in the Image component so our life doesn't lose its proportions.

That's our life game object created. The final thing we need to do to it is to turn it into a prefab. As a reminder, the benefits of a prefab are that we have a game object with its components, preferences, and settings all stored. The prefab will allow us to create as many clones as required.

To turn this life game object into a prefab, do the following:

  1. In the Project window, navigate to Assets/Resources.
  2. Click and drag the life game object from the Hierarchy window into the Prefab folder. That's our prefab created.

We can now delete the life game object in the Hierarchy window as we will be creating this game object with code in the next section.

Coding our UI life counter

In this section, we are going to revisit the GameManager script, taking the information about the player's life count and displaying it in the form of our UI system.

The following screenshot shows a section of the Hierarchy window that holds the level1 scene's Canvas game object. Within Canvas is the HUD background game object at the top, followed by the lives game object. Finally, with our code (which we will write shortly), we have created three life game objects within our lives game object:

Figure 9.12 – Our Hierarchy containing a lives game object and its life children

Figure 9.12 – Our Hierarchy containing a lives game object and its life children

To instantiate the life game objects so that they show the same amount as our player's lives, do the following:

  1. In the Project window, navigate to the Assets/Script folder.
  2. Double-click the file to open GameManager.

The GameManager script already has an Awake() function, which is the first thing the script tries to activate once the script becomes active. What we don't currently have is a Start() function that gets called after Awake().

We can create a Start() function in GameManager and make it call a method that we are going to make shortly, called SetLivesDisplay, and send it our playerLives variable, which is the count of the player's lives.

Like any function, we can place it anywhere within the class, as long as it's not inside another method/function. I typically keep my Awake() and Start() functions near the top of the GameManager class. To call the custom method in the Start() function, do the following.

  1. Enter the following code in the GameManager script:

       void Start()

       {

         SetLivesDisplay(playerLives);

       }

Now, we'll fill in the content for the SetLivesDisplay method.

I have put my SetLivesDisplay near the bottom of the GameManager script, but like the Start and Awake functions, you can put them wherever you wish in the GameManager script.

  1. Enter the following code:

    public void SetLivesDisplay(int players)

        {

This method is set to public because our ScenesManager script will need to access it to load whatever level the player is on. We have our SetLivesDisplay method set to void as we aren't returning anything while in the method. As I mentioned previously, we take in the playerLives integer but we will refer to it as players while in the method.

Let's continue by adding some code inside the SetLivesDisplay method. This is where we will be checking, adding, and visually removing lives if the player dies.

  1. Enter the following code inside the SetLivesDisplay method:

    if (GameObject.Find("lives"))

        {

          GameObject lives = GameObject.Find("lives");

          if (lives.transform.childCount < 1)

          {

            for (int i = 0; i < 5; i++)

            {

              GameObject life = GameObject.Instantiate(Resources

                .Load ("life")) as GameObject;

              life.transform.SetParent(lives.transform);

            }

          }

In the previous code, we ran a check to find a game object called lives. If we find the game object, we store its reference in a game object called lives. We then ran a check to see if our lives game object is holding any game objects. If lives isn't holding any game objects, we are assuming this is the beginning of the level and that we need to create some lives. Inside the if statement, we ran a for loop with a limit of 5 counts. Inside this for loop, we instantiate our life prefab and let it sit inside the lives game object.

  1. Continue writing inside the SetLivesDisplay method, which is where we manage the count of each life prefab, and make it show the actual amount of lives the player has:

          //set visual lives

          for (int i = 0; i < lives.transform.childCount; i++)

          {

            lives.transform.GetChild(i).localScale = new Vector3(1,1,1);

          }

          //remove visual lives

          for (int i = 0; i < (lives.transform.childCount - players); i++)

          {

            lives.transform.GetChild(lives.transform.childCount - i

                -1).localScale = Vector3.zero;

          }

        }

      }

There are two main parts to the code we just wrote. The first for loop is set by the count of how many game objects sit under the lives game object. Each game object under lives gets scaled to 1.

The second for loop takes the count of game objects under lives and subtracts it against the player's int variable that is brought into the parameters of this method. Inside this second for loop – depending on how big the player's int variable is – each life prefab is shrunk to 0. Scaling the life prefab to 0 doesn't affect the Horizontal Layout Group spacing, allowing the lives counter to not fluctuate based on the number of lives shown.

  1. Save the script.

GameManager is now capable of creating a life meter at the bottom of the level1 scene. We now need to add some functionality so that ScenesManager loads the number of lives when the level is loaded.

To get the ScenesManager script to load the player's lives when a level starts or when the player dies, do the following:

  1. In the Project window, navigate to the ScenesManager script, Assets/Script.
  2. Double-click the ScenesManager script to be able to start coding.
  3. In the ScenesManager script, we will add a Start() function that will contain a known Unity delegate, sceneLoaded, which is called from Unity's own SceneManager. This delegate will subscribe to when our game scene changes. For more information about the sceneLoaded delegate, go to https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager-sceneLoaded.html.
  4. Within the ScenesManager script, enter the Start function, along with the name of the function we are hooking into the delegate:

      void Start()

       {

           SceneManager.sceneLoaded += OnSceneLoaded;

       }

Still in the ScenesManager script, we will add the Unity-recognized function, which will automatically take Scene and LoadSceneMode types, even if we aren't going to do anything with them.

Inside the function, we are calling the GameManager script's SetLivesDisplay, along with the number of lives the player has.

  1. Enter the following code we just discussed inside ScenesManager:

    private void OnSceneLoaded(Scene aScene, LoadSceneMode aMode)

    {

        GetComponent<GameManager>().SetLivesDisplay(GameManager.playerLives);

    }

  2. Save the script.

Let's check what we have made:

  1. Go back into the Unity Editor while still being in the scene we are working on (level1).
  2. Press Play – three lives should be displayed. If the player dies, the life count will drop to two.

The following screenshot shows the game being played, alongside the player's lives in the bottom left:

Figure 9.13 – Screenshot of our game currently with its lives counter

Figure 9.13 – Screenshot of our game currently with its lives counter

In this section, we have hooked up the player's lives so that they can be displayed in the bottom-left corner of the HUD. We have applied components such as Horizontal Layout Group and Layout Element to set the player's lives images in uniform order and size. We also made the code apply and update the player's lives whenever the scene loads up.

Next, we will focus on the other side of the HUD and display the player's score.

Displaying the player's score

In this section, we will be applying the player's score on the right-hand side of the HUD, which we are currently filling up with information about the player.

We will continue to work in the Canvas game object and add another game object called score. Here, we will add a Text component and update a small section of the ScenesManager code to load the score display. Let's get started:

  1. While still in the level1 scene, right-click the Canvas game object in the Hierarchy window.
  2. From the drop-down list, select UI | Text.
  3. Right-click the new Text game object and select Rename from the drop-down list.
  4. Rename the game object score.

With the score game object renamed and located inside the Canvas game object, the next thing we need to do is to size and move the score game object into position.

  1. With the score game object still selected, alter its Rect Transform properties in the Inspector window so that they look like the ones shown in the following screenshot:
Figure 9.14 – score Rect Transform property settings

Figure 9.14 – score Rect Transform property settings

With the score game object in the correct position and scale, we can now customize its Text component settings.

With the score game object still selected, make the following changes to its Text component in the Inspector window:

  1. Change the Text field from New Text to 00000000. The number of zeros in the Text field will help us specify the size of the board.
  2. As shown in the following screenshot, we have selected the same custom text we used for the game's level scene titles. Click the remote button to the right of the Font field and select ethnocentric rg it from the drop-down list.
  3. Set the Alignment buttons to Align Right and Middle Center. This will position the text and minimize any space on its right-hand side.
  4. Have Best Fit ticked so that our score text font size will set itself.
  5. Change the Best Fit properties to the following: Min Size to 0 and Max Size to 60. This will set the limits for the Best Fit text.

The last property to change is the Color property of the text. We will set this to the same color as our player's lives.

  1. Click the Color field property and change its RGBA values to R: 153, G: 177, B: 178, A: 255.

The following screenshot shows what our Text component properties have been set to:

Figure 9.15 – score Text Component property settings

Figure 9.15 – score Text Component property settings

If we check the Game window, we should see that the score in the right-hand corner is a good size, as shown in the following screenshot:

Figure 9.16 – Our In-Game HUD now holding the score

Figure 9.16 – Our In-Game HUD now holding the score

The final phase for our score game object is to update our ScenesManager script by adding an if statement to check whether the score game object is in the scene.

To update the ScenesManager script so that it supports our new score game object, do the following:

  1. In the Project window, navigate to the Assets folder.
  2. Double-click the ScenesManager script and scroll down to where we entered the OnSceneLoaded function.
  3. Inside the OnSceneLoaded function, enter the following code:

    if (GameObject.Find("score"))

    {

    GameObject.Find("score").GetComponent<Text>().text =

        GetComponent<ScoreManager>().PlayersScore.ToString();

    }

As briefly mentioned in the newly added piece of code, we are checking whether the score game object is in the scene. If score is present in the scene, then we grab its Text component and apply the player's score integer to it from the ScoreManager script. Also make sure you have the using UnityEngine.UI at the top of the script to access the Text component.

  1. Save the script.

Speaking of the ScoreManager script, we need to load this script back up so that its ResetMethod resets the score UI at the start/end of each game. Follow these steps to do so:

  1. In the Project window, navigate to the ScoreManager script located in Assets/Script and open the file.

Inside the script, we need to bring in the UnityEngine.UI library so that we can make changes to our game's visual score.

  1. At the very top of the ScoreManager script, enter the following code:

    using UnityEngine.UI;

  2. Within the ResetScore method, add an if statement that checks that the score UI game object is in the scene and updates. The following code shows the complete ResetScore method in the ScoreManager script:

    public void ResetScore()

    {

         playerScore = 00000000;

         UpdateScore();

    }

  3. We also need to apply the UpdateScore method to our SetScore function. Go ahead and apply it as shown in the following code:

    public void SetScore(int incomingScore)

    {

             playerScore += incomingScore;

            

             UpdateScore();

    }

  4. We now need to move our UpdateScore method within the ScoreManager script. To do that, add the following new method to update our score whenever it is set or reset:

    void UpdateScore()

    {

    if (GameObject.Find("score"))

            {     GameObject.Find("score").GetComponent<Text>().text = playerScore.ToString();

            }

    }

  5. Save the script.
  6. Go back into the Unity Editor and click the Play button to play level1.

Our score game object will now update when we destroy the enemies, as shown in the following screenshot:

Figure 9.17 – Screen of our game with the score totaling up

Figure 9.17 – Screen of our game with the score totaling up

In this section, we took the existing ScoreManager code that was originally displaying the player's score and made it into a Console window in the Editor. Now, it sends the score variable to the new HUD score in the level1 scene, which will update when an enemy is destroyed.

The final piece we need to create for the HUD is the mini-map, which will give us a visual of the enemies in our level.

Creating a mini-map

In this section, we are going to fit a mini-map inside the HUD display to show a larger scope of the level. This will display the player, along with the enemies nearby, in a radar style. The following screenshot shows a radar in the middle of the HUD that represents the player, along with the enemies around them and other enemies that are due to enter the player's screen:

Figure 9.18 – Screenshot of our game displaying a mini-map

Figure 9.18 – Screenshot of our game displaying a mini-map

We will break down the mini-map into three sections:

  • Radar Camera: The second camera in the scene.
  • Layers: This makes the second camera recognize only a particular set of game objects.
  • Render Texture: This displays the final results in an animated image on the HUD.

Let's start by creating an extra layer so that we can expose certain game objects to our radar camera.

Creating and adding layers to our player and enemy game objects

In this section, we will add an extra game object to our player and enemy game objects so that our second camera will only see the attached sprites. These will look like blips on a radar.

To add our radar blips to the game objects, do the following:

  1. At the top right of the Unity Editor, click the Layers button, followed by Edit Layers..., as shown in the following screenshot:
Figure 9.19 – Edit Layers… will allow us to add another layer to our game

Figure 9.19 – Edit Layers… will allow us to add another layer to our game

  1. The Inspector window will change and show the Tags & Layers properties. From here, we can click to expand the Layers tab.
  2. Click on one of the available layers near the top and enter Radar, as shown in the following screenshot:
    Figure 9.20 – Expanding our Layers list and adding a new layer

Figure 9.20 – Expanding our Layers list and adding a new layer

Now, we can add a radar point to the player and enemies. Let's make a start by bringing the player into the scene and updating its prefab so that it will be recognized by the radar camera. To do this, follow these instructions:

  1. In the Project window, navigate to the Assets/Prefab/Player folder.
  2. Drag and drop player_ship into the Hierarchy window at the bottom, in an open space.
  3. In the Hierarchy window, right-click player_ship, select Create Empty.
  4. Right-click the game object and select Rename from the dropdown.
  5. Rename the game object to radarPoint.
  6. With radarPoint still selected, click the Add Component button in the Inspector window. Start typing Sprite Renderer until it appears in the component drop down. Next,click its layer on top of the Inspector window and select Radar from the drop-down list, as shown in the following screenshot. We can also set our Transform property to match the ones in the following image:
Figure 9.21 – radarPoint game object with its Radar layer

Figure 9.21 – radarPoint game object with its Radar layer

With the Transform properties set, we can now drop the radar dot sprite into the Sprite field and change its color:

  1. Click the remote button to the right of the Sprite field in the Sprite Renderer component.
  2. Start typing knob in the drop-down list until you can see it and select it, as shown in the following screenshot:
Figure 9.22 – Accessing and selecting the default knob sprite

Figure 9.22 – Accessing and selecting the default knob sprite

  1. We can now change the color of the sprite by clicking the Sprite Renderer Color field and giving it the following color properties: R: 0, G: 245, B: 255, A: 255.
  2. Select player_ship in the Hierarchy window and click the Overrides button near the Inspector window followed by Apply All to update the player_ship game object's prefab settings.
  3. Select the player_ship game object in the Hierarchy window and press Delete on your keyboard.

We have now set the player ship so that it's ready to be detected by the radar camera.

The next thing to do is repeat the same methodology for the enemies, which are located in Assets/Prefab/Enemies.

  1. Without going through the same instructions, the following screenshot shows our enemies' radarPoint game object with a bright red color value (R: 255, G: 0, B: 0, A: 0). If you get stuck, just follow the same steps as for the player ship's radarPoint:
Figure 9.23 – radarPoint properties setup

Figure 9.23 – radarPoint properties setup

Tip

In the Inspector window, click on Overrides | Apply All once you have finished making changes to your prefab.

Once we have finished making our changes and applied them to the prefab, we no longer need the enemy_wave game object as we have saved its details in the prefab.

  1. Delete enemy_wave from the Hierarchy window.

We have effectively created a tracker (radarPoint) and attached it to the player and enemies for our level.

The next step is to add a Render Texture, which will work with a second camera in our scene. The feed from the second camera will be fed into a Render Texture. This Render Texture will then be placed at the bottom middle of the screen and display our player and enemy location.

Adding and customizing our Render Texture

Render Texture is typically used to hold moving images while in Play Mode (at runtime). We are going to use this Render Texture to hold the second camera's feed. This will work like a little TV screen in the center of our HUD.

To create and customize Render Texture, we will do the following:

  1. In the Project window of the Unity Editor, navigate to the Texture folder, that is, Assets/Texture.
  2. Right-click in an open space area and from the drop-down list, select Create, then Render Texture, as shown in the following screenshot:
Figure 9.24 – Creating a Render Texture

Figure 9.24 – Creating a Render Texture

Tip

If you don't have an open space to right-click, as step 2 suggests, you can change the size of the icons to gain space.

Change your icon size with the slider in the bottom-right corner of the Project window.

  1. Click the name of the file slowly twice and rename it radar.
  2. With the radar Render Texture selected, we will need to change its size to one that will fit the HUD and, ideally, make it less blurry.
  3. In the Inspector area, change the Size fields from 256, 256 to 236, 46.
  4. Change Filter Mode from Bilinear to Point.

The last part of setting up Render Texture is to place it into the HUD. Follow these steps:

  1. Still in the level1 scene, right-click the Canvas game object in the Hierarchy window and select Create Empty.
  2. Select the new empty game object in the Hierarchy window, right-click it, and select Rename from the drop-down list.
  3. Rename the game object radar.

The radar game object will work as housing for anything related to the game object.

  1. This game object will now need to be positioned and sized in the HUD. To do that, change the radar game object's Rect Transform properties in the Inspector window to the ones shown in the following screenshot:
Figure 9.25 – radar Rect Transform property settings

Figure 9.25 – radar Rect Transform property settings

Moving and resizing the radar game object will give us a letterbox window for Render Texture to sit inside, as shown in the following screenshot:

Figure 9.26 – The placement of our radar game object in the Game window

Figure 9.26 – The placement of our radar game object in the Game window

We can now add another game object that will be a child of the radar game object we've just made. This game object will store Render Texture:

  1. Right-click the radar game object in the Hierarchy window. From the drop-down list, select UI and then Raw Image.
  2. Right-click the new game object called Raw Image, select Rename from the dropdown, and rename the game object radarImage.
  3. With the radarImage game object still selected, change its Rect Transform settings to the ones shown in the Inspector window in the following screenshot:
Figure 9.27 – radarImage Rect Transform property settings

Figure 9.27 – radarImage Rect Transform property settings

Next, we need to apply radar Render Texture to the Raw Image Texture field:

  1. With radarImage still selected in the Hierarchy window, click the remote button next to the Texture field in the Raw Image component.
  2. Start typing radar in the search bar at the top of the new window until radar Render Texture appears and select it.

That's our Render Texture made and set. Now, we can pass this into the second camera. But before we do that, we need to add the camera!

Adding and customizing our second camera

In this section, we will be adding a second camera so that we can only see the radarPoint game objects.

Let's start by setting up a second camera in our level1 scene:

  1. In the Hierarchy window, right-click in an open space and from the dropdown, select Camera.
  2. Right-click the newly created Camera, select Rename from the drop-down list, and rename it RadarCam.
  3. With RadarCam still selected, change its Transform settings in the Inspector window to the ones shown in the following screenshot:
Figure 9.28 – radarCam Transform property settings

Figure 9.28 – radarCam Transform property settings

Still in the Inspector window and with our RadarCam selected, we need to change its Camera component settings to the following:

  • Clear Flags: Solid Color. We don't require anything in the background for the second camera, so something basic such as a solid color would work fine.
  • Background: R: 255, G: 0, B: 0, A: 50. This will give our radar a red tint.
  • Culling Mask: Click the parameter field labeled Everything. Do the following:
    • Select Nothing from the drop-down list to remove all layers.
    • Select the field again and select Radar (shown in the following screenshot). By doing this, all our camera will see is the game objects that relate to that layer:
Figure 9.29 – Select Radar from Culling Mask

Figure 9.29 – Select Radar from Culling Mask

  • Projection: Orthographic. The radar camera is 2D, so there is no need to have a perspective view.
  • Size: 150. The size of our camera view will be larger than the main view the player is in.
  • Target Texture: Click the remote button and select the radar's Render Texture from the new window that appears. This will send the feed from RadarCam to the radar's Render Texture.
  1. Our Main Camera (not RadarCam) won't need to see the Radar layer. Select Main Camera from the Hierarchy window and deselect the Radar layer from its Culling Mask.
  2. Also, with RadarCam still selected, click the three dots next to its Audio Listener component and remove it. We already have one camera that listens for audio in our scene.
  3. Finally, we need to make it so that RadarCam is a child of Main Camera so that it's part of the same functionality. Click and drag RadarCam into Main Camera in the Hierarchy window.
  4. Click Overrides | Apply All in the Inspector window to update the Main Camera prefab and save the scene.

Now, if we click Play in the Unity Editor, we will see the radar in the HUD with its red tint showing red dots for enemies and a neon blue for the player, as shown in the following screenshot:

Figure 9.30 – Our mini-map detecting the player and its enemies

Figure 9.30 – Our mini-map detecting the player and its enemies

This mini-map was created without any code and made use of two new components: Render Texture, which will hold the second camera's feed, and a Raw Image component, which will display the final output.

In this section, we created a functioning HUD that has three main segments: the player's lives, a mini-map, and the player's score. We used the two fundamental UI tools that Unity offers to create a UI display. However, we also introduced three new components, as follows:

  • Horizontal Group Layout: Spaces the player's lives equally
  • Render Texture: Transfers the second camera's feed
  • Raw Image: Displays the feed from the render texture

The following screenshot shows the final HUD:

Figure 9.31 – Our HUD is complete

Figure 9.31 – Our HUD is complete

Because we have updated our level1 scene, we need to update level2 and level3. The quickest way to do this would be to delete level2 and level3 and duplicate level1, as we did before, which leaves us with updating the level number in the Text component. We did this in the previous chapter, right at the end, so please check that if you need some guidance.

Now, we will move on and improve the existing shop scene by removing the pre-made polygons for UI components. This will also introduce us to using UI event triggers and making our code smaller and more efficient.

Making our shop scene support alternative screen ratios

In this section, we are going to take our current shop scene and make it compatible with various screen ratios. Currently, our shop visuals are made out of polygons, which look fine, but, for example, our selection grid of buttons at the bottom of the screen has the risk of being clipped off at the edges. We can also change the way we select our buttons by using Unity's Button component, which works within the Canvas.

Figure 9.32 – There is a wide range of ratios in what a game is displayed in

Figure 9.32 – There is a wide range of ratios in what a game is displayed in

Because of these UI changes, this will cut our code down and make it more efficient as we will be relying on click events. We will cover these later in this section.

Let's make a start by replacing the selection grid at the bottom of our shop scene.

Upgrading our shop selection

In this section, we are going to remove all of the shop buttons and replace them with a Horizontal Layout Group set of buttons to add the player's lives to the HUD. Each of the new selection buttons will contain a Unity pre-made script called button that has its own raycast system. This raycast system will give us an easier way of adding and customizing our buttons when it comes to adding or extracting buttons to/from the selection grid.

In the next section, we will support this change by removing our 3D assets so that we can replace them with Unity's own 2D buttons.

Preparing our shop scene to go 2D

Let's start by removing the old selection grid at the bottom and our BUY ? button as that follows the same suit from our shop scene:

  1. If you haven't loaded the shop scene already, locate it in the Project window in Assets/Scene.
  2. Double-click shop to load the shop scene.
  3. In the Hierarchy window, hold Ctrl (command on Mac) on the keyboard and select all of the game objects shown in the following screenshot:
Figure 9.33 – Select these game objects from the shop scene Hierarchy

Figure 9.33 – Select these game objects from the shop scene Hierarchy

Press Delete on the keyboard if you are presented with a window to open the prefab. Open it and repeat the process. Once deleted, press the back button in the top left corner of the Hierarchy window. We haven't lost any of our sprite images, scripts, or any other type of information. We are simply removing polygons, 3D materials, and colliders (physics-based components). We are now going to move the same information we have into Canvas.

To create a Canvas with its own background, do the following:

  1. In the lower part of the Hierarchy window, right-click and from the drop-down list, select UI, followed by Canvas.
  2. Right-click the Canvas game object in the Hierarchy window and from the drop-down list, select UI, followed by Image.
  3. Right-click the new game object called Image and select Rename from the drop-down list.
  4. Rename Image to backGround.
  5. With the backGround game object still selected, change its Rect Transform properties to the ones shown in the following screenshot:
Figure 9.34 – backGround Rect Transform property settings

Figure 9.34 – backGround Rect Transform property settings

  1. We can now give backGround some color. With the backGround game object still selected, click the Color field in the Image component in the Inspector window and set its values to R: 255, G: 0, B: 0, A: 63.

The following screenshot shows the backGround game object positioned and scaled with a red tint:

Figure 9.35 – backGround in our Game window

Figure 9.35 – backGround in our Game window

We can now move on to the next section, where we will add three game objects that will control the position and scale of the button game objects.

Adding layout group components

In this section, we will add game objects that will support the spacing of the buttons we add to the grid. The benefit of this is that we can control the properties of each section of the buttons, as shown in the following figure:

Figure 9.36 – Our shop scene button layout as planned

Figure 9.36 – Our shop scene button layout as planned

Next, we will make an empty game object and add a Horizontal Layout Group to it, which will keep our top row buttons in order:

  1. Right-click the Canvas game object and from the drop-down list, select Create Empty.
  2. Rename the new game object gridTop.
  3. With gridTop still selected, change its Rect Transform settings to the ones shown in the following screenshot:
Figure 9.37 – gridTop Rect Transform property settings

Figure 9.37 – gridTop Rect Transform property settings

Now that our gridTop is positioned correctly, we can add a Horizontal Layout Group to it:

  1. With the gridTop game object still selected, click the Add Component button in the Inspector window and type Horizontal Layout Group into the search bar at the top of the drop-down list until you see Horizontal Layout Group. When this group appears in the list, select it.
  2. Give Horizontal Layout Group the following settings:
Figure 9.38 – Horizontal Layout Group property values

Figure 9.38 – Horizontal Layout Group property values

gridTop will now automatically order the top row of upgrade buttons.

We now need to repeat the process for the bottom row, without repeating the entire procedure. Follow the same steps for gridTop but make the following changes:

  1. Name the next game object in Canvas gridBottom.
  2. Give the game object the following Rect Transform settings:
Figure 9.39 – gridBottom Rect Transform property settings

Figure 9.39 – gridBottom Rect Transform property settings

  1. Then, like before, we need to add a Horizontal Layout Group component with the same settings as gridTop.
  2. We then repeat this process but this time, for our "AD" and "START" buttons, we will be adding a Vertical Layout Group component.
  3. Like before, create an empty game object and store it in the Canvas game object.
  4. Name a new game object called gridOther.
  5. Give gridOther's Rect Transform the following settings:
Figure 9.40 – gridOther Rect Transform property settings

Figure 9.40 – gridOther Rect Transform property settings

  1. As mentioned previously, we will add a Vertical Layout Group component to the gridOther game object and give it the following settings:
Figure 9.41 – Vertical Layout Group property values

Figure 9.41 – Vertical Layout Group property values

Our new, reworked selection grid now supports the creation of multiple self-scaling buttons. In the next section, we will demonstrate how to create multiple buttons that scale themselves to fit in the selection grid.

Adding UI buttons

In this section, we are going to create a button that won't need any sizing changes to be made to it as the layout groups we placed in the previous section will take care of this.

To create a UI button for our new selection grid, right-click the gridTop game object in the Hierarchy window and do the following:

  1. From the drop-down list, select UI and then Button.
  2. Right-click the newly created Button game object and name it 00.

We will get a button that will be stretched and out of place, but don't worry – this is normal. Later, when we add more buttons to this and the other rows, the buttons will snap into place and scale in size automatically.

By default, the button comes with an Image component with rounded-off edges. For cosmetic purposes, this doesn't suit our scene. We can remove this by doing the following:.

  1. Click the three-dots icon to the top right of the Image component.
  2. From the drop-down list, select Remove Component.

The button no longer has any color.

Next, we are going to fill this game object with five game objects. In brief, their names and properties are as follows:

  • outline: Adds a border to the button
  • backPanel: The color of the button when it's not selected
  • selection: The color of the button when it is selected
  • powerUpimage: The picture on the button
  • itemText: The cost or sold out message

The following screenshot shows all of these game objects combined to create our new shop button:

Figure 9.42 – A shop button

Figure 9.42 – A shop button

Tip

The other way of changing a button's condition is by using Unity's Button component states. For more information about this and the Button script, check out https://docs.unity3d.com/2017.3/Documentation/Manual/script-Button.html.

Adding the outline game object

Let's start by adding an outline game object for our new shop button:

  1. Right-click the 00 game object in the Hierarchy window and from the dropdown, select UI | Image.
  2. Select the Image game object, right-click it in the Hierarchy window, select Rename, and change its name to outline.
  3. With outline still selected in the Hierarchy window, update its Rect Transform and Image Color fields to the following:
Figure 9.43 – outline Rect Transform property settings and Image component color and alpha values

Figure 9.43 – outline Rect Transform property settings and Image component color and alpha values

The shop button will now have a colored outline. Now, let's move on and look at the button's backPanel.

Adding the backPanel game object

Let's add backPanel to the 00 game object:

  1. In the Hierarchy window, right-click the 00 game object and from the dropdown, select UI | Image.
  2. Right-click the newly created Image game object and name it backPanel.
  3. With backPanel still selected, in the Inspector window, change its Rect Transform so that it has the following values:
Figure 9.44 – backPanel Rect Transform property settings

Figure 9.44 – backPanel Rect Transform property settings

With the backPanel game object still selected, we can change the Image component's Color properties in the Inspector window. By clicking the Color field, we can change the backPanel game object's settings to R: 40, G: 39, B: 36, A: 255. That's the second game object that we've applied that gives us our default color.

We'll add the selection game object to the 00 game object next.

Adding the selection game object

To create the selection button, follow the same steps provided in the previous section. However, note that there are three differences:

  1. Name this game object selection.
  2. Give the Image component's Color field the following values: R: 144, G: 0, B: 0, A: 255.
  3. Create and apply a Tag called Selection.

    Information

    We covered creating and applying tags back in Chapter 2, Adding and Manipulating Objects.

The following screenshot shows our selection game object's Tag and Rect Transform property values:

Figure 9.45 – selection Rect Transform property settings

Figure 9.45 – selection Rect Transform property settings

That's the third game object that we've applied to our 00 game object. Our buttons will light up and stay red until a purchase is made or a different button is pressed. We'll add the powerUpImage game object to our 00 game object next.

Adding the powerUpImage game object

To create the powerUpImage button, follow the same steps provided in the previous section, but make three changes:

  1. Name this game object powerUpImage.
  2. Drag and drop the powerup sprite into the Source Image field of the Image component.
  3. Tick the Preserve Aspect box.

That's our fourth game object that displays each button's icon.

We'll add the itemText game object to the 00 game object next.

Adding the itemText game object

To add the itemText game object to our 00 game object, do the following:

  1. In the Hierarchy window, right-click the 00 game object and from the drop-down list, select UI, followed by Text.
  2. Right-click the newly created Text game object and name it itemText.
  3. With itemText still selected, in the Inspector window, change its Rect Transform and Text components so that they have the following properties:
Figure 9.46 – itemText Rect Transform property settings and Text Component values

Figure 9.46 – itemText Rect Transform property settings and Text Component values

That's the fifth and final game object we need to add to our weapon upgrade button.

In the Hierarchy window, our 00 game object should be in the order shown in the following screenshot. If the order isn't the same, simply click and drag either one into position:

Figure 9.47 – 00 game object and its children in the Hierarchy window

Figure 9.47 – 00 game object and its children in the Hierarchy window

In this section, we stripped out the old shop scene setup where we were selecting items in the shop with a raycast system. We replaced the old selection grid with a 2D interface with Button components. These buttons were grouped with Unity's Horizontal and Vertical Layout Group components. The benefit of these two groups is that if we add more or fewer buttons to the grid, the buttons will reorganize their position and scale automatically.

We need to make a slight modification to the ShopPiece script that was originally attached to each game object button.

Once we have applied and modified the script, we will check what the buttons look like in the new selection grid.

Applying and modifying our shop script

Let's briefly recall the purpose of the ShopPiece script. Each button in the selection grid will be given information from a scriptable object that will customize the button's name, description, value, and image. Because the buttons have changed from being 3D assets to 2D ones, we need to alter and add some more code to make this work.

To modify ShopPiece so that it's compatible with our new 2D button, do the following:

  1. In the Project window in the Unity Editor, navigate to the Assets/Script folder.
  2. Double-click the ShopPiece script to open the file.

The first line of code will allow our new code support to grab references from the Text component on the 00 game object.

  1. Enter the following piece of code at the top of the ShopPiece script:

    using UnityEngine.UI;

The second modification to make will be to replace the content of the Awake function. The original code accessed SpriteRenderer, which was used for accessing the sprite on each polygon button. The other piece of code we are replacing applied changes to the TextMesh component, which displays 3D text.

  1. To update our Awake function, select the code within the Awake() function and delete it. Our Awake() function should look as follows:

    void Awake()

    {

    }

We can now enter the first if statement, which applies our scriptable object icon image to our button's image.

  1. Within the Awake() function, add the following if statement:

        if (transform.GetChild(3).GetComponent<Image>() !=        null)

        {

          transform.GetChild(3).GetComponent<Image>().      sprite=shopSelection.icon;

    }

The if statement grabs a reference from the second child in the 00 button and checks to see whether it has an Image component. If it does (and it should), we apply the scriptable object icon to it.

  1. The other if statement updates the text of the button. Within the Awake() function, just after the first if statement, add the following piece of code:

    if(transform.Find("itemText"))

    {

    GetComponentInChildren<Text>().text = shopSelection.cost.ToString();

    }

The if statement makes sure the 00 button has itemText (it should). When the itemText game object is found, its Text component receives the scriptable object price of the weapon.

  1. Save the script.
  2. Back in the Unity Editor, select the 00 game object in the Hierarchy and click the Add Component button.
  3. Start typing ShopPiece in the drop-down list until you see it. When you do, select it.
  4. With the 00 game object still selected, in the Inspector window, click the remote button in the ShopPiece component.
  5. Select any weapon upgrade scriptable object from the list.

The following screenshot shows the ShopPiece script with a scriptable object applied to it:

Figure 9.48 – ShopPiece script, holding in its Shop Selection field the Shot_PowerUp Scriptable Object

Figure 9.48 – ShopPiece script, holding in its Shop Selection field the Shot_PowerUp Scriptable Object

We are now in a position to check what our button looks like with the four game objects we've applied and with its modified ShopPiece script.

In the next few sections, we are going to duplicate a series of the new shop buttons. These shop buttons will automatically fit in the allocated game object space we have put them in. Then, we will clear up any of the old UI and replace it with our new interface. Finally, we will comment out the old raycast system from our code and add our new interface code.

Reviewing the button's results

In this section, we will be reviewing the new 00 button in the gridTop game object. The button is too big and spreads across the majority of the Canvas, as shown in the following screenshot:

Figure 9.49 – 00 game object currently sitting in the gridTop game object

Figure 9.49 – 00 game object currently sitting in the gridTop game object

But if I select the 00 game object in the Hierarchy window and press Ctrl (command on Mac) and D to duplicate the game object a few times, the button will divide equally, as shown in the following screenshot:

Figure 9.50 – Three 00 game objects looking less stretched

Figure 9.50 – Three 00 game objects looking less stretched

The button divides well and can be duplicated several times (not in Play Mode) to fill the top and bottom grids. To fill up and name the grids, do the following:

  1. In the Hierarchy window, select the 00 game object and press Ctrl (command on a Mac) and D three times.
  2. Rename the three new duplicate game objects 01, 02, and 03 respectively.
  3. Select 01 in the Hierarchy window and click the remote button in its ShopPiece component in the Inspector window.
  4. Select a different scriptable object from the list to change the weapon upgrade.
  5. Select game object 02 and select a different weapon in the ShopPiece component.

Now, we need to fill up the bottom row with buttons. To do that, follow these steps:

  1. Click and drag the 03 game object from the Hierarchy window into the gridBottom game object.
  2. With 03 still selected, press Ctrl (command on a Mac) and D twice.
  3. Rename our newly created game objects 04 and 05.

The following screenshot shows the top and bottom rows filled up:

Figure 9.51 – Six shop buttons

Figure 9.51 – Six shop buttons

Because we don't have any more items to sell in our shop, the bottom three buttons look odd, so let's replace these with some sold-out signs. This can easily be achieved with our scriptable object assets.

To create a sold-out sign for our bottom row in our shop, we need to do the following:

  1. In the Project window, navigate to Assets/ScriptableObject, right-click in an open space, and select Create | Create Shop Piece.
  2. Rename the Create Shop Piece file SoldOut.
  3. Select SoldOut and give it the following property values:
Figure 9.52 – SoldOut Scriptable Object and its values entered along with a soldOut sprite

Figure 9.52 – SoldOut Scriptable Object and its values entered along with a soldOut sprite

Lastly, apply the SoldOut file to game objects 03, 04, and 05 in the Hierarchy window in the Shop Piece component field's Shop Selection in the Inspector window.

Now, we need to repeat a similar process for our advert and start buttons.

Creating the advert and start buttons

To recreate the advert button, select either one of the buttons we duplicated in the Hierarchy window and do the following:

  1. Press Ctrl (command on a Mac) and D to duplicate another button and drag it into the gridOther game object in the Hierarchy window.
  2. Rename the duplicate game object AD.
  3. Because the AD game object doesn't need powerUpImage, we can delete it.
  4. Expand the AD game object by clicking the arrow to the left of its name in the Hierarchy window and select the itemText button.
  5. Apply the following settings to the Text component in the Inspector window:
Figure 9.53 – itemText Rect Transform property settings and Text component values

Figure 9.53 – itemText Rect Transform property settings and Text component values

  1. Repeat a similar process for the START button game object, except for its itemText and selection game object components (selection hex color: FFC300FF), as shown in the following figure:
Figure 9.54 – itemText Text component values and selection Image component property values

Figure 9.54 – itemText Text component values and selection Image component property values

The following screenshot shows the Hierarchy window of the gridOther game object and its content, including the two buttons:

Figure 9.55 – gridOther game object holding AD and START buttons with their children in the Hierarchy window

Figure 9.55 – gridOther game object holding AD and START buttons with their children in the Hierarchy window

Now that our selection grid visuals are completed, we can move on to the description panel and partially convert it from 3D into 2D.

Adding the BUY? button

To add the 2D BUY? button to the description panel, do the following:

  1. Right-click the Canvas game object in the Hierarchy panel and select UI, followed by Button, from the drop-down list.
  2. Right-click the newly created Button game object and select Rename from the drop-down list.
  3. Rename the Button game object BUY?.
  4. With the BUY? button still selected in the Hierarchy window, set its Rect Transform properties to the ones shown in the following screenshot:
Figure 9.56 – BUY? Rect Transform property settings

Figure 9.56 – BUY? Rect Transform property settings

Now that the BUY? button is in place and scaled correctly, we can alter the aesthetics for the Image and Button components. In the Image component, select the remote button for the Source Image field and select None from the list to remove the curved edges for the button.

Next, we will make it so that the BUY? button changes colors when it's highlighted and pressed in the Button component. Follow these steps to do so:

  1. In the Button component, select the Normal Color field and change its values to R: 255, G: 0, B: 0, A: 255.
  2. Select the Highlighted Color field and changes its values to R: 255, G: 195, B: 0, A: 255.
  3. When the cursor moves over the BUY? button, it will turn yellow and when pressed, it will turn red.

Finally, for the BUY? button, we need to alter its Text component, as follows:

  1. In the Hierarchy window, select the arrow next to the BUY? button to expand it.
  2. Then, select the BUY? game object's child, called Text.
  3. Enter the following values for the Text game object's Text component in the Inspector window:
Figure 9.57 – BUY? game object's Text component property values

Figure 9.57 – BUY? game object's Text component property values

The following screenshot shows our BUY ? button positioned and styled:

Figure 9.58 – The BUY? game object should now look like the one in this screenshot

Figure 9.58 – The BUY? game object should now look like the one in this screenshot

In this section, we applied Unity's different state settings for our button without adding any extra code. Next, we will add a simple rectangle image to replace the polygon quad.

Replacing our textBoxPanel game object

In the previous section, we changed our BUY ? button so that it is 2D and part of the Canvas, which also means the BUY ? button will now be moved, scaled, and adjusted to the ratio of the screen instead of remaining static. Because of this, we have the risk of our BUY ? button moving outside of the static textBoxPanel it sits in, as shown in the following screenshot:

Figure 9.59 – Fixing the issue with the textBoxPanel game object alignment with the BUY ? button

Figure 9.59 – Fixing the issue with the textBoxPanel game object alignment with the BUY ? button

Also, the PlayerShipBuild script has a reference to textBoxPanel, so we can't delete the game object without altering our code. To fix this dilemma, we can remove the 3D components of textBoxPanel, leaving it as an empty game object to house other game objects within it.

To remove the components from the textBoxPanel game object, do the following:

  1. In the Hierarchy window, start typing textBoxPanel in the search bar until it appears.
  2. Select textBoxPanel and remove the two components in the Inspector window for Quad (Mesh Filter) and Mesh Renderer by selecting and clicking the three dots and selecting Remove Component.
  3. To bring back our full game object content in the Hierarchy window, click the cross at the top of its window, to the right of the search bar.

The following screenshot shows the locations of both three-dot icons:

Figure 9.60 – Remote button locations for the Quad and Mesh Renderer components

Figure 9.60 – Remote button locations for the Quad and Mesh Renderer components

Now, we can create the 2D panel game object to replace the textBoxPanel game object's visuals, as follows:

  1. In the Hierarchy window, right-click the Canvas game object and select UI, followed by Image, from the drop-down list.
  2. Select the newly created game object, right-click it, and select Rename from the drop-down list.
  3. Rename the game object panel.
  4. Move the BUY? game object below the panel game object in the Hierarchy window so that the BUY? button sits on top of panel in the Scene window. The following screenshot shows the order of the two game objects:
Figure 9.61 – BUY? game object placement in the Hierarchy window

Figure 9.61 – BUY? game object placement in the Hierarchy window

  1. With the panel game object still selected, give its Rect Transform the following values in the Inspector window:
Figure 9.62 – panel Rect Transform property settings

Figure 9.62 – panel Rect Transform property settings

We can change the color of our panel game object by clicking its Color field within its Image component in the Inspector window and giving it the values highlighted in the following screenshot:

Figure 9.63 – Change the panel game object Image component color values to those in this screenshot

Figure 9.63 – Change the panel game object Image component color values to those in this screenshot

Finally, we can amend our textBoxPanel and bank balance fonts so that they fit in with the shop buttons.

To amend our bank balance, we need to do the following:

  1. In the Hierarchy window, expand ShopManager | bank and select the bankText game object.
  2. With bankText selected, update its Text Mesh component in the Inspector window so that the Font field takes our new ethnocentric rg it font.
  3. Change the Color field to red (R: 255, G: 0, B: 0, A: 255).
  4. Add a few digits in the Text field to check the results, as shown in the following screenshot:
Figure 9.64 – bank game object font updated

Figure 9.64 – bank game object font updated

To change our textBoxPanel, we need to do something similar. Here, we will select its two child game objects in the Hierarchy window, name and desc, and update their Text Mesh components to the following:

  1. Add the ethnocentric rg it font to the name and desc game object's Text Mesh Font fields.
  2. Give them a white Color.
  3. In the name game object's Text Mesh Text field, add the following:

    officer:

  4. Apply the same changes to the desc game object but add the following text to the Text field:

    will you need any

    upgrades before

    launch?

The following screenshot shows a section of the Game window and its updated font:

Figure 9.65 – Our textBoxPanel game object and its content updated

Figure 9.65 – Our textBoxPanel game object and its content updated

Now, all of our shop's visuals have been amended and will support various screen ratios. By doing this, we also introduced Unity's own Button component.

We have now reached the point where we can open a template script of PlayerShipBuild from our chapter's project files folder. This script will be a replica of the current PlayerShipBuild script we have been making but with highlighted code we will add to the project to support our shop scene's functionality.

Upgrading the PlayerShipBuild script

In this section, we are going to replace the current PlayerShipBuild script with the one from this chapter's project files folder. The replacement script will contain the same code as your current script but with code to show what we will be adding and removing step by step.

Let's rename our current PlayerShipBuild script to something else before we begin working on our new replacement script. To rename the current PlayerShipBuild script, do the following:

  1. In the Project window of the Unity Editor, navigate to the Assets/Script folder.
  2. Double-click the PlayerShipBuild script.
  3. With the PlayerShipBuild script open, rename the class name near the top of the script from PlayerShipBuild to PlayerShipBuild_OLD.
  4. Save the script and return to the Assets/Script folder in the Project window.
  5. Click the PlayerShipBuild script slowly twice so that you're provided with the option to rename the filename.
  6. Change the filename to PlayerShipBuild_OLD.

Now, we need to disconnect the PlayerShipBuild_OLD script from the shop game object.

  1. In the Hierarchy window, at the top, type shop into the search bar until you see the shop game object. When you do, select it.
  2. With the shop game object selected, click the three dots in the Inspector window in the PlayerShipBuild_OLD component (not Transform).
  3. Select Remove Component from the drop-down list.

With that, we have renamed and detached the script from the scene. Now, we can bring in the new replica PlayerShipBuild script from this chapter's project files folder.

To hook up the new replica PlayerShipBuild script from our project files folder, do the following:

  1. In the Project window of the Unity Editor, navigate to the Assets/ folder.
  2. Select the PlayerShipBuild_NEW.txt script inside the folder and drag it to the Assets/Script folder. Rename it and its file format from .txt to .cs. This will replace some of our old raycast scripts with the same name, PlayerShipBuild.cs, as shown in the following screenshot:
Figure 9.66 – Update the PlayerShipBuild script from the Chapter 9 folder

Figure 9.66 – Rename and move PlayerShipBuild_NEW to the project's Assets/Script folder

We can now apply this replica script to the shop game object in the scene. Let's get started:

  1. Select the shop game object in the Hierarchy window, as we did before.
  2. Click the Add Component button in the Inspector window and start typing PlayerShipBuild. When you see the PlayerShipBuild script, select it from the drop-down list.
  3. With the shop game object still selected, we can now configure the attached PlayerShipBuild script.
  4. To configure the script, set Visual Weapons Size to 3, click each remote button to the right of each field, and add the following highlighted, from the Hierarchy window:
Figure 9.67 – Add the following three game objects to the PlayerShipBuild script's fields

Figure 9.67 – Add the following three game objects to the PlayerShipBuild script's fields

Our new PlayerShipBuild script is now in place. This means we can now open the script and check through and reveal new sections of the code while explaining the fundamental parts of the old code's removal.

Each of the following Removing the old… subsections will do the following:

  • //REMOVE(number): Refer to what part of the code we are talking about.
  • Reason for removal: Specify why in the new PlayerShipBuild script its code has been removed.
  • Replacement: What the previous code has been replaced with.

Removing the old shop scene's code

In this section, we are going to go through the newly installed PlayerShipBuild script and review parts of the code I have commented out so that it won't be acknowledged when it's compiled and executed in Unity.

We will be turning off the ability to raycast a 3D object, which we coded in Chapter 5, Creating a Shop Scene for Our Game. Because we have swapped the interactable game objects from 3D to 2D, we are no longer required to shoot and identify game objects as Unity will take care of this with its own Button component.

To review the code we have commented out, go to the Project window and double-click the PlayerShipBuild script located where we left it (Assets/Script).

Information

Commenting, Comments, and UnComment are words that refer to when a piece of code has two forward slashes in front of it. These will be ignored when our code is read by the compiler (when we run our code).

We are going to review each piece of code in separate sections so that it's clear when the changes we are going to make in PlayerShipBuild will be applied.

Reviewing code – REMOVED 01

Each main chunk of code begins with //REMOVED, followed by a number. Here are the reasons why we have removed the particular piece of code for //REMOVED 01:

  • //REMOVED 01: This piece of code creates a raycast and returns a game object called target.
  • Reason for removal: We no longer need to rely on getting references for each game object we shoot a ray at.
  • Replacement: The Button component comes with an OnClick event, which is typically used to load a method when it's selected.

Let's continue scrolling down the PlayerShipBuild script until we get to //REMOVED 02.

Reviewing code – REMOVED 02

In this section, we are going to review what we have commented out in //REMOVED 02:

  • //REMOVED 02: This piece of code will take a reference from a raycast-selected game object and turn on that selection game object to show that a selection has been made.
  • Reason for removal: The game object served no benefit apart from serving a cosmetic purpose.
  • Replacement: The buttons still highlight when selected with the selection game object.

Let's continue scrolling down the PlayerShipBuild script until we get to //REMOVED 03.

Reviewing code – REMOVED 03

In this section, we are going to review what we have commented out in //REMOVED 03:

  • //REMOVED 03: This part of the code checks for the player pressing the fire button; if they do, the code will shoot out a raycast to check whether it made contact with a collider.
  • Reason for removal: We no longer need this because game objects are identified in an if statement.
  • Replacement: The OnClick event system holds a reference to what game object is selected.

Let's continue scrolling down the PlayerShipBuild script until we get to //REMOVED 04.

Reviewing code – REMOVED 04

In this section, we are going to review what we have commented out in //REMOVED 04:

  • //REMOVED 04: This script checks what the name of the raycast game object is. Once it's identified through a series of if statements, it runs the method applicable to it.
  • Reason for removal: This section of the code would have checked for specific names our raycast would have made contact with. We no longer use the raycast system now.
  • Replacement: Each button has its own event trigger that runs its own method.

Let's continue scrolling down the PlayerShipBuild script until we get to //REMOVED 05.

Reviewing code – REMOVED 05

In this section, we are going to review what we have commented out in //REMOVED 05:

  • //REMOVED 05: At every frame, it checks whether the player has made a selection in the shop.
  • Reason for removal: Selection is now based on events; we no longer need to check every frame through the Update method.
  • Replacement: The event trigger system.

In the previous sections, we reviewed and amended the way we interacted with the old shop scene's raycast system.

The next phase is to apply methods that can be called directly via an event when a button is pressed in the shop scene.

Adding methods to our PlayerShipBuild script

In this section, we are going to build two main parts so that we can set up our script for 2D UI selection. Thankfully, we have done most of the work for this chapter and all that remains is to make parts of the script public so that our code can be accessed from other sources, that is, our event trigger (OnClick()).

The second thing we are going to do is make our AttemptSelection method receive the game object button so that it will replace the previous target game object.

To confirm this, the target game object was originally used to store ray hits from our raycast system. If you would like to know more about raycast systems, check out Chapter 5, Creating a Shop Scene for Our Game, if this sounds hazy.

Let's start by making the PlayerShipBuild script's methods accessible to other classes:

Information

By default, the accessibility levels for our methods/functions and classes are set to private unless stated otherwise. For more information about accessibility levels, check out the following link: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/accessibility-levels.

  1. Open the PlayerShipBuild script and add public to the following methods:
    • public void WatchAdvert()
    • public void BuyItem()
    • public void StartGame()

These methods are now open to other scripts and the Unity Editor via the Inspector. We will cover this in the next section, but before we do that, we need to amend our AttemptSelection method.

AttemptSelection will be given the same treatment with regard to being a public method, but it will now also take a game object in parameters, which will be the button our script is attached to.

  1. Scroll to the AttemptSelection method and add a public accessibility level, including a game object with the reference name buttonName:

    public void AttemptSelection(GameObject buttonName)

    {

Inside this AttemptSelection method, we check buttonName instead of what we did before by checking target. We then follow the same procedure of turning off any buttons highlighted, then apply the buttonName game object reference to another game object called tmpSelection, which was originally set in the Select method.

  1. Update AttemptSelection with the following code:

    if (buttonName)

            {

              TurnOffSelectionHighlights();

              tmpSelection = buttonName;

Continuing with the next line of code inside our method, we set the button's child selection game object to active (switch it on). The following screenshot shows the child number of the selection game object in the Hierarchy window:

Figure 9.68 – Hierarchy order of game object 00's children

Figure 9.68 – Hierarchy order of game object 00's children

  1. Enter the following code:

    tmpSelection.transform.GetChild(1).gameObject.SetActive(true);

  2. Inside the AttemptSelection method, we now need to change the old target game object's name to the new buttonName game object. The code in bold in the following snippet shows where you need to change the names:

    UpdateDescriptionBox();

    //not sold

    if (buttonName.GetComponentInChildren<Text>().text !=     "SOLD")

    {

        //can afford

        Affordable();

        //can not afford

        LackOfCredits();

    }

    else if (buttonName.GetComponentInChildren<Text>().text     == "SOLD")

    {

        SoldOut();

    }

    }

    }

  3. Save the script and re-enter your Game IDs in the Inspector window for the shop game object. (Edit | Project Settings | Services | Ads )

Remember that if you get stuck with this part, you can always check the Complete folder for this chapter, where you'll have access to the completed files.

So far, we have removed multiple chunks of code and replaced them with a minimal amount that now supports the event triggers from the Unity Editor. This will help with performance and improve the readability of our code. In the next section, we are going to let each of the UI buttons know what methods to run when a selection is made.

Applying trigger events to call methods

In this final section, we are going to make it so that when the player presses a button in the shop, they will get access to it immediately, instead of our script shooting a ray and checking whether and what collider it has made contact with to get access to its method. We will be doing this using Unity's Event system to run methods directly.

To make a button run a method directly, follow these steps:

  1. In the Unity Editor, select the first shop scene button in the selection grid called 00 in the Hierarchy window, as shown in the following screenshot:
Figure 9.69 – Select the 00 game object in the Hierarchy window

Figure 9.69 – Select the 00 game object in the Hierarchy window

  1. With 00 selected, scroll down the Inspector window until you come across the Button component. Within the On Click () panel, click the + icon, as shown in the following screenshot:
Figure 9.70 – In the Button component click the + button to add an On Click () event

Figure 9.70 – In the Button component click the + button to add an On Click () event

  1. The On Click() panel will update from where we need to apply our shop game object to the field that currently says None (Object).
  2. Click and drag the shop game object from the Hierarchy window into the None (Object) field, as shown in the following screenshot:
Figure 9.71 – Drag the shop game object from the Hierarchy into the 00 game object Button's On Click () field

Figure 9.71 – Drag the shop game object from the Hierarchy into the 00 game object Button's On Click () field

The On Click () panel has been updated with the 00 game object. Now, we need to direct what function from 00 it should load.

We will call the AttemptSelection method by making a request when we tap/click one of the shop scene's buttons.

To make our 00 button load the AttemptSelection method, do the following:

  1. Click the No Function field, followed by our PlayerShipBuild script and the AttemptSelection(GameObject) public method, as shown in the following screenshot:
Figure 9.72 – With the shop game object in the field, select the PlayerShipBuild script and then the AttemptSelection function

Figure 9.72 – With the shop game object in the field, select the PlayerShipBuild script and then the AttemptSelection function

The last field to add within the On Click () panel is the button we want to put through AttemptSelection.

  1. Click the remote button on the far right and type the game object we have selected, that is, 00. When you see it in the list, click it.

So, when the player presses the 00 button, our On Click () event will run the PlayerShipBuild script from the shop game object. Then, it will run the AttemptSelection method, taking the 00 game object as a reference in parameters.

  1. Set up the On Click() panel for game objects 01 and 02. Once completed, each On Click() panel will look as follows for all three game objects:
Figure 9.73 – Each game object (00, 01, 02) and their Button component On Click () event property settings

Figure 9.73 – Each game object (00, 01, 02) and their Button component On Click () event property settings

Things are slightly different for our START and AD game object buttons (sat in the Hierarchy window).

To make our AD and START game object buttons work in the game, do the following:

  1. Apply the shop game object to the AD game object's OnClick event in the Inspector window, as we did with the last few game object buttons.
  2. Do the same for the START game object button.
  3. Update each of the START and AD game object's OnClick events, as follows:
    • AD game object: Select PlayerShipBuild, followed by the WatchAdvert method.
    • START game object: Select PlayerShipBuild, followed by the StartGame method. Remember, if you can't see it in the list, make sure it is made public in the PlayerShipBuild script. You know how to do this now. If not, check the Complete folder.
Figure 9.74 – AD and START game object Button On Click () event property values

Figure 9.74 – AD and START game object Button On Click () event property values

The very last button to change is the BUY? button. Follow the same principles that we used before and select the BUY? game object button in the Hierarchy window:

  1. Apply the usual shop game object to its On Click() panel.
  2. Set the script to PlayerShipBuild, followed by BuyItem.

Note that we don't apply event listeners to our bottom row of buttons (Sold Out) as there is no reason to press these buttons.

Our shop scene is now ready to test. Save the scene and press Play in the Unity Editor to try out our new shop buttons. It would also be worth testing different landscape views in the Game window to see the UI buttons pop into shape when a landscape ratio is selected.

The following screenshot shows the steps you have to follow to change the ratio. Do this by clicking the Game tab in the Unity Editor, followed by making a selection from two fairly common ratios:

Figure 9.75 – This screenshot shows two common ratios marked

Figure 9.75 – This screenshot shows two common ratios marked

In this section, we reevaluated our code and took out the old raycast system, which involved selecting 3D game objects to run methods. We replaced this with Unity's Event System, complete with Button components that were dynamically organized with the Horizontal and Vertical Layout Group components.

Now, the UI is more robust since it supports different screen ratios. This will make our game more compatible with a variety of mobile and tablet screens that are old and current, as well as portable devices that haven't been released yet. This helps future-proof our application without any embarrassing ratio issues occurring.

Summary

In this chapter, we looked into two different parts of our game: the in-game HUD and rebuilding our shop scene. Both of these used Unity's UI components but in different ways.

In the in-game HUD section, we read up about what a HUD is and how we can incorporate one into our game. By doing this, we learned how to use Horizontal Layout Group to order images correctly, Render Texture to take a feed from a second camera, and Raw Image to display a feed from Render Texture.

Most importantly, as required by the Unity Programmer Exam, you need to understand what a HUD is and how to build elements into it, such as a mini-map.

In the second part of this chapter, we reviewed our current shop scene's interface and code. We took it apart and rebuilt its interface as a Unity Event system that ran methods directly instead of casting a ray to call a method. We also made the interface support multiple ratios.

With the skills covered in this chapter, you should feel more confident in reviewing and understanding code that could be made more efficient.

In the next chapter, we will continue working on our in-game level so that we can pause the game, add and change the volume of our music and sound effects manually, and more.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset