Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Unity Cookbook - Fifth Edition
Unity Cookbook - Fifth Edition

Unity Cookbook: Over 160 recipes to craft your own masterpiece in Unity 2023, Fifth Edition

By Matt Smith , Shaun Ferns , Sinéad Murphy
$39.99 $27.98
Book Nov 2023 780 pages 5th Edition
eBook
$39.99 $27.98
Print
$49.99
Subscription
$15.99 Monthly
eBook
$39.99 $27.98
Print
$49.99
Subscription
$15.99 Monthly

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Nov 30, 2023
Length 780 pages
Edition : 5th Edition
Language : English
ISBN-13 : 9781805123026
Vendor :
Unity Technologies
Category :
Concepts :
Table of content icon View table of contents Preview book icon Preview Book

Unity Cookbook - Fifth Edition

Responding to User Events for Interactive UIs

Almost all the recipes in this chapter involve different interactive UI controls. Although there are different kinds of interactive UI controls, the basic way to work with them, as well as to have scripted actions respond to user actions, is all based on the same idea: events triggering the execution of object method functions.

Then, for fun, and as an example of a very different kind of UI, the final recipe will demonstrate how to add sophisticated, real-time communication for the relative positions of objects in the scene to your game - in the form of a radar!

The UI can be used for three main purposes:

  • To display static (unchanging) values, such as the name or logo image of the game, or word labels such as Level and Score, that tell us what the numbers next to them indicate (the recipes for these can be found in Chapter 1, Displaying Data with Core UI Elements).
  • To display values that change due to our scripts, such as timers, scores, or the distance from our Player character to some other object (an example of this is the radar recipe at the end of this chapter, Displaying a radar to indicate the relative locations of objects).
  • Interactive UI controls, whose purpose is to allow the player to communicate with the game scripts via their mouse or touchscreen. These are the ones we’ll look at in detail in this chapter.

The core concept of working with Unity interactive UI controls is to register an object’s public method so that we’re informed when a particular event occurs. For example, we can add a UI dropdown to a scene named DropDown1, and then write a MyScript script class containing a NewValueAction() public method to perform an action. However, nothing will happen until we do two things:

  • We need to add an instance of the script class as a component of a GameObject in the scene (which we’ll name go1 for our example – although we can also add the script instance to the UI GameObject itself if we wish to).
  • In the UI dropdown’s properties, we need to register the GameObject’s public method of its script component so that it responds to On Value Changed event messages.

The NewValueAction() public method of the MyScript script will typically retrieve the value that’s been selected by the user in the dropdown and do something with it. For example, the NewValueAction() public method might confirm the value to the user, change the music volume, or change the game’s difficulty. The NewValueAction() method will be executed each time GameObject go1 receives the NewValueAction() message. In the properties of DropDown1, we need to register go1's scripted component – that is, MyScript's NewValueAction() public method – as an event listener for On Value Changed events. We need to do all this at design time (that is, in the Unity Editor before running the scene):

A screenshot of a design

Description automatically generated

Figure 2.1: Graphical representation of the UI at design time

At runtime (when the scene in the application is running), the following will happen:

  1. If the user changes the value in the drop-down menu of the DropDown1 GameObject (step 1 in the following diagram), this will generate an On Value Changed event.
  2. DropDown1 will update its display on the screen to show the user the newly selected value (step 2a). It will also send messages to all the GameObject components registered as listeners to On Value Changed events (step 2b).
  3. In our example, this will lead to the NewValueAction() method in the go1 GameObject’s scripted component being executed (step 3).
A diagram of a run-time

Description automatically generated

Figure 2.2: Graphical representation of the UI at runtime

Registering public object methods is a very common way to handle events such as user interaction or web communications, which may occur in different orders, may never occur, or may happen several times in a short period. Several software design patterns describe ways to work with these event setups, such as the Observer pattern and the Publisher-Subscriber design pattern (more details can be found at https://unity.com/how-to/create-modular-and-maintainable-code-observer-pattern).

Core GameObject components related to interactive Unity UI development include the following:

  • Visual UI controls: The visible UI controls themselves include Button, Image, Text, and Toggle. These are the UI controls the user sees on the screen and uses their mouse/touchscreen to interact with. These are the GameObjects that maintain a list of object methods that have subscribed to user-interaction events.
  • Interaction UI controls: These are non-visible components that are added to GameObjects; examples include Input Field and Toggle Group.
  • Panel: UI objects can be grouped together (logically and physically) with UI Panels. Panels can play several roles, including providing a GameObject parent in the Hierarchy window for a related group of controls. They can provide a visual background image to graphically relate controls on the screen, and they can also have scripted resize and drag interactions added if desired.

In addition, the concept of sibling depth is important when multiple UI components are overlapping. The bottom-to-top display order (what appears on the top of what) for a UI element is determined initially by its place in the sequence in the Hierarchy window. At design time, this can be manually set by dragging GameObjects into the desired sequence in the Hierarchy window. At runtime, we can send messages to the Rect Transforms of GameObjects to dynamically change their Hierarchy position (and, therefore, the display order) as the game or user interaction demands. This is illustrated in the Organizing images inside panels and changing panel depths via buttons recipe in this chapter.

Often, a UI element exists with most of the components that you may need for something in your game, but you may need to adapt it somehow. An example of this can be seen in the Displaying a countdown timer graphically with a UI Slider recipe, which makes a UI Slider non-interactive so as to display a red-green progress bar for the status of a countdown timer.

In this chapter, we will cover the following recipes:

  • Creating a UI Button to reveal an image
  • Creating a UI Button to move between scenes
  • Animating UI Button properties on mouseover
  • Organizing image panels and changing panel depths via UI Buttons
  • Displaying the value of an interactive UI Slider
  • Displaying a countdown timer graphically with a UI Slider
  • Setting custom mouse cursors for 2D and 3D GameObjects
  • Setting custom mouse cursors for UI controls
  • Interactive text entry with Input Field
  • Detecting interactions with a single Toggle UI component
  • Creating related radio buttons using UI Toggles
  • Creating text UI Dropdown menus
  • Creating image icon UI Dropdown menus
  • Displaying a radar to indicate the relative locations of objects.

Creating a UI Button to reveal an image

In this recipe, we’ll create a button that, when pressed, will make an image appear.

Getting ready

For this recipe, we have prepared the image that you need in a folder named Images in the 02_01 folder.

How to do it...

To create a UI Button to reveal an image, follow these steps:

  1. Create a new Unity 2D project.
  2. Import the unity_logo image into the Project folder.
  3. Drag the unity_logo image into the scene and in the Inspector panel, set the scale property to X 2 Y 2, and uncheck the check box as per the screenshot to make this GameObject inactive. An inactive GameObject is not displayed, and nor does it have any active behavior (so no scripts run and it does not respond to any event messages).
  4. Load the TextMeshPro Essential Resources, by choosing Window | TextMeshPro | Import TMP Essential Resources.
A screenshot of a computer

Description automatically generated with medium confidence

Figure 2.3: Inspector properties for an image in the scene

  1. In the Hierarchy window, right-click and select Create | UI | Button-TextMeshPro.
  2. Select GameObject Button in the Hierarchy window and click on the plus (+) button at the bottom of the Inspector window, to create a new OnClick event handler.
  3. Drag the unity_logo image from the Hierarchy window over the Object slot immediately below the menu that says Runtime Only.
  4. Select the SetActive (bool) method from the GameObject drop-down list (initially showing No Function) and click on the checkbox.
A screenshot of a computer

Description automatically generated with medium confidence

Figure 2.4: Settings for the OnClick event handler for the button

  1. Save your changes and run the scene. When you click the button, the image will appear.

How it works...

In this recipe, you created a new scene, imported an image, and unchecked the Visible in Runtime checkbox for the image so that it would not be seen at runtime.

You added a UI Button and a new OnClick event action that executes the GameObject.SetActive() method of the GameObject drop-down list of the button, and you checked the box so that the image (unity_logo) appears when the button is clicked.

There’s more...

As an alternative to having the image appear when clicking on a button, the same button could be used to make an image disappear by the checkbox on the image.

Creating a UI Button to move between scenes

The majority of games include menu screens that display settings, buttons to start the game playing, messages to the user about instructions, high scores, the level they have reached so far, and so on. Unity provides UI Buttons to offer users a simple way to interact with the game and its settings.

Figure 2.5: Example of a main menu UI Button

In this recipe, we’ll create a very simple game consisting of two screens, each with a button to load the other one, as illustrated in the preceding screenshot.

How to do it...

To create a button-navigable multi-scene game, follow these steps:

  1. Create a new Unity 2D project.
  2. Save the current (empty) scene in a new folder called Scenes, naming the scene page1.
  3. Load the TextMeshPro Essential Resources, by choosing Window | TextMeshPro | Import TMP Essential Resources.
  4. In the Hierarchy panel, add a Text (TMP) GameObject to the scene positioned at the top center of the scene containing large white text that says Main Menu (page 1). Having added a Text (TMP) GameObject to the scene, in the Inspector for the Rect Transform component, click on the Anchor Presets square icon, which will result in several rows and columns of preset position squares appearing. Holding down Shift + Alt, click on the top row and the center column.
  5. Add a UI Button-TextMeshPro to the scene positioned in the middle-center of the screen.
  6. In the Hierarchy window, click on the Tree View toggle triangle to display the Text child of this GameObject button. Select the Text GameObject and, in the Inspector window for the Text Input property, enter the text goto page 2:
A screenshot of a computer

Description automatically generated

Figure 2.6: UI Button Text child

  1. Create a second scene, named page2, with the UI text Instructions (page 2) and a UI Button-TextMeshPro with the text goto page 1. You can either repeat the preceding steps or duplicate the page1 scene file, name the duplicate page2, and then edit the UI TMP text and UI Button text appropriately.
  2. Add both scenes to the build, which is the set of scenes that will end up in the actual application built by Unity. Open the Build Settings panel by choosing File | Build Settings.... Then drag the two scenes from the Project panel into the top section (Scenes in Build) of the Build Settings panel. Ensure the sequence goes page1 first, then page2 second – drag to rearrange them if necessary.

    We cannot tell Unity to load a scene that has not been added to the list of scenes in the build. This makes sense since when an application is built, we should never try to open a scene that isn’t included as part of that application. The scene that appears first (index 0) in the Build Settings panel will be the first scene opened when the game is run.

  1. Ensure you have the page1 scene open.
  2. Create a new empty GameObject named SceneManager.
  3. Create a C# script class called SceneLoader, in a new folder called _Scripts, that contains the following code. Then, add an instance of SceneLoader as a scripted component to the SceneManager GameObject:
    using UnityEngine;
    using UnityEngine.SceneManagement;
    public class SceneLoader : MonoBehaviour {
        public void LoadOnClick(int sceneIndex) {
            SceneManager.LoadScene(sceneIndex);
        }
    }
    
  4. Select the Button GameObject in the Hierarchy panel and click on the plus (+) button at the bottom of the Button (Script) component in the Inspector. This will create a new OnClick event handler for this button (that is, a method to execute when the button is clicked).
  5. Drag the SceneManager GameObject from the Hierarchy window over the Object slot immediately below the menu that says Runtime Only. This means that when the button receives an OnClick event, we can call a public method from a scripted object inside SceneManager.
  6. Select the LoadOnClick(int) method from the SceneLoader drop-down list. Type 1 (the index of the scene we want to be loaded when this button is clicked) in the text box, below the method’s drop-down menu.

    This integer, 1, will be passed to the method when the button receives an OnClick event message, as shown here:

    A screenshot of a computer

Description automatically generated

    Figure 2.7: Button (Script) settings

    Save the current scene (page1).

  1. Open page2 and follow the same steps to make the page2 button load page1. That is, create a new empty SceneManager GameObject, add an instance of the SceneLoader script class to SceneManager, and then add an OnClick event action to the button that calls LoadOnClick and passes an integer of 0 so that page1 is loaded.
  2. Save page2.
  3. When you run the page1 scene, you will be presented with your Main Menu text and a button that, when clicked, makes the game load the page2 scene. On page2, you’ll have a button to take you back to page1.

How it works...

In this recipe, you created two scenes and added both of these scenes to the game’s build. You added a UI Button and some UI Text to each scene.

When a UI Button is added to the Hierarchy window, a child UI Text object is also automatically created, and the content of the Text Input property of this UI Text child is the text that the user sees on the button.

Here, you created a script class and added an instance as a component to GameObject SceneManager. In fact, it didn’t really matter where this script instance was added, so long as it was in one of the GameObjects of the scene. This is necessary since the OnClick event action of a button can only execute a method (function) of a component in a GameObject in the scene.

For the buttons for each scene, you added a new OnClick() event handler that invokes (executes) the LoadOnClick() method of the SceneLoader scripted component in SceneManager. This method inputs the integer index of the scene in the project’s Build Settings so that the button on the page1 scene gives integer 1 as the scene to be loaded and the button for page2 gives integer 0.

There’s more...

There are several ways in which we can visually inform the user that the button is interactive when they move their mouse over it. The simplest way is to add a Color Tint that will appear when the mouse is over the button – this is the default Transition. With Button selected in the Hierarchy window, choose a tint color (for example, red), for the Highlighted Color property of the Button (Script) component in the Inspector panel:

Figure 2.8: Adjusting the mouseover settings for buttons

Another form of visual transition to inform the user of an active button is Sprite Swap. In this case, the properties of different images for Targeted/Highlighted/Pressed/Disabled are available in the Inspector window. The default target graphic is the built-in Unity Button (Image) – this is the gray rounded rectangle default when GameObject buttons are created. Dragging in a very different-looking image for the highlighted sprite is an effective alternative to setting a Color Tint:

Figure 2.9: Example of an image as a button

We have provided a rainbow.png image in a folder named 02_02 that can be used for the button mouseover’s Highlighted sprite. You will need to ensure this image asset has its Texture Type set to Sprite (2D and UI) in the Inspector window. The preceding screenshot shows the button with this rainbow background image.

Animating UI Button properties on mouseover

At the end of the previous recipe, we illustrated two ways to visually communicate buttons to users. The animation of button properties can be a highly effective and visually interesting way to reinforce to the user that the item their mouse is currently over is a clickable, active button. One common animation effect is for a button to become larger when the mouse is over it and then shrink back to its original size when the mouse is moved away. Animation effects are achieved by choosing the Animation option for the Transition property of a Button GameObject, and by creating an Animation Controller with triggers for the Normal, Highlighted, Pressed, and Disabled states.

How to do it...

To animate a button for enlargement when the mouse is over it (the Highlighted state), do the following:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Create a UI Button-TextMeshPro GameObject.
  3. In the Inspector panel, for the Button component, set the Transition property to Animation.
  4. Click the Auto Generate Animation button (just below the Disabled Trigger property) for the Button (Script) component. This will create a new Animator Controller asset file defining some default animations for each of the button states.

Figure 2.10: Auto Generate Animation

  1. Save the new controller (in a new folder called Animations), naming it button-animation-controller.
  2. Ensure that the Button GameObject is selected in the Hierarchy window. Open Window | Animation | Animation. In the Animation window, select the Highlighted clip from the drop-down menu:

Figure 2.11: Selecting the Button GameObject in the Hierarchy window

  1. In the Animation window, click on the red record circle button, and then click on the Add Property button, choosing to record changes to the Rect Transform | Scale property.
  2. Two keyframes will have been created. Delete the second one at 1:00 (since we don’t want a “bouncing” button):

Figure 2.12: Deleting the keyframe

  1. Select the frame at 1:00 by clicking one of the diamonds (both turn blue when selected), and then press the Backspace/Delete key.
  2. Select the first keyframe at 0:00 (the only one now!). In the Inspector window, set the X and Y scale properties of the Rect Transform component to (1.2, 1.2).
  3. Click on the red record circle button for the second time to stop recording the animation changes.
  4. Save and run your scene. You will see that the button smoothly animates and becomes larger when the mouse is over it, and then smoothly returns to its original size when the mouse has moved away.

How it works...

In this recipe, you created a button and set its Transition mode to Animation. This makes Unity require an Animation Controller with four states: Normal, Highlighted, Pressed, and Disabled. You then made Unity automatically create an Animation Controller with these four states.

Then, you edited the animation for the Highlighted (mouseover) state, deleting the second keyframe, and making the only keyframe a version of the button that’s larger so that its scale is 1.2. So, as is the case, if the GameObject has a Scale of 1 initially, when animating it will be scaled up to 1.2.

When the mouse is not hovering over the button, it’s unchanged, and the Normal state settings are used. When the mouse moves over the button, the Animation Controller smoothly modifies the settings of the button to become those of its Highlighted state (that is, bigger). When the mouse is moved away from the button, the Animation Controller smoothly modifies the settings of the button to become those of its Normal state (that is, its original size).

The following web pages offer video and web-based tutorials on UI animations:

Organizing image panels and changing panel depths via buttons

UI Panels are provided by Unity to allow UI controls to be grouped and moved together, and also to visually group elements with an image background (if desired). The sibling depth is what determines which UI elements will appear above or below others. We can see the sibling depth explicitly in the Hierarchy window, since the top-to-bottom sequence of UI GameObjects in the Hierarchy window sets the sibling depth. So, the first item has a depth of 1, the second has a depth of 2, and so on. The UI GameObjects with larger sibling depths (further down the hierarchy, which means they’re drawn later) will appear above the UI GameObjects with smaller sibling depths:

Figure 2.13: Example of organizing panels

In this recipe, we’ll begin by creating two UI Panels, each showing a different playing card image, and we’ll use one button to move between them. We’ll then expand the recipe by creating one more UI Panel. We’ll also add four triangle arrangement buttons to change the display order (move to bottom, move to top, move up one, and move down one).

Getting ready

For this recipe, we have prepared the images that you need in a folder named Images/ornamental_deck-png and Images /icons in the 02_04 folder.

How to do it...

To create the UI Panels whose layering can be changed by clicking buttons, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Create a new UI Panel GameObject named Panel-jack-diamonds. Do the following to this panel:
    • For the Image (Script) component, drag the jack_of_diamonds playing card image asset file from the Project window into the Source Image property. Select the Color property and increase the Alpha value to 255 (so that this background image of the panel is no longer partly transparent).
    • For the Rect Transform property, position it in the middle-center part of the screen and set its Width to 200 and its Height to 300.
  3. Create a UI Button-TextMeshPro GameObject named Button-move-to-front. In the Hierarchy window, make this button a child of Panel-jack-diamonds. Delete the Text child GameObject of this button (since we’ll use an icon to indicate what this button does).
  4. With the Button-move-to-front GameObject selected in the Hierarchy window, do the following in the Inspector window:
    • In Rect Transform, position the button at the top-center of the player card image so that it can be seen at the top of the playing card. Size the image to Width = 16 and Height = 16. Move the icon image down slightly, by setting Pos Y = -5 (to ensure we can see the horizontal bar above the triangle).
    • For the Source Image property of the Image (Script) component, select the arrangement triangle icon image; that is, icon_move_to_front.
    • Add an OnClick event handler by clicking on the plus (+) sign at the bottom of the Button (Script) component.
    • Drag Panel-jack-diamonds from the Hierarchy window over to the Object slot (immediately below the menu saying Runtime Only).
    • Select the RectTransform.SetAsLastSibling method from the drop-down function list (initially showing No Function):
    A screenshot of a computer

Description automatically generated

    Figure 2.14: Addition of an OnClick event handler

  5. Repeat step 2 to create a second panel named Panel-2-diamonds with its own move-to-front button and a Source Image of 2_of_diamonds. Move and position this new panel slightly to the right of Panel-jack-diamonds, allowing both move-to-front buttons to be seen.
  6. Save your scene and run the game. You will be able to click the move-to-front button on either of the cards to move that card’s panel to the front. If you run the game with the Game window not maximized, you’ll actually see the panels changing the order in the list of the children of Canvas in the Hierarchy window.

How it works...

In this recipe, you created two UI Panels, each of which contains a background image of a playing card and a UI Button whose action will make its parent panel move to the front. You set the Alpha (transparency) setting of the background image’s Color setting to 255 (no transparency).

You then added an OnClick event handler to the button of each UI Panel. This action sends a SetAsLastSibling message to the button’s panel parent. When the OnClick message is received, the clicked panel is moved to the bottom (end) of the sequence of GameObjects in the Canvas, so this panel is drawn last from the Canvas objects. This means that it appears visually in front of all the other GameObjects.

The button’s action illustrates how the OnClick function does not have to be calling a public method of a scripted component of an object, but it can be sending a message to one of the non-scripted components of the targeted GameObject. In this recipe, we send the SetAsLastSibling message to the Rect Transform component of the panel where the button is located.

There’s more...

There are some details you don’t want to miss.

Moving up or down by just one position, using scripted methods

While Rect Transform offers SetAsLastSibling (move to front) and SetAsFirstSibling (move to back), and even SetSiblingIndex (if we knew exactly what position in the sequence to type in), there isn’t a built-in way to make an element move up or down just one position in the sequence of GameObjects in the Hierarchy window.

However, we can write two straightforward methods in C# to do this, and we can add buttons to call these methods, providing full control of the top-to-bottom arrangement of the UI controls on the screen. To implement four buttons (move-to-front/move-to-back/up one/down one), do the following:

  1. Create a C# script class called ArrangeActions containing the following code and add an instance as a scripted component to each of your UI Panels:
    using UnityEngine;
    public class ArrangeActions : MonoBehaviour {
       private RectTransform panelRectTransform;
       void Awake() {
             panelRectTransform = GetComponent<RectTransform>();
       }
    	public void MoveDownOne() {
    		print("(before change) " + gameObject.name + " sibling index = " + panelRectTransform.GetSiblingIndex());
    		int currentSiblingIndex = panelRectTransform.GetSiblingIndex();
    		if (currentSiblingIndex > 0) {}
    			panelRectTransform.SetSiblingIndex(currentSiblingIndex - 1);
    		}
    		print("(after change) " + gameObject.name + " sibling index = " + panelRectTransform.GetSiblingIndex());
    	}
    	
    	public void MoveUpOne() {
    		print ("(before change) " + gameObject.name +  " sibling index = " + panelRectTransform.GetSiblingIndex());
    		
    		int currentSiblingIndex = panelRectTransform.GetSiblingIndex();
    		int maxSiblingIndex = panelRectTransform.childCount - 1;
    		if (currentSiblingIndex < maxSiblingIndex) {
    			panelRectTransform.SetSiblingIndex(currentSiblingIndex + 1);
    		}
    		print ("(after change) " + gameObject.name +  " sibling index = " + panelRectTransform.GetSiblingIndex());
    	}
    }
    
  2. Add a second UI Button to each card panel, this time using the arrangement triangle icon image called icon_move_to_back, and set the OnClick event function for these buttons to SetAsFirstSibling.
  3. Add two more UI Buttons to each card panel with the up and down triangle icon images; that is, icon_up_one and icon_down_one. Set the OnClick event handler function for the down-one buttons to call the MoveDownOne() method and set the function for the up-one buttons to call the MoveUpOne() method.
  4. Copy one of the UI Panels to create a third card (this time showing the ace of diamonds). Arrange the three cards so that you can see all four buttons for at least two of the cards, even when those cards are at the bottom (see the screenshot at the beginning of this recipe).
  5. Save the scene and run your game. You will now have full control over how to layer the three card UI Panels.

Note that the MoveDownOne() and MoveUpOne() methods subtract and add 1 to the sibling depth of the panel the scripted object is a component of. The methods contain a test, so ensure we don’t try to set a negative panel depth or a depth that is higher than the maximum index for the number of panels.

Displaying the value of an interactive UI Slider

A UI Slider is a graphical tool that allows a user to set the numerical value of an object.

A picture containing icon

Description automatically generated

Figure 2.15: Example of a UI Slider offering a range of 0 to 20

This recipe illustrates how to create an interactive UI Slider and execute a C# method each time the user changes the UI Slider value.

How to do it...

To create a UI Slider and display its value on the screen, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Add a UI Text-TextMeshPro GameObject to the scene with a Font size of 30 and placeholder text, such as Slider value here (this text will be replaced with the slider value when the scene starts). Set Overflow to Overflow. Since we may change the font and message at a later date, it’s useful to allow overflow to prevent some of the message being truncated.
  3. In the Hierarchy window, add a UI Slider GameObject to the scene by going to GameObject | UI | Slider.
  4. In the Inspector window, modify the settings for the position of the UI Slider GameObject’s Rect Transform to the top-middle part of the screen.
  5. In the Inspector window, modify the settings of Position for the UI Text’s Rect Transform so that they’re just below the slider (top, middle, then Pos Y = -30).
  6. In the Inspector window, set the UI Slider’s Min Value to 0 and Max Value to 20. Then, check the Whole Numbers checkbox:

Figure 2.16: Setting the UI Slider’s Min Value and Max Value

  1. Create a C# script class called SliderValueToText containing the following code and add an instance as a scripted component to the Text(TMP)GameObject:
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    public class SliderValueToText : MonoBehaviour {
       public Slider sliderUI;
       private TextMeshProUGUI textSliderValue;
       void Awake() {
             textSliderValue = GetComponent<TextMeshProUGUI>();
       }
       void Start() {
             ShowSliderValue();
       }
       public void ShowSliderValue () {
             string sliderMessage = "Slider value = " + sliderUI.value;
             textSliderValue.text = sliderMessage;
       }
    }
    
  2. Ensure that the Text(TMP) GameObject is selected in the Hierarchy window. Then, in the Inspector window, drag the Slider GameObject into the public Slider UI variable slot for the Slider Value To Text (Script) scripted component:
A red arrow pointing to text

Description automatically generated

Figure 2.17: Dragging Slider into the Slider UI variable

  1. Ensure that the Slider GameObject is selected in the Hierarchy window. Then, in the Inspector window, add an OnValue Changed (Single) event handler by clicking on the plus (+) sign at the bottom of the Slider component.
  2. Drag the Text(TMP) GameObject from the Hierarchy window over to the Object slot (immediately below the menu that says Runtime Only), as shown in the following screenshot:
    A screenshot of a computer

Description automatically generated

    Figure 2.18: Dragging the Text GameObject into None (Object)

    You have now told Unity which object a message should be sent to each time the slider is changed.

  1. From the drop-down menu, select SliderValueToText and the ShowSliderValue method, as shown in the following screenshot. This means that each time the slider is updated, the ShowSliderValue() method, in the scripted object in the Text(TMP) GameObject, will be executed:

Figure 2.19: Drop-down menu for On Value Changed

  1. When you run the scene, you will see a UI Slider. Below it, you will see a text message in the form Slider value = <n>.
  2. Each time the UI Slider is moved, the text value that’s shown will be (almost) instantly updated. The values should range from 0 (the leftmost of the slider) to 20 (the rightmost of the slider).

How it works...

In this recipe, you created a UI Slider GameObject and set it to contain whole numbers in the range of 0 to 20.

You also added an instance of the SliderValueToText C# script class to the UI Text(TMP) GameObject.

The Awake() method caches references to the Text component in the textSliderValue variable.

The Start() method invokes the ShowSliderValue() method so that the display is correct when the scene begins (that is, the initial slider value is displayed).

The ShowSliderValue() method gets the value of the slider and then updates the text that’s displayed to be a message in the form of Slider value = <n>.

Finally, you added the ShowSliderValue() method of the SliderValueToText scripted component to the Slider GameObject’s list of On Value Changed event listeners. So, each time the slider value changes, it sends a message to call the ShowSliderValue() method so that the new value is updated on the screen.

Displaying a countdown timer graphically with a UI Slider

There are many cases where we wish to inform the player of how much time is left in a game or how much longer an element will take to download – for example, a loading progress bar, the time or health remaining compared to the starting maximum, or how much the player has filled up their water bottle from the fountain of youth.

In this recipe, we’ll illustrate how to remove the interactive “handle” of a UI Slider, and then change the size and color of its components to provide us with an easy-to-use, general-purpose progress/proportion bar:

A screenshot of a computer

Description automatically generated

Figure 2.20: Example of a countdown timer with a UI Slider

In this recipe, we’ll use our modified UI Slider to graphically present to the user how much time remains on a countdown timer.

Getting ready

For this recipe, we have prepared the script and images that you need in the 02_04 folder, respectively named _Scripts and Images.

How to do it...

To create a digital countdown timer with a graphical display, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Import the CountdownTimer script and the red_square and green_square images into this project.
  3. Add a UI Text(TMP) GameObject to the scene with a Font size of 30 and placeholder text such as a UI Slider value (this text will be replaced with the slider value when the scene starts). Check that Overflow is set to Overflow.
  4. In the Hierarchy window, add a Slider GameObject to the scene by going to GameObject | UI | Slider.
  5. In the Inspector window, modify the settings for the position of the Slider GameObject’s Rect Transform to the top-center part of the screen.
  6. Ensure that the Slider GameObject is selected in the Hierarchy window.
  7. Deactivate the Handle Slide Area child GameObject (by unchecking it).
  8. You’ll see the “drag circle” disappear in the Game window (the user will not be dragging the slider since we want this slider to be display-only):
A screenshot of a computer

Description automatically generated

Figure 2.21: Ensuring Handle Slide Area is deactivated

  1. Select the Background child and do the following:
    • Drag the red_square image into the Source Image property of the Image component in the Inspector window.
  2. Select the Fill child of the Fill Area child and do the following:
    • Drag the green_square image into the Source Image property of the Image component in the Inspector window.
  3. Select the Fill Area child and do the following:
    • In the Rect Transform component, use the Anchors preset position of left-middle.
    • Set Width to 155 and Height to 12:

Figure 2.22: Selections in the Rect Transform component

  1. Create a C# script class called SliderTimerDisplay that contains the following code and add an instance as a scripted component to the Slider GameObject:
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    [RequireComponent(typeof(CountdownTimer))]
    public class SliderTimerDisplay : MonoBehaviour {
       private CountdownTimer countdownTimer;
       private Slider sliderUI;
       void Awake() {
             countdownTimer = GetComponent<CountdownTimer>();
             sliderUI = GetComponent<Slider>();
       }
       void Start() {
             SetupSlider();
             countdownTimer.ResetTimer( 30 );
       }
       void Update () {
             sliderUI.value = countdownTimer.GetProportionTimeRemaining();
             print (countdownTimer.GetProportionTimeRemaining());
       }
       private void SetupSlider () {
             sliderUI.minValue = 0;
             sliderUI.maxValue = 1;
             sliderUI.wholeNumbers = false;
       }
    }
    

Run your game. You will see the slider move with each second, revealing more and more of the red background to indicate the time remaining.

How it works...

In this recipe, you hid the Handle Slide Area child so that the UI Slider is for display only, which means it cannot be interacted with by the user. The Background color of the UI Slider was set to red so that, as the counter goes down, more and more red is revealed, warning the user that the time is running out.

The Fill property of the UI Slider was set to green so that the proportion remaining is displayed in green – the more green that’s displayed, the greater the value of the slider/timer.

An instance of the provided CountdownTimer script class was automatically added as a component to the UI Slider via [RequireComponent(...)].

The Awake() method caches references to the CountdownTimer and Slider components in the countdownTimer and sliderUI variables.

The Start() method calls the SetupSlider() method and then resets the countdown timer so that it starts counting down from 30 seconds.

The SetupSlider() method sets up this slider for float (decimal) values between 0.0 and 1.0.

In each frame, the Update() method sets the slider value to the float that’s returned by calling the GetProportionRemaining() method from the running timer. At runtime, Unity adjusts the proportion of red/green that’s displayed in the UI Slider so that it matches the slider’s value.

Setting custom mouse cursors for 2D and 3D GameObjects

Cursor icons are often used to indicate the nature of the interactions that can be done with the mouse. Zooming, for instance, might be illustrated by a magnifying glass; shooting, on the other hand, is usually represented by a stylized target or reticle:

A picture containing text

Description automatically generated

Figure 2.23: Mouse pointer represented as a stylized target

The preceding screenshot shows an example of the Unity logo with the cursor represented as a stylized target. In this recipe, we will learn how to implement custom mouse cursor icons to better illustrate your gameplay – or just to escape the Windows, macOS, and Linux default UI.

Getting ready

For this recipe, we have prepared the folders that you’ll need in the 02_07 folder.

How to do it...

To make a custom cursor appear when the mouse is over a GameObject, follow these steps:

  1. Create a new Unity 2D project.
  2. Import the provided folder, called Images. Select the unity_logo image in the Project window. Then, in the Inspector window, change Texture Type to Sprite (2D and UI). This is because we’ll use this image for a 2D Sprite GameObject and it requires this Texture Type (it won’t work with the Default type).
  3. Go to GameObject | 2D Object | Sprites | Square to add the necessary GameObject to the scene. Name this New Sprite, if this wasn’t the default name when it was created:
    • In the Inspector window, set the Sprite property of the Sprite Renderer component to the unity_logo image. In the GameObject’s Transform component, set the scaling to (3,3,3) and, if necessary, reposition Sprite so that it’s centered in the Game window when the scene runs.
    • Go to Component | Physics 2D | Box Collider 2D to create a Box Collider and add it to the Sprite GameObject. This is needed for this GameObject to receive OnMouseEnter and OnMouseExit event messages.
  4. Import the provided folder called IconsCursors. Select all three images in the Project window and, in the Inspector window, change Texture Type to Cursor. This will allow us to use these images as mouse cursors without any errors occurring.
  5. Create a C# script class called CustomCursorPointer containing the following code and add an instance as a scripted component to the New Sprite GameObject:
    using UnityEngine;
    public class CustomCursorPointer : MonoBehaviour {
      public Texture2D cursorTexture2D;
      private CursorMode cursorMode = CursorMode.Auto;
      private Vector2 hotSpot = Vector2.zero;
      public void OnMouseEnter() {
        SetCustomCursor(cursorTexture2D);
      }
      public void OnMouseExit() {
        SetCustomCursor(null);
      }
      private void SetCustomCursor(Texture2D curText){
        Cursor.SetCursor(curText, hotSpot, cursorMode);
      }
    }
    

    The OnMouseEnter() and OnMouseExit() event methods have been deliberately declared as public. This will allow these methods to also be called from UI GameObjects when they receive the OnPointerEnterExit events.

  1. With the New Sprite item selected in the Hierarchy window, drag the CursorTarget image into the public Cursor Texture 2D variable slot in the Inspector window for the Custom Cursor Pointer (Script) component:

Figure 2.24: Cursor Texture 2D dragged to the variable slot

  1. Save and run the current scene. When the mouse pointer moves over the Unity logo sprite, it will change to the custom CursorTarget image that you chose.

How it works...

In this recipe, you created a Sprite GameObject and assigned it the Unity logo image. You imported some cursor images and set their Texture Type to Cursor so that they can be used to change the image for the user’s mouse pointer. You also added a Box Collider to the Sprite GameObject so that it would receive OnMouseEnter and OnMouseExit event messages.

Then, you created the CustomCursorPointer script class and added an instance object of this class to the Sprite GameObject. This script tells Unity to change the mouse pointer when an OnMouseEnter message is received – that is, when the user’s mouse pointer moves over the part of the screen where the Unity logo’s sprite image is being rendered. When an OnMouseExit event is received (the user’s mouse pointer is no longer over the cube part of the screen), the system is told to go back to the operating system’s default cursor. This event should be received within a few milliseconds of the user’s mouse exiting from the collider.

Finally, you selected the CursorTarget image to be the custom mouse cursor image the user sees when the mouse is over the Unity logo image.

Setting custom mouse cursors for UI controls

The previous recipe demonstrated how to change the mouse pointer for 2D and 3D GameObjects receiving OnMouseEnter and OnMouseExit events. Unity UI controls do not receive OnMouseEnter and OnMouseExit events. Instead, UI controls can be made to respond to PointerEnter and PointerExit events if we add a special Event Trigger component to the UI GameObject:

Figure 2.25: Mouse pointer as a magnifying glass cursor

In this recipe, we’ll change the mouse pointer to a custom magnifying glass cursor when it moves over a UI Button GameObject.

Getting ready

For this recipe, we’ll use the same asset files as we did for the previous recipe, as well as the CustomCursorPointer C# script class from that recipe, all of which can be found in the 02_08 folder.

How to do it...

To set a custom mouse pointer when the mouse moves over a UI control GameObject, do the following:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Import the provided IconsCursors folder. Select all three images in the Project window and, in the Inspector window, change Texture Type to Cursor. This will allow us to use these images as mouse cursors without any errors occurring.
  3. Import the provided _Scripts folder containing the CustomCursorPointer C# script class.
  4. Add a UI Button-TextMeshPro GameObject to the scene, leaving this named Button.
  5. Add an instance of the CustomCursorPointer C# script class to the Button GameObject.
  6. With the Button GameObject selected in the Hierarchy window, drag the CursorZoom image into the public Cursor Texture 2D variable slot in the Inspector window for the Customer Cursor Pointer (Script) component.
  7. In the Inspector window, add an Event Trigger component to the Button GameObject by going to Add Component | Event | Event Trigger.
  8. Add a PointerEnter event to your Event Trigger component, click on the plus (+) button to add an event handler slot, and drag the Button GameObject into the Object slot.
  9. From the Function drop-down menu, choose CustomCursorPointer and then choose the OnMouseEnter method:

Figure 2.26: Event Trigger settings

  1. Add a Pointer Exit event to your Event Trigger component, and make it call the OnMouseExit() method from CustomCursorPointer when this event is received.
  2. Save and run the current scene. When the mouse pointer moves over our UI Button, it will change to the custom CursorZoom image that you chose.

How it works...

In this recipe, you imported some cursor images and set their Texture Type to Cursor so that they could be used to change the image for the user’s mouse pointer. You also created a UI Button GameObject and added to it an Event Trigger component.

You then added an instance of the CustomCursorPointer C# script class to the Button GameObject and selected the magnifying-glass-style CursorZoom image.

After that, you created a PointerEnter event and linked it to invoke the OnMouseEnter method of the instance of the CustomCursorPointer script in the Button GameObject (which changes the mouse pointer image to the custom mouse cursor).

Finally, you created a PointerExit event and linked it to invoke the OnMouseExit method of the instance of the CustomCursorPointer C# script class to the Button GameObject (which resets the mouse cursor back to the system default).

Essentially, you have redirected PointerEnter/Exit events to invoke the OnMouseEnter/Exit methods of the CustomCursorPointer C# script class so that we can manage custom cursors for 2D, 3D, and UI GameObjects with the same scripting methods.

Interactive text entry with Input Field

While we often just wish to display non-interactive text messages to the user, there are times (such as name entry for high scores) where we want the user to be able to enter text or numbers into our game. Unity provides the UI Input Field component for this purpose. In this recipe, we’ll create an input field that prompts the user to enter their name:

Figure 2.27: Example of interactive text entry

Having interactive text on the screen isn’t of much use unless we can retrieve the text that’s entered to be used in our game logic, and we may need to know each time the user changes the text’s content and act accordingly. In this recipe, we’ll add an event handler C# script that detects each time the user finishes editing the text and updates an extra message onscreen, confirming the newly entered content.

Getting ready

For this recipe, we have prepared the required assets in a folder named IconsCursors in the 02_09 folder.

How to do it...

To create an interactive text input box for the user, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Change the background of Main Camera to solid white. Do this in the Inspector for the Camera component by setting Clear Flags to Solid Color, and then choosing a white color for the Background property. The complete background of the Game panel should now be white.
  3. Add a UI Input Field - TextMeshPro named InputField to the scene (importing TMP Essential Resources). Position this at the top center of the screen.
  4. Add a UI Text - TextMeshPro GameObject to the scene, naming it Text-prompt. Position this to the left of Input Field. Change the Text property of this GameObject to Name:.
  5. Create a new UI Text - TextMeshPro GameObject named Text-display. Position this to the right of the Input Text control, and make its text red.
  6. Delete all of the content of the Text property of GameObject Text-display (so that, initially, the user won’t see any text onscreen for this GameObject).
  7. Create a new C# script class named DisplayChangedTextContent containing the following code:
    using UnityEngine;
    using TMPro;
    public class DisplayChangedTextContent : MonoBehaviour
    {
        public TMP_InputField inputField;
        private TextMeshProUGUI textDisplay;
        void Awake()
        {
            textDisplay = GetComponent<TextMeshProUGUI>();
        }
        public void DISPLAY_NEW_VALUE()
        {
            textDisplay.text = "last entry = '" + inputField.text + "'";
        }
    }
    
  8. Add an instance of the DisplayChangedTextContent C# script class to the Text-display GameObject.
  9. With Text-display selected in the Hierarchy window, from the Project window, drag the InputField GameObject into the public Input Field variable of the Display Changed Text Content (Script) component:
A screenshot of a computer

Description automatically generated with low confidence

Figure 2.28: Setting the Input Field variable

  1. Select the Input Field GameObject in the Hierarchy. Add an On Value Changed (String) event to the list of event handlers for the Input Field (Script) component. Click on the plus (+) button to add an event handler slot and drag the Text-display GameObject into the Object slot.
  2. Then, from the Function drop-down menu, choose the DisplayChangedTextContent component and then choose the DISPLAY_NEW_VALUE method (we named this in capitals to make it easy to find in the menu!).
A screenshot of a computer

Description automatically generated with medium confidence

Figure 2.29: Making DISPLAY_NEW_VALUE the method to execute each time the Input Text changes

  1. Save and run the scene. Each time the user changes the text in the input field, the On Value Changed event will fire, and you’ll see a new content text message displayed in red on the screen.

How it works...

The core of interactive text input in Unity is the responsibility of the Input Field component. This needs a reference to a UI Text GameObject. To make it easier to see where the text can be typed, Text Input (TMP) (similar to buttons) includes a default rounded rectangle image with a white background.

There are usually three Text GameObjects involved in user text input:

  • The static prompt text, which, in our recipe, displays the text Name:.
  • The faint placeholder text, reminding users where and what they should type.
  • The editable text object (with the font and color settings) that is actually displayed to the user, showing the characters as they type.

First, you created an InputField GameObject, which automatically provides two child Text GameObjects, named Placeholder and Text. These represent the faint placeholder text and the editable text, which you renamed Text-input. You then added a third Text GameObject, Text-prompt, containing Name:.

The built-in scripting that is part of Input Field components does lots of work for us. At runtime, a Text-Input Input Caret (text insertion cursor) GameObject is created, displaying the blinking vertical line to inform the user where their next letter will be typed. When there is no text content, the faint placeholder text will be displayed. As soon as any characters have been typed, the placeholder will be hidden and the characters typed will appear in black text. Then, if all the characters are deleted, the placeholder will appear again.

You then added a fourth Text GameObject called Text-display and made it red to tell the user what they last entered in the input field. You created the DisplayChangedTextContent C# script class and added an instance as a component of the Text-display GameObject. You linked the InputField GameObject to the Input Field public variable of the scripted component (so that the script can access the text content entered by the user).

Finally, you registered an On Value Changed event handler of the Input Field so that each time the user changes the text in the input field, the DISPLAY_NEW_VALUE() method of your DisplayChangedTextContent scripted object is invoked (executed), and the red text content of Text-display is updated to tell the user what the newly edited text consisted of.

I suggest you use capital letters and underscores when naming methods you plan to register for event handlers, such as our input field On Value Changed event. This makes them much easier to find navigating component methods when setting up the event handler in the Inspector.

A screen shot of a computer

Description automatically generated with low confidence

Figure 2.30: Making DISPLAY_NEW_VALUE the method to execute each time the Input Text changes

There’s more...

Rather than the updated text being displayed with every key press, we can wait until Tab or Enter is pressed to submit a new text string by using an On End Edit event handler rather than On Value Changed.

Also, the content type of Input Field (Script) can be set (restricted) to several specific types of text input, including email addresses, integer or decimal numbers only, or password text (where an asterisk is displayed for each character that’s entered). You can learn more about input fields by reading the Unity Manual page: https://docs.unity3d.com/Manual/script-InputField.html.

Detecting interactions with a single Toggle UI component

Users make choices and, often, these choices have one of two options (for example, sound on or off), or sometimes one of several possibilities (for example, difficulty level as easy/medium/hard). Unity UI Toggles allows users to turn options on and off; when combined with toggle groups, they restrict choices to one of the groups of items.

In this recipe, we’ll explore the basic Toggle and a script to respond to a change in values.

A screenshot of a computer

Description automatically generated

Figure 2.31: Example showing the button’s status changing in the Console window

Getting ready

For this recipe, we have prepared the C# script ToggleChangeManager class in the 02_10 folder.

How to do it...

To display an on/off UI Toggle to the user, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. In the Inspector window, change the Background color of Main Camera to white.
  3. Add a UI Toggle to the scene.
  4. For the Label child of the Toggle GameObject, set the Text property to First Class.
  5. Add an instance of the C# script class called ToggleChangeManager to the Toggle GameObject:
    using UnityEngine;
    using UnityEngine.UI;
    public class ToggleChangeManager : MonoBehaviour {
       private Toggle toggle;
       void Awake () {
             toggle = GetComponent<Toggle>();   
       }
       public void PrintNewToggleValue() {
             bool status = toggle.isOn;
             print ("toggle status = " + status);
       }
    }
    
  6. With the Toggle GameObject selected, add an On Value Changed event to the list of event handlers for the Toggle (Script) component, click on the plus (+) button to add an event handler slot, and drag Toggle into the Object slot.

    Note. If this is the first time you have done this, it may seem strange to select a GameObject, and then drag this same GameObject into a property of one of its components. However, this is quite common, since the logic location for behavior like button actions and scripted actions is a component of the GameObject whose behavior is being set. So, it is correct to drag the Toggle GameObject into the Object slot for that toggle’s On Value Changed event handler.

  1. From the Function drop-down menu, choose ToggleChangeManager and then choose the PrintNewToggleValue method.
A screenshot of a computer

Description automatically generated

Figure 2.32: Setting the Toggle’s On Value Changed event handler function

  1. Save and run the scene. Each time you check or uncheck the Toggle GameObject, the On Value Changed event will fire, and you’ll see a new text message printed into the Console window by our script, stating the new Boolean true/false value of Toggle.

How it works...

When you create a Unity UI Toggle GameObject, it comes with several child GameObjects automatically – Background, Checkmark, and the text’s Label. Unless we need to style the look of a Toggle in a special way, all we must do is simply edit the text’s Label so that the user knows what option or feature this Toggle is going to turn on/off.

The Awake() method of the ToggleChangeManager C# class caches a reference to the Toggle component in the GameObject where the script instance is located. When the game is running, each time the user clicks on the Toggle component to change its value, an On Value Changed event is fired. Then, we register the PrintNewToggleValue() method, which is to be executed when such an event occurs. This method retrieves, and then prints out to the Console window, the new Boolean true/false value of Toggle.

Creating related radio buttons using UI Toggles

Unity UI Toggles are also the base components if we wish to implement a group of mutually exclusive options in the style of radio buttons. We need to group related radio buttons together (UI Toggles) to ensure that when one radio button turns on (is selected), all the other radio buttons in the group turn off (are unselected).

We also need to change the visual look if we want to adhere to the usual style of radio buttons as circles, rather than the square UI Toggle default images:

A screenshot of a computer

Description automatically generated

Figure 2.33: Example of three buttons with Console status

Getting ready

For this recipe, we have prepared the images that you’ll need in a folder named UI Demo Textures in the 02_11 folder.

How to do it...

To create related radio buttons using UI Toggles, do the following:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. In the Inspector window, change the Background color of Main Camera to white.
  3. Import the UI Demo Textures folder into the project.
  4. Add a UI Toggle to the scene, naming this new GameObject Toggle-easy.
  5. For the Label child of the Toggle-easy GameObject, set the Text property to Easy.
  6. Select the Canvas GameObject and, in the Inspector window, add a UI | Toggle Group component.
  7. With the Toggle-easy GameObject selected, in the Inspector window, drag the Canvas GameObject into the Toggle Group property of the Toggle (Script) component.
A screenshot of a computer

Description automatically generated

Figure 2.34: Assigning the Canvas Toggle Group to the Toggle-easy GameObject

  1. Assign the Toggle-easy GameObject with a new Tag called Easy. Do this by selecting Add Tag… from the Tag drop-down menu in the Inspector window – this will open the Tags & Layers panel. Click the plus (+) button for a new Tag, and create a new Tag, Easy. Finally, select the Toggle-easy GameObject again in the Hierarchy window, and at the top of the Inspector, set its Tag to Easy.
    A screenshot of a computer

Description automatically generated

    Figure 2.35: Creating new Tag called Easy

    You can also create/edit Tags & Layers from the project Settings panel, accessed from the Edit | Settings… menu.

  1. Select the Background child GameObject of Toggle-easy and, for its Image component, select the UIToggleBG image in the Source Image property (a circle outline).

    To make these toggles look more like radio buttons, the background of each is set to the circle outline image of UIToggleBG, and the checkmark (which displays the toggles that are on) is filled with the circle image called UIToggleButton.

  1. In the Inspector for the Toggle component, ensure the Is On property is checked. Then select the Checkmark child GameObject of Toggle-easy. In the Image component, choose the UIToggleButton image for the Source Image property (a filled circle).

    Of the three choices (easy, medium, and hard) that we’ll offer to the user, we’ll set the easy option to be the one that is supposed to be initially selected. Therefore, we need its Is On property to be checked, which will lead to its checkmark image being displayed.

  1. Add an instance of the RadioButtonManager C# script class to the Canvas GameObject:
    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    public class RadioButtonManager : MonoBehaviour {
      private string currentDifficulty = "Easy";
      public void PrintNewGroupValue(Toggle sender){
        // only take notice from Toggle just switched to On
        if(sender.isOn){
          currentDifficulty = sender.tag;
          print ("option changed to = " + currentDifficulty);
        }
      }
    }
    
  2. With the Toggle-easy GameObject selected, add an On Value Changed event to the list of event handlers for the Toggle (Script) component, click on the plus (+) button to add an event handler slot, and drag the Canvas GameObject into the Object slot.
  3. Then, from the Function drop-down menu, choose RadioButtonManager and then choose the PrintNewGroupValue method.
  4. Into the Toggle parameter slot, which is initially None (Toggle), drag the Toggle-easy GameObject. This means that the Toggle-easy GameObject calls the PrintNewGroupValue(...) method of a C# scripted component called RadioButtonManager in the Canvas GameObject, passing itself as a parameter.
A graph on a computer screen

Description automatically generated

Figure 2.36: Dragging the Toggle-easy GameObject to the Toggle parameter slot

  1. Duplicate the Toggle-easy GameObject, naming the copy Toggle-medium. Set its Rect Transform property’s Pos Y to -25 (so that this copy is positioned below the easy option) and uncheck the Is On property of the Toggle component. Create a new Tag, Medium, and assign the Tag to the new GameObject, Toggle-medium.
  2. Duplicate the Toggle-medium GameObject, naming the copy Toggle-hard. Set its Rect Transform property’s Pos Y to -50 (so that this copy is positioned below the medium option). Create a new Tag, Hard, and assign the Tag to the new GameObject, Toggle-hard.
  3. Save and run the scene. Each time you check one of the three radio buttons, the On Value Changed event will fire, and you’ll see a new text message printed into the Console window by our script, stating the tag of whichever Toggle (radio button) was just set to true (Is On).

How it works...

By using the UIToggleBG and UIToggleButton images, we made the UI GameObject look like radio buttons – circles that when selected have a filled center. By adding a Toggle Group component to Canvas, and having each Toggle GameObject link to it, the three radio buttons can tell Toggle Group when they have been selected. Then, the other members of the group are deselected.

Each Toggle has an On Value Changed event handler that prints out the Tag for the GameObject. So, by creating the Tags Easy, Medium, and Hard, and assigning them to their corresponding GameObjects, we are able to print out a message corresponding to the radio button that has been clicked by the user.

If you had several groups of radio buttons in the same scene, one strategy is, for each group, to add the Toggle Group component to one of the Toggles and have all the others link to that one. So, all Toggles in each group link to the same Toggle Group component.

Note. We store the current radio button value (the last one switched On) in the currentDifficulty property of the RadioButtonManager component of GameObject Canvas. Since variables declared outside a method are remembered, we could, for example, add a public method, such as GetCurrentDifficulty(), which could tell other scripted objects the current value, regardless of how long it’s been since the user last changed their option.

Creating text UI Dropdown menus

In the previous recipe, we created radio-style buttons with a Toggle Group to present the user with a choice of one of many options. Another way to offer a range of choices is with a drop-down menu. Unity provides the UI Dropdown control for such menus. In this recipe, we’ll offer the user a drop-down choice for the suit of a deck of cards (hearts, clubs, diamonds, or spades):

Figure 2.37: Checking the drop-down menu in the Console window

Note that the UI Dropdown that’s created by default includes a scrollable area if there isn’t space for all the options. We’ll learn how to remove such GameObjects and components to reduce complexity when such a feature is not required.

How to do it...

To create a UI Dropdown control GameObject, follow these steps:

  1. Create a new Unity 2D project and install TextMeshPro by choosing: Window | TextMeshPro | Import TMP Essential Resources.
  2. Add a UI Dropdown - TextMeshPro to the scene.
  3. In the Inspector window, for the Dropdown (Script) component, change the list of Options from Option A, Option B, and Option C to Hearts, Clubs, Diamonds, and Spades. You’ll need to click the plus (+) button to add space for the fourth option, that is, Spades.
  4. Add an instance of the C# script class called DropdownManager to the Dropdown GameObject:
    using UnityEngine;
    using TMPro;
    public class DropdownManager : MonoBehaviour  {
        private TMP_Dropdown dropdown;
        private void Awake() {
            dropdown = GetComponent<TMP_Dropdown>();
        }
        public void PrintNewValue() {
            int currentValue = dropdown.value;
            print ("option changed to = " + currentValue);
       }
    }
    
  5. With the Dropdown GameObject selected, add an On Value Changed event to the list of event handlers for the Dropdown (Script) component, click on the plus (+) button to add an event handler slot, and drag Dropdown into the Object slot.
  6. From the Function drop-down menu, choose DropdownManager and then choose the PrintNewValue method.
  7. Save and run the scene. Each time you change Dropdown, the On Value Changed event will fire, and you’ll see a new text message is printed to the Console window by our script, stating the Integer index of the chosen Dropdown value (0 for the first item, 1 for the second item, and so on).
  8. Select the Template child GameObject of Dropdown in the Project window and, in its Rect Transform panel, reduce its height to 50. When you run the scene, you should see a scrollable area, since not all options fit within the template’s height:

Figure 2.38: Example of a drop-down menu

  1. Delete the Scrollbar child of the Template GameObject and remove the Scroll Rect (Script) component of it. When you run the scene now, you’ll only see the first two options (Hearts and Clubs), with no way to access the other two options. When you are sure your template’s height is sufficient for all its options, you can safely remove these scrollable options to simplify the GameObjects in your scene.

How it works...

When you create a Unity UI DropDown-TextMeshPro GameObject, it comes with several components and child GameObjects – Label, Arrow, and Template (as well as ViewPort and Scrollbar, and so on). Dropdowns work by duplicating the Template GameObject for each of the options listed in the Dropdown (Script) component. Both the Text and Sprite image values can be given for each option. The properties of the Template GameObject are used to control the visual style and behavior of the dropdown’s thousands of possible settings.

First, you replaced the default options (Option A, Option B, and so on) in the Dropdown (Script) component. You then created a C# script class called DropdownManager, which, when attached to your dropdown and having its PrintNewValue method registered for On Value Changed events, means you can see the Integer index of the option each time the user changes their choice. Item index values start counting at zero (as is the case in many computing contexts), so 0 for the first item, 1 for the second item, and so on.

Since the default Dropdown GameObject that was created includes a Scroll Rect (Script) component and a Scrollbar child GameObject, when you reduced the height of Template, you could still scroll through the options. You then removed these items so that your dropdown didn’t have a scrolling feature anymore.

Creating image icon UI Dropdown menus

In this recipe, you’ll learn how to create UI Dropdown menus that show image icons next to the text for each menu item. We’ll build on the previous recipe to offer the user a drop-down choice for the suit of a deck of cards (hearts, clubs, diamonds, or spades).

A screenshot of a card game

Description automatically generated

Figure 2.39: Example showing UI Dropdown menus with text and image

There are two pairs of items Unity uses to manage how text and images are displayed for a UI Dropdown control:

  • The Caption Text and Image GameObjects (direct children of the Dropdown GameObject) are used to control how the currently selected item for the dropdown is displayed – this is the part of the dropdown we always see, regardless of whether it is being interacted with.
  • The Item Text and Image GameObjects (children of the Template child of the Dropdown GameObject) define how each option is displayed as a row when the drop-down menu items are being displayed – the rows that are displayed when the user is actively working with the Dropdown GameObject.

So, we have to add an image in two places (for the Caption Image and the Item Image settings), in order to get a dropdown working fully with image icons for each option.

Getting ready

This recipe builds on the previous one. So, create a copy of that project and work on the copy.

For this recipe, we have prepared the image that you need in a folder named Images in the 02_13 folder.

How to do it...

To create image icon dropdown menus, follow these steps:

  1. Open the copy you made of the previous recipe.
  2. Import the provided Images folder.
  3. In the Inspector window, for the Dropdown TextMeshPro component, for each item in the Options list – Hearts, Clubs, Diamonds, and Spades – drag the associated Sprite image from the card_suits folder into the Project window (hearts.png for Hearts, and so on).
  4. Add a UI Image GameObject to the scene and make this Image a child of the Dropdown GameObject.
  5. Drag the hearts.png image from the Project window into the Source Image property of Image for the Image GameObject. Set its size to 25 x 25 in Rect Transform and drag it over the letter H in Hearts in the Label GameObject.
  6. In the Scene panel, drag the Label GameObject so it appears to the right of the Hearts image.
A screenshot of a computer

Description automatically generated

Figure 2.40: Adding a Hearts Image GameObject as a child of the Dropdown GameObject

  1. With Dropdown selected in the Hierarchy, drag the Image GameObject into the Caption Image property of the Dropdown (Script) component.
  2. Enable the Template GameObject (usually, it is disabled). Make it active by checking its Active checkbox at the top of the Inspector.
  3. Duplicate the Image GameObject child of Dropdown and name the copy Item Image. Make this image a child of the Item GameObject that is in Dropdown-Template-Content-Item (Item Image needs to appear below the white Item Background Image; otherwise, it will be covered by the background and not be visible). Also, delete the Item Checkmark GameObject that is in Dropdown-Template-Content-Item.
  4. Since items in the dropdown are slightly smaller, resize Item Image to be 20 x 20 in its Rect Transform.
  5. Position Item Image over the letter O of Option A of Item Text, and then move Item Text to the right so that the icon and text are not on top of each other.
  6. With Dropdown selected in the Project window, drag the Item Image GameObject into the Item Image property of the Dropdown (Script) component:
A screenshot of a computer

Description automatically generated

Figure 2.41: Setting the Caption and Item images for the Dropdown UI menu component

  1. Disable the Template GameObject by unchecking its Active checkbox at the top of the Inspector. Then run the scene to see your Dropdown with icon images for each menu option.

How it works...

You assigned an image sprite for each option in the properties of the Dropdown GameObject. So for each dropdown option you have both its text name (Hearts, Diamonds etc.) and its corresponding sprite. The UI Image you added as a child of the Dropdown GameObject was assigned to the Dropdown’s Caption Image property – which is used by Unity to show the current selections image at the top of the dropdown menu. The UI Image copy you made named Item Image was made a child of the Item GameObject, and that is used to display the sprites for each option when the rows of the dropdown menu are being displayed.

Displaying a radar to indicate the relative locations of objects

A radar displays the locations of other objects relative to the player, usually based on a circular display, where the center represents the player and each graphical blip indicates how far away and what relative direction objects are to the player. Sophisticated radar displays will display different categories of objects with different colored or shaped blip icons:

A green target with red squares and white balls

Description automatically generated

Figure 2.42: Example of a radar

In the preceding screenshot, we can see two red square blips, indicating the relative position of the two red cube GameObjects tagged Cube near the player, and a yellow circle blip indicating the relative position of the yellow sphere GameObject tagged Sphere. The green circle radar background image gives the impression of an aircraft control tower radar or something similar.

Getting ready

For this recipe, we have prepared the radar images and terrain textures that you need in folders named Images and Textures in 02_14.

How to do it...

To create a radar to show the relative positions of the objects, follow these steps:

  1. Create a new Unity 3D project.
  2. Import the provided Images and Textures folders into the project.
  3. Create a terrain by choosing Create | 3D Object | Terrain.
  4. Change the size of Terrain to 20 x 20 by setting the Terrain Width and Terrain Length properties for the Terrain component in the Inspector. Also, set its position to (-10, 0, -10) so that its center is at (0, 0, 0):
    A screenshot of a computer

Description automatically generated with medium confidence

    Figure 2.43: Terrain settings for this recipe

    Note. We change the size of a Terrain through its Terrain Width and Terrain Length properties in its Terrain component. The Scale property of a Terrain’s Transform component does not affect its size.

  1. Let’s give this whole terrain a sandy look. Select the Paint Terrain tool (second from left – mountains and paintbrush icon) for the Terrain component in the Inspector. Then choose the Paint Texture option in the drop-down menu. Create a new Terrain Layer by clicking the Edit Terrain Layers… button. Finally, you must select the SandAlbedo texture from the imported Textures folder – find this easily by typing sand in the search bar. You should now see a new Terrain Layer named NewLayer, and the whole terrain should have been textured with the SandAlbedo texture.
A screenshot of a computer

Description automatically generated with medium confidence

Figure 2.44: Settings for painting the terrain

  1. Create a 3D Cube GameObject at Position (2, 0.5, 2). Create a Cube tag and tag this GameObject with this new tag. Texture this GameObject with the red image called icon32_square_red by dragging the icon32_square_red image from the Project window over this GameObject in the Hierarchy window.
  2. Duplicate the cube GameObject and move it to Position (6, 0.5, 2).
  3. Create a 3D Sphere GameObject at Position (0, 0.5, 4). Create a tag called Sphere and tag this GameObject with this new tag. Texture this GameObject with the yellow image called icon32_square_yellow.
  4. In the Hierarchy window, add a UI RawImage GameObject to the scene named RawImage-radar.
  5. Ensure that the RawImage-radar GameObject is selected in the Hierarchy window. From the Images folder in the Project window, drag the radarBackground image into the Raw Image (Script) public property’s Texture.
  6. In Rect Transform, position RawImage-radar at the top left using the Anchor Presets item. Then, set both Width and Height to 200 pixels.
  7. Create a new Tag named Blip.
  8. Create a new UI RawImage named blip-cube. Assign it the redSquareBlackBorder texture image file from the Project window. Tag this GameObject as Blip.
  9. Create a new UI RawImage named blip-sphere. Assign it the yellowCircleBlackBorder texture image file from the Project window. Tag this GameObject as Blip.
  10. In the Project window, create a folder named Prefabs.
  11. Drag the blip-sphere and blip-cube GameObjects into the Project folder Prefabs. You should now see two new prefab asset files in this folder with the same names as the GameObjects.
  12. Delete the blip-sphere and blip-cube GameObjects from the Hierarchy. We don’t need these in the scene initially, and the prefabs have stored all the properties of these GameObjects, to be instantiated at runtime by our scripts.
  13. Create a C# script class called Radar containing the following code and add an instance as a scripted component to the RawImage-radar GameObject:
    using UnityEngine;
    using UnityEngine.UI;
    public class Radar : MonoBehaviour {
       public float insideRadarDistance = 20;
       public float blipSizePercentage = 5;
       public GameObject rawImageBlipCube;
       public GameObject rawImageBlipSphere;
       private RawImage rawImageRadarBackground;
       private Transform playerTransform;
       private float radarWidth;
       private float radarHeight;
       private float blipHeight;
       private float blipWidth;
       void Start() {
             rawImageRadarBackground = GetComponent<RawImage>();
             playerTransform =
                 GameObject.FindGameObjectWithTag("Player").transform;
             radarWidth = rawImageRadarBackground.rectTransform.rect.width;
             radarHeight = rawImageRadarBackground.rectTransform.rect.height;
             blipHeight = radarHeight * blipSizePercentage / 100;
             blipWidth = radarWidth * blipSizePercentage / 100;
       }
       void Update() {
             RemoveAllBlips();
             FindAndDisplayBlipsForTag("Cube", rawImageBlipCube);
             FindAndDisplayBlipsForTag("Sphere", rawImageBlipSphere);
       }
       private void FindAndDisplayBlipsForTag(string tag, GameObject prefabBlip) {
             Vector3 playerPos = playerTransform.position;
             GameObject[] targets = GameObject.FindGameObjectsWithTag(tag);
             foreach (GameObject target in targets) {
                   Vector3 targetPos = target.transform.position;
                   float distanceToTarget = Vector3.Distance(targetPos,
                       playerPos);
                   if ((distanceToTarget <= insideRadarDistance))
                    CalculateBlipPositionAndDrawBlip (playerPos, targetPos,
                        prefabBlip);
             }
       }
        private void CalculateBlipPositionAndDrawBlip (Vector3 playerPos, Vector3
            targetPos, GameObject prefabBlip) {
             Vector3 normalisedTargetPosition = NormalizedPosition(playerPos,
                 targetPos);
             Vector2 blipPosition =
                 CalculateBlipPosition(normalisedTargetPosition);
             DrawBlip(blipPosition, prefabBlip);
       }
       private void RemoveAllBlips() {
             GameObject[] blips = GameObject.FindGameObjectsWithTag("Blip");
             foreach (GameObject blip in blips)
                   Destroy(blip);
       }
       private Vector3 NormalizedPosition(Vector3 playerPos, Vector3 targetPos) {
             float normalisedyTargetX = (targetPos.x - playerPos.x) /
                 insideRadarDistance;
             float normalisedyTargetZ = (targetPos.z - playerPos.z) /
                 insideRadarDistance;
             return new Vector3(normalisedyTargetX, 0, normalisedyTargetZ);
       }
       private Vector2 CalculateBlipPosition(Vector3 targetPos) {
             float angleToTarget = Mathf.Atan2(targetPos.x, targetPos.z) *
                 Mathf.Rad2Deg;
             float anglePlayer = playerTransform.eulerAngles.y;
             float angleRadarDegrees = angleToTarget - anglePlayer - 90;
             float normalizedDistanceToTarget = targetPos.magnitude;
             float angleRadians = angleRadarDegrees * Mathf.Deg2Rad;
             float blipX = normalizedDistanceToTarget * Mathf.Cos(angleRadians);
             float blipY = normalizedDistanceToTarget * Mathf.Sin(angleRadians);
             blipX *= radarWidth / 2;
             blipY *= radarHeight / 2;
             blipX += radarWidth / 2;
             blipY += radarHeight / 2;
             return new Vector2(blipX, blipY);
       }
       private void DrawBlip(Vector2 pos, GameObject blipPrefab) {
             GameObject blipGO = (GameObject)Instantiate(blipPrefab);
             blipGO.transform.SetParent(transform.parent);
             RectTransform rt = blipGO.GetComponent<RectTransform>();
             rt.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, pos.x,
                 blipWidth);
             rt.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, pos.y,
                 blipHeight);
       }
    }
    
  14. Ensure that the RawImage-radar GameObject is selected in the Hierarchy. We now need to populate public variables Raw Image Blip Cube and Raw Image Blip Sphere the for the Radar scripted component in the Inspector.
  15. Drag the blip-sphere from the Prefab folder in the Project panel into the Raw Image Blip Cube public variable in the Inspector. Then drag the blip-sphere Prefab asset file from the Prefab folder into the Raw Image Blip Sphere public variable. By doing this, you are setting these public script variables to reference these prefabs, allowing the Radar scripted component to control the display of GameObjects created from these Prefabs at runtime.
A screenshot of a computer

Description automatically generated

Figure 2.45: Calculation for the blip method

  1. In the Inspector, set the Main Camera Transform properties to have Position (0, 5, -10), and Rotation (10, 0, 0). This will allow you to see the cubes and sphere GameObjects easily when playing the game.
  2. Save and Run your game. We just see an empty radar image at the top left of the screen! The Radar script will draw blips on the radar relative to the position of the GameObject tagged Player.
  3. Stop the game and create a new 3D capsule named Capsule-player, positioned at (0, 1, 0), and tag this Player.
  4. Save and Run your game. Now we can see a yellow circle blip, and two red square blips on the radar, showing the relative position of the yellow sphere and two red cubes to the capsule tagged Player!

How it works...

A radar background is displayed on the screen. The center of this circular image represents the position of the player’s character. In this recipe, you created two prefabs – one for red square images to represent each red cube found within the radar distance, and one for yellow circles to represent yellow sphere GameObjects.

The Radar C# script class has been added to the radar UI Image GameObject. This class defines four public variables:

  • insideRadarDistance: This value defines the maximum distance in the scene that an object may be from the player so that it can still be included on the radar (objects further than this distance will not be displayed on the radar).
  • blipSizePercentage: This public variable allows the developer to decide how large each blip will be, as a proportion of the radar’s image.
  • rawImageBlipCube and rawImageBlipSphere: These are references to the prefab UI RawImages that are to be used to visually indicate the relative distance and position of cubes and spheres on the radar.

Since there is a lot happening in the code for this recipe, each method will be described in its own section.

The Start() method

The Start() method caches a reference to the RawImage of the radar background image. Then, it caches a reference to the Transform component of the player’s character (tagged as Player). This allows the scripted object to know about the position of the player’s character in each frame. Next, the width and height of the radar image are cached, so that the relative positions for blips can be calculated based on the size of this background radar image. Finally, the size of each blip (blipWidth and blipHeight) is calculated using the blipSizePercentage public variable.

The Update() method

The Update() method calls the RemoveAllBlips() method, which removes any old UI RawImage GameObjects of cubes and spheres that might currently be displayed. If we didn’t remove old blips before creating new ones, then you’d see “tails” behind each blip as new ones are created in different positions – which could actually be an interesting effect.

Next, the FindAndDisplayBlipsForTag(...) method is called twice. First, for the objects tagged Cube to be represented on the radar with the rawImageBlipCube prefab, and then again for objects tagged Sphere to be represented on the radar with the rawImageBlipSphere prefab. As you might expect, most of the hard work of the radar is to be performed by the FindAndDisplayBlipsForTag(...) method.

This code is a simple approach to creating a radar. It is very inefficient to make repeated calls to FindGameObjectWithTag("Blip") for every frame from the Update() method. In a real game, it would be much better to cache all created blips in something such as a List or ArrayList, and then simply loop through that list each time.

The FindAndDisplayBlipsForTag(...) method

This method inputs two parameters: the string tag for the objects to be searched for, and a reference to the RawImage prefab to be displayed on the radar for any such tagged objects within the range.

First, the current position of the player’s character is retrieved from the cached player Transform variable. Next, an array is constructed, referring to all GameObjects in the scene that have the provided tag. This array of GameObjects is looped through, and for each GameObject, the following actions are performed:

  1. The position of the target GameObject is retrieved.
  2. The distance from this target’s position to the player’s position is calculated.
  3. If this distance is within the range (less than or equal to insideRadarDistance), then the CalculateBlipPositionAndDrawBlip(...) method is called.

The CalculateBlipPositionAndDrawBlip (...) method

This method inputs three parameters: the position of the player, the position of the target, and a reference to the prefab of the blip to be drawn.

Three steps are now required to get the blip for this object to appear on the radar:

  1. The normalized position of the target is calculated by calling NormalizedPosition(...).
  2. The position of the blip on the radar is calculated from this normalized position by calling CalculateBlipPosition(...).
  3. The RawImage blip is displayed by calling DrawBlip(...) and passing the blip’s position and the reference to the RawImage prefab that is to be created there.

The NormalizedPosition(...) method

The NormalizedPosition(...) method inputs the player’s character position and the target GameObject's position. It has the goal of outputting the relative position of the target to the player, returning a Vector3 object (actually, a C# struct – but we can think of it as a simple object) with a triplet of X, Y, and Z values. Note that since the radar is only 2D, we ignore the Y-value of the target GameObjects, so the Y-value of the Vector3 object that’s returned by this method will always be 0. So, for example, if a target was at exactly the same location as the player, the X, Y, and Z of the returned Vector3 object would be (0, 0, 0).

Since we know that the target GameObject is no further from the player’s character than insideRadarDistance, we can calculate a value in the -1 ... 0 ... +1 range for the X and Z axes by finding the distance on each axis from the target to the player and then dividing it by insideRadarDistance. An X-value of -1 means that the target is fully to the left of the player (at a distance that is equal to insideRadarDistance), while +1 means it is fully to the right. A value of 0 means that the target has the same X position as the player’s character. Likewise, for -1 ... 0 ... +1 values in the Z-axis (this axis represents how far, in front or behind us, an object is located, which will be mapped to the vertical axis in our radar).

Finally, this method constructs and returns a new Vector3 object with the calculated X and Z normalized values and a Y-value of zero.

The normalized position

The normalized value is one that has been simplified in some way so that its context has been abstracted away. In this recipe, what we are interested in is where an object is relative to the player. So, our normal form is to get a value of the X and Z position of a target in the -1 to +1 range for each axis. Since we are only considering the GameObjects within our insideRadarDistance value, we can map these normalized target positions directly onto the location of the radar image in our UI.

The CalculateBlipPosition(...) method

First, we calculate angleToTarget, which is the angle from (0, 0, 0) to our normalized target position.

Next, we calculate anglePlayer, which is the angle the player’s character is facing. This recipe makes use of the yaw angle of the rotation, which is the rotation about the Y-axis – that is, the direction that a character controller is facing. This can be found in the Y component of a GameObject’s eulerAngles component of its transform. You can imagine looking from above and down at the character controller and seeing what direction they are facing – this is what we are trying to display graphically with the radar.

Our desired radar angle (the angleRadarDegrees variable) is calculated by subtracting the player’s direction angle from the angle between the target and the player, since a radar displays the relative angle from the direction that the player is facing to the target object. In mathematics, an angle of zero indicates a direction of east. To correct this, we need to also subtract 90 degrees from the angle.

The angle is then converted into radians since this is required for these Unity trigonometry methods. We then multiply the Sin() and Cos() results by our normalized distances to calculate the X and Y values, respectively (see the following diagram):

A picture containing circle, diagram, line

Description automatically generated

Figure 2.46: Calculation for the blip method

In the preceding diagram, alpha is the angle between the player and the target object, “a” is the adjacent side, “h” is the hypotenuse, and “o” is the side opposite the angle.

Our final position values need to be expressed as pixel lengths, relative to the center of the radar. So, we multiply our blipX and blipY values by half the width and the height of the radar; note that we only multiply with half the width since these values are relative to the center of the radar. We then add half the width and the height of the radar image to the blipX/Y values so that these values are now positioned relative to the center.

Finally, a new Vector2 object is created and returned, passing back these final calculated X and Y pixel values for the position of our blip icon.

The DrawBlip() method

The DrawBlip() method takes the input parameters of the position of the blip (as a Vector2 X, Y pair) and the reference to the RawImage prefab to be created at that location on the radar.

A new GameObject is created (instantiated) from the prefab and is parented to the radar GameObject (of which the scripted object is also a component). A reference is retrieved from the Rect Transform component of the new RawImage GameObject that has been created for the blip. Calls to the Unity RectTransform method, SetInsetAndSizeFromParentEdge(...), result in the blip GameObject being positioned at the provided horizontal and vertical locations over the radar image, regardless of where in the Game window the background radar image has been located.

There’s more...

This radar script scans 360 degrees all around the player and only considers straight-line distances on the X-Z plane. So, the distances in this radar are not affected by any height difference between the player and target GameObjects. The script can be adapted to ignore targets whose height is more than some threshold different from the player’s height.

Also, as presented, this recipe’s radar sees through everything, even if there are obstacles between the player and the target. This recipe can be extended to not show obscured targets by using raycasting techniques.

And, of course, you can replace the 3D capsule with a user-controlled animated character, such as those covered in Chapter 9, Animated Characters.

Further reading

The following are some useful resources for learning more about working with core UI elements in Unity:

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Explore VR and AR development to create immersive experiences that redefine gaming
  • Craft captivating mobile games with optimized performance and user-friendly controls
  • Elevate gameplay with expertly composed music, dynamic sound effects, and seamless audio integration

Description

Unleash your game development potential with Unity Cookbook, 5th Edition, designed to equip you with the skills and knowledge needed to excel in Unity game development. With over 160 expertly crafted recipes empowering you to pioneer VR and AR experiences, excel in mobile game development, and become a master of audio techniques. In this latest edition, we've meticulously curated a collection of recipes that reflect the latest advancements in Unity 2023, ensuring you stay at the forefront of game development. You'll discover dedicated recipes for First/Third Person (Core) templates, create engaging mobile games, delve into Virtual and Augmented Reality, and go further with audio by exploring advanced techniques. Additionally, the book has been fully updated to incorporate the new input system and TextMeshPro, essential elements for modern game development. From exploring C# scripting to crafting stylish UIs, creating stunning visual effects, and understanding shader development through Shader Graph, every chapter is designed to take you closer to your goal of becoming a proficient Unity developer. So, whether you're aiming to develop the next hit game, enhance your portfolio, or simply have fun building games, this book will be your trusted companion on your journey to Unity proficiency.

What you will learn

Craft stylish user interfaces, from power bars to radars, and implement button-driven scene changes effortlessly Enhance your games with AI controlled characters, harnessing Unity's navigation meshes, surfaces, and agents Discover the power of Cinemachine in Unity for intelligent camera movements Elevate games with immersive audio, including background music and dynamic sound effects Bring your games to life with captivating visual effects, from smoke and explosions to customizable particle systems Build your own shaders using Unity's Shader Graph tool

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Nov 30, 2023
Length 780 pages
Edition : 5th Edition
Language : English
ISBN-13 : 9781805123026
Vendor :
Unity Technologies
Category :
Concepts :

Table of Contents

22 Chapters
Preface Chevron down icon Chevron up icon
Displaying Data with Core UI Elements Chevron down icon Chevron up icon
Responding to User Events for Interactive UIs Chevron down icon Chevron up icon
Inventory and Advanced UIs Chevron down icon Chevron up icon
Playing and Manipulating Sounds Chevron down icon Chevron up icon
Textures, Materials, and 3D Objects Chevron down icon Chevron up icon
Creating 3D Environments with Terrains Chevron down icon Chevron up icon
Creating 3D Geometry with ProBuilder Chevron down icon Chevron up icon
2D Animation and Physics Chevron down icon Chevron up icon
Animated Characters Chevron down icon Chevron up icon
Saving and Loading Data Chevron down icon Chevron up icon
Controlling and Choosing Positions Chevron down icon Chevron up icon
Navigation Meshes and Agents Chevron down icon Chevron up icon
Cameras, Lighting, and Visual Effects Chevron down icon Chevron up icon
Shader Graphs and Video Players Chevron down icon Chevron up icon
Particle Systems and Other Visual Effects Chevron down icon Chevron up icon
Mobile Games and Applications Chevron down icon Chevron up icon
Augmented Reality (AR) Chevron down icon Chevron up icon
Virtual and Extended Reality (VR/XR) Chevron down icon Chevron up icon
Advanced Topics – Gizmos, Automated Testing, and More Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
(1 Ratings)
5 star 0%
4 star 0%
3 star 100%
2 star 0%
1 star 0%

Filter reviews by


N/A Jan 25, 2024
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
Only just started it so ask again later
Feefo Verified review Feefo image
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.