In this article by Charles Pearson, the author of Learning NGUI for Unity, we will talk about C# scripting with NGUI. We will learn how to handle events and interact with them through code. We'll use them to:
We'll learn many more useful C# tips throughout the book. Right now, let's start with events and their associated methods.
(For more resources related to this topic, see here.)
When scripting in C# with the NGUI plugin, some methods will often be used. For example, you will regularly need to know if an object is currently hovered upon, pressed, or clicked. Of course, you could code your own system—but NGUI handles that very well, and it's important to use it at its full potential in order to gain development time.
When you create and attach a script to an object that has a collider on it (for example, a button or a 3D object), you can add the following useful methods within the script to catch events:
In order to handle double clicks, you can also use the OnDoubleClick() method, which works in the same way.
If you attach your script on a 3D object to catch these events, make sure it is on a layer included in Event Mask of UICamera.
Now that we've seen the available event methods, let's see how they are used in a simple example.
To illustrate when these events occur and how to catch them, you can create a new EventTester.cs script with the following code:
void OnHover(bool state) { Debug.Log(this.name + " Hover: " + state); } void OnPress(bool state) { Debug.Log(this.name + " Pressed: " + state); } void OnClick() { Debug.Log(this.name + " Clicked"); } void OnDrag(Vector2 delta) { Debug.Log(this.name + " Drag: " + delta); } void OnDrop(GameObject droppedObject) { Debug.Log(droppedObject.name + " dropped on " + this.name); } void OnSelect(bool state) { Debug.Log(this.name + " Selected: " + state); } void OnTooltip(bool state) { Debug.Log("Show " + this.name + "'s Tooltip: " + state); } void OnScroll(float delta) { Debug.Log("Scroll of " + delta + " on " + this.name); }
The above highlighted lines are the event methods we discussed, implemented with their respective necessary arguments.
Attach our Event Tester component now to any GameObject with a collider, like our Main | Buttons | Play button. Hit Unity's play button. From now on, events that occur on the object they're attached to are now tracked in the Console output:
I recommend that you keep the EventTester.cs script in a handy file directory as a reminder for available event methods in the future. Indeed, for each event, you can simply replace the Debug.Log() lines with the instructions you need.
Now we know how to catch events through code. Let's use them to display a tooltip!
Let's use the OnTooltip() event to show a tooltip for our buttons and different options, as shown in the following screenshot:
The tooltip object shown in the preceding screenshot, which we are going to create, is composed of four elements:
We will also make sure the tooltip is localized using NGUI methods.
In order to create the tooltip object, we'll first create its visual elements (widgets), and then we'll attach the Tooltip component to it in order to define it as NGUI's tooltip.
First, we need to create the tooltip object's visual elements:
In the preceding steps, we've created the tooltip container. It has UIPanel with a Depth value of 10 in order to make sure our tooltip will remain on top of other panels.
Now, let's create the faintly transparent background sprite:
With Tooltip selected, hit Alt + Shift + S to create a new child sprite.
Select our new Tooltip | Background GameObject, and configure UISprite, as follows:
Perform the following steps:
Make sure Atlas is set to Wooden Atlas.
Ok, we can now easily add a fully opaque border with the following trick:
With Tooltip | Background selected, hit Ctrl + D to duplicate it.
Select Tooltip | Border and configure its attached UI Sprite, as follows:
Perform the following steps:
Disable the Fill Center option.
By not filling the center of the Border sprite, we now have a yellow border around our background. We used anchors to make sure this border always wraps the background even during runtime—thanks to the Execute parameter set to OnUpdate.
Right now, our Game and Hierarchy views should look like this:
Let's create the tooltip's label. With Tooltip selected, hit Alt + Shift + L to create a new label. For the new Label GameObject, set the following parameters for UILabel:
Set Font Type to NGUI, and Font to Arimo20 with a size of 40.
Ok, good. We now have a label that can display our tooltip's text. This label's height will adjust automatically as the text gets longer or shorter.
Let's configure anchors to make sure the background always wraps around the label:
Select our Tooltip | Background GameObject.
Great! Now, if you edit our tooltip's text label to a very large text, you'll see that it adjusts automatically, as shown in the following screenshot:
We can now add the UITooltip component to our tooltip object:
Select our UI Root | Tooltip GameObject.
Configure the newly attached UITooltip component, as follows:
Drag UI Root | Tooltip | Label in the Text field.
The tooltip object is ready! It is now defined as a tooltip for NGUI. Now, let's see how we can display it when needed using a few simple lines of code.
We must now show the tooltip when needed. In order to do that, we can use the OnTooltip() event, in which we request to display the tooltip with localized text:
Select our three Main | Buttons | Exit, Options, and Play buttons.
First, we need to add this public key variable to define which text we want to display:
// The localization key of the text to display public string key = "";
Ok, now add the following OnTooltip() method that retrieves the localized text and requests to show or hide the tooltip depending on the state
bool: // When the OnTooltip event is triggered on this object void OnTooltip(bool state) { // Get the final localized text string finalText = Localization.Get(key); // If the tooltip must be removed... if(!state) { // ...Set the finalText to nothing finalText = ""; } // Request the tooltip display UITooltip.ShowText(finalText); }
Save the script. As you can see in the preceding code, the Localization.Get(string key) method returns localized text of the corresponding key parameter that is passed. You can now use it to localize a label through code anytime! In order to hide the tooltip, we simply request UITooltip to show an empty tooltip.
To use Localization.Get(string key), your label must not have a UILocalize component attached to it; otherwise, the value of UILocalize will overwrite anything you assign to UILabel.
Ok, we have added the code to show our tooltip with localized text. Now, open the Localization.txt file, and add these localized strings:
// Tooltips Play_Tooltip, "Launch the game!", "Lancer le jeu !" Options_Tooltip, "Change language, nickname, subtitles...", "Changer la langue, le pseudo, les sous-titres..." Exit_Tooltip, "Leaving us already?", "Vous nous quittez déjà ?"
Now that our localized strings are added, we could manually configure the key parameter for our three buttons' Show Tooltip components to respectively display Play_Tooltip, Options_Tooltip, and Exit_Tooltip.
But that would be a repetitive action, and if we want to add localized tooltips easily for future and existing objects, we should implement the following system: if the key parameter is empty, we'll try to get a localized text based on the GameObject's name.
Let's do this now! Open our ShowTooltip.cs script, and add this Start() method:
// At start void Start() { // If key parameter isn't defined in inspector... if(string.IsNullOrEmpty(key)) { // ...Set it now based on the GameObject's name key = name + "_Tooltip"; } }
Click on Unity's play button. That's it! When you leave your cursor on any of our three buttons, a localized tooltip appears:
The preceding tooltip wraps around the displayed text perfectly, and we didn't have to manually configure their Show Tooltip components' key parameters!
Actually, I have a feeling that the display delay is too long. Let's correct this:
Select our UI Root | Camera GameObject.
That's better—our localized tooltip appears after 0.3 seconds of hovering.
We can now easily add tooltips for our Options page's element. The tooltip works on any GameObject with a collider attached to it. Let's use a search by type to find them:
In the Hierarchy view's search bar, type t:boxcollider
For the objects with generic names, such as Input and List, we need to set their key parameter manually, as follows:
Select the Checkbox GameObject, and set Key to Sound_Tooltip.
To know if the selected list is the language or subtitles list, look at Options of its UIPopup List: if it has the None option, then it's the subtitles selection.
Finally, we need to add these localization strings in the Localization.txt file:
Sound_Tooltip, "Enable or disable game sound", "Activer ou désactiver le son du jeu" Nickname_Tooltip, "Name used during the game", "Pseudo utilisé lors du jeu" Language_Tooltip, "Game and user interface language", "Langue du jeu et de l'interface" Subtitles_Tooltip, "Subtitles language", "Langue des sous-titres" Confirm_Tooltip, "Confirm and return to main menu", "Confirmer et retourner au menu principal" Music_Tooltip, "Game music volume", "Volume de la musique" SFX_Tooltip, "Sound effects volume", "Volume des effets"
Hit Unity's play button. We now have localized tooltips for all our options! We now know how to easily use NGUI's tooltip system. It's time to talk about Tween methods.
The tweens we have used until now were components we added to GameObjects in the scene. It is also possible to easily add tweens to GameObjects through code.
You can see all available tweens by simply typing Tween inside any method in your favorite IDE. You will see a list of Tween classes thanks to auto-completion, as shown in the following screenshot:
The strong point of these classes is that they work in one line and don't have to be executed at each frame; you just have to call their Begin() method!
Here, we will apply tweens on widgets, but keep in mind that it works in the exact same way with other GameObjects since NGUI widgets are GameObjects.
Previously, we've used the Tween Scale component to make our main window disappear when the Exit button is pressed. Let's do the same when the Play button is pressed, but this time we'll do it through code to understand how it's done.
We will first create a new DisappearOnClick.cs script that will tween a target's scale to {0.01, 0.01, 0.01} when the GameObject it's attached to is clicked on:
Select our UI Root | Main | Buttons | Play GameObject.
First, we must add this public target GameObject to define which object will be affected by the tween, and a duration float to define the speed:
// Declare the target we'll tween down to {0.01, 0.01, 0.01} public GameObject target; // Declare a float to configure the tween's duration public float duration = 0.3f;
Ok, now, let's add the following OnClick() method, which creates a new tween towards {0.01, 0.01, 0.01} on our desired target using the duration variable:
// When this object is clicked private void OnClick() { // Create a tween on the target TweenScale.Begin(target, duration, Vector3.one * 0.01f); }
In the preceding code, we scale down the target for the desired duration, towards 0.01f.
Save the script. Good. Now, we simply have to assign our variables in the Inspector view:
Great. Now, hit Unity's play button. When you click the menu's Play button, our main menu is scaled down to {0.01, 0.01, 0.01}, with the simple TweenScale.Begin() line!
Now that we've seen how to make a basic tween, let's see how to add effects.
Right now, our tween is simple and linear. In order to add an effect to the tween, we first need to store it as UITweener, which is its parent class.
Replace lines of our OnClick() method by these to first store it and set an effect:
// Retrieve the new target's tween UITweener tween = TweenScale.Begin(target, duration, Vector3.one * 0.01f); // Set the new tween's effect method tween.method = UITweener.Method.EaseInOut;
That's it. Our tween now has an EaseInOut effect. You also have the following tween effect methods:
Perform the following steps:
Great. We now know how to add tween effects through code. Now, let's see how we can set event delegates through code.
You can set the tween's ignoreTimeScale to true if you want it to always run at normal speed even if your Time.timeScale variable is different from 1.
Many NGUI components broadcast events, for which you can set an event delegate—also known as a callback method—executed when the event is triggered. We did it through the Inspector view by assigning the Notify and Method fields when buttons were clicked.
For any type of tween, you can set a specific event delegate for when the tween is finished. We'll see how to do this through code. Before we continue, we must create our callback first. Let's create a callback that loads a new scene.
Open our MenuManager.cs script, and add this static LoadGameScene() callback method:
public static void LoadGameScene() { //Load the Game scene now Application.LoadLevel("Game"); }
Save the script. The preceding code requests to load the Game scene. To ensure Unity finds our scenes at runtime, we'll need to create the Game scene and add both Menu and Game scenes to the build settings:
Navigate to File | Build Settings.
Ok, now that both scenes have been added to the build settings, we are ready to link our callback to our event.
Now that our LoadGameScene() callback method is written, we must link it to our event. We have two solutions. First, we'll see how to assign it using code exclusively, and then we'll create a more flexible system using NGUI's Notify and Method fields.
In order to set a callback for a specific event, a generic solution exists for all NGUI events you might encounter: the EventDelegate.Set() method. You can also add multiple callbacks to an event using EventDelegate.Add().
Add this line at the end of the OnClick() method of DisappearOnClick.cs:
// Set the tween's onFinished event to our LoadGameScene callback
EventDelegate.Set(tween.onFinished, MenuManager.LoadGameScene);
Instead of the preceding line, we can also use the tween-specific SetOnFinished() convenience method to do this. We'll get the exact same result with fewer words:
// Another way to assign our method to the onFinished event
tween.SetOnFinished(MenuManager.LoadGameScene);
Great. If you hit Unity's play button and click on our main menu's Play button, you'll see that our Game scene is loaded as soon as the tween has finished!
It is possible to remove the link of an existing event delegate to a callback by calling EventDelegate.Remove(eventDelegate, callback);.
Now, let's see how to link an event delegate to a callback using the Inspector view.
Now that we have seen how to set event delegates through code, let's see how we can create a variable to let us choose which method to call within the Inspector view, like this:
The method to call when the target disappears can be set any time without editing the code
The On Disappear variable shown in the preceding screenshot is of the type EventDelegate. We can declare it right now with the following line as a global variable for our DisappearOnClick.cs script:
// Declare an event delegate variable to be set in Inspector public EventDelegate onDisappear;
Now, let's change the OnClick() method's last line to make sure the tween's onFinished event calls the defined onDisappear callback:
// Set the tween's onFinished event to the selected callback tween.SetOnFinished(onDisappear);
Ok. Great. Save the script and go to Unity. Select our main menu's Play button: a new On Disappear field has appeared.
Drag UI Root—which holds our MenuManager.cs script—in the Notify field. Now, try to select our MenuManager | LoadGameScene method. Surprisingly, it doesn't appear, and you can only select the script's Exit method… why is that?
That is simply because our LoadGameScene() method is currently static. If we want it to be available in the Inspector view, we need to remove its static property:
Open our MenuManager.cs script.
Save the script and return to Unity. You can now select it in the drop-down list:
Great! We have set our callback through the Inspector view; the Game scene will be loaded when the menu disappears.
Now that we have learned how to assign event delegates to callback methods through code and the Inspector view, let's see how to assign keyboard keys to user interface elements.
In this section, we'll see how to add keyboard control to our UI. First, we'll see how to bind keys to buttons, and then we'll add a navigation system using keyboard arrows.
The UIKey Binding component assigns a specific key to the widget it's attached to. We'll use it now to assign the keyboard's Escape key to our menu's Exit button:
Select our UI Root | Main | Buttons | Exit GameObject.
Let's see its available parameters.
We've just added the following UIKey Binding component to our Exit button, as follows:
The newly attached UIKey Binding component has three parameters:
Ok, now, we'll configure it to see how it works.
Simply set the Key Code field to Escape. Now, hit Unity's play button. When you hit the Escape key of our keyboard, it reacts as if the Exit button was pressed!
We can now move on to see how to add keyboard and controller navigation to the UI.
The UIKey Navigation component helps us assign objects to select using the keyboard arrows or controller directional-pad. For most widgets, the automatic configuration is enough, but we'll need to use the override parameters in some cases to have the behavior we need.
The nickname input field has neither the UIButton nor the UIButton Scale components attached to it. This means that there will be no feedback to show the user it's currently selected with the keyboard navigation, which is a problem. We can correct this right now.
Select UI Root | Options | Nickname | Input, and then:
Add the Button component (UIButton) to it.
The Nickname | Input GameObject should have an Inspector view like this:
Ok. We'll now add the Key Navigation component (UIKey Navigation) to most of the buttons in the scene. In order to do that, type t:uibutton in the Hierarchy view's search bar to display only GameObjects with the UIButton component attached to them:
Ok. With the preceding search filter, select the following GameObjects:
Now, with the preceding selection, follow these steps:
Click the Add Component button in the Inspector view.
We've added the UIKey Navigation component to our selection. Let's see its parameters.
We've just added the following UIKey Navigation component to our objects:
The newly attached UIKey Navigation component has four parameter groups:
Starts Selected: Is this widget selected by default at the start?
This article thus has given an introduction to how C# is used in Unity.
Further resources on this subject: