The Utility of Delegates and Events in Unity

timrodz

Juan Alejandro Morais

Posted on September 25, 2019

The Utility of Delegates and Events in Unity

Originally published at timrodz.com.

Update: @damagefilter wrote a more advanced tutorial regarding events in Unity. Go check it out here 😄.

¡Hola! My name is Juan Rodriguez. I am a Panamanian data & game developer and programmer based in New Zealand. This blog post was written for notGDC as I wanted to contribute some of my knowledge with the community. It is aimed towards programmers looking to get the most out of Unity, no matter the skill level. The initial stages will be fairly basic as I want everyone to understand this tutorial, so feel free to skip parts as you wish! (Or just grab the source code). The tools I used are Unity 2017.3.1p2 and Visual Studio Code.

📩 What

In games, we often encounter situations where we want to notify objects about events that are happening without having to tie them together. Let’s consider the following situation. You have a UI system with:

  • An input field to write a message.
  • A button to send the message.
  • A field to display the message sent.

When the user clicks the ‘Send Message’ button, you want to inform the UI controller that a new message has been sent. After this, you will replace the current message with the new one. Lastly, the input field will be cleared up.

Here’s a GIF of how the game will look like:

Simple, but works.

📤 How

In order to achieve the desired functionality, we will approach the solution by implementing delegates. Delegates allow us to treat methods as variables, and they work like a mailing list. Subscribers will receive emails and can opt-out at any time. Note: You can subscribe as many methods as you desire. Events are very similar. The main difference is they add a layer of abstraction and protection on the delegate instance. This protection prevents clients of the delegate from resetting the delegate and its invocation list and only allows adding or removing targets from the invocation list (Source).

On the programming side, we are going to have two scripts: an event controller and a UI controller. The event controller will work as our mailing list, and it will have one event: OnMessageUpdateRequest. Since we want to replace our current message for the input field’s one, so this method will send out new message update requests.

The UI controller will have an UpdateMessage method subscribed to the OnMessageUpdateRequest event, and it will work as a listener. Also, to introduce you to UnityEvents we will play around with our Button and its capabilities, but we’ll get to that later. In essence: Delegates, Events and UnityEvents are very similar.

📨 Implementation

Inside Unity, create a new Scene and add the following objects to it:

  • Two Empty GameObjects. Rename them ‘UI Controller’ and ‘Event Controller’ respectively.
  • Right click on the hierarchy view:
    • Choose UI → Button.
    • Choose UI → Input Field.
    • Choose UI → Text.

Note: New UI objects will automatically add a Canvas and an EventSystem if your scene does not already have them inside. Why? because the Canvas displays UI elements and the EventSystem handles interactions from inputs that are not keys — They’re necessary to make any UI system work.

Your scene should look similar to this one:

A very simple scene.

Applying our solution

Once you have finished setting up your scene, it’s time to create the following C# scripts:

  • UIController.cs
  • EventController.cs

These scripts have the same names as our previously created Empty GameObjects. They will be attached to them as components.

UIController.cs

Once you create the class, it should look similar to this snippet:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIController : MonoBehaviour
{
    // Use this for initialization
    void Start() {}

    // Update is called once per frame
    void Update() {}
}
Enter fullscreen mode Exit fullscreen mode

Since we’re working with UI, we need to include the UnityEngine.UI library in order to access functionality of UI elements. Between lines 3 and 5, add the following code snippet:

using UnityEngine.UI;
Enter fullscreen mode Exit fullscreen mode

After this is done, remove the Update method— We won’t be needing it. Now that we’ve got our UI library imported, we will proceed to create references to our UI objects in the scene. Inside the UIController class, add the following declarations before the Start method:

public Button ButtonSendMessage;
public InputField InputField;
public Text MessageDisplay;
Enter fullscreen mode Exit fullscreen mode

Now, we need to create two methods: SendMessageUpdateRequest and UpdateMessage. Inside UIController.cs, add the following lines of code:

void SendMessageUpdateRequest() {}

void UpdateMessage(string _message) {}
Enter fullscreen mode Exit fullscreen mode

If you’ve followed the steps correctly, your class should now look like this: Gist url.

Head over to the Unity inspector. Click on the UI Controller GameObject and click on the ‘Add component’, from here you will add the UIController script. Alternatively, you can also drag and drop the script into the GameObject. Once added, the UI Controller component will have three visible variables. After this, proceed to drag and drop the matching components inside the Canvas object.

Note: The UI Controller GameObject holds components such as the Transform. The UIController script can be attached as a component to any GameObject. In this case, we’re attaching it to the UI Controller GameObject.

Adding our components to the UI Controller.

Before going forward, we need to talk about UnityEvents and how we’re using them in this tutorial. If you click on the Button inside the canvas, you will see the following component:

A button’s view in the Unity inspector.

At the bottom of the component’s view, you see a On Click () field with an empty list and a +/- sign. This is a UnityEvent: They allow you to add methods of objects in the current scene to to it! So, whatever methods you add here will be called when the button receives a click (onClick).

If the method you assign is not in the same hierarchy as the component (In this case, our button), its reference might get lost. This can create many undesired scenarios where you have to reassign methods over and over again. To combat this problem, we will add a listener to our Button’s onClick event via code. Inside the Start method, add the following line:

ButtonSendMessage.onClick.AddListener(SendMessageUpdateRequest);
Enter fullscreen mode Exit fullscreen mode

The AddListener method allows us to subscribe any of our classes’ functions to the desired event; in this case, it’s onClick. The parameter taken is of type UnityAction, which is essentially anything that is considered a method according to Unity. Now, everytime you click the ‘Send Message’ button, the SendMessageUpdateRequest method will be called, no matter the contents.

Note: The AddListener method only accepts UnityEvents with no parameters, unless you add them via a Lambda expression.

EventController.cs

Before going any further, we need to declare our delegate and implement its functionality.
Add the following declarations inside EventController.cs (and before the Start method):

public delegate void OnMessageUpdateRequestDelegate(string _message);
Enter fullscreen mode Exit fullscreen mode

Here we declare our delegate OnMessageUpdateRequestDelegate. In this part of the declaration, you specify the parameters it will have (no parameters is also valid). In our case, we are adding a string message.

public static OnMessageUpdateRequestDelegate OnMessageUpdateRequest;
Enter fullscreen mode Exit fullscreen mode

We’re declaring OnMessageUpdateRequest as an OnMessageUpdateRequestDelegate. This is because delegates are methods treated as variables. Also, it’s static because we want to be able to access it from other classes without having to save a reference of the EventController (This is entirely optional, and it will vary heavily from your implementation/coding practices).

Now, replace the Start method for the following one:

public static void Event_OnMessageUpdateRequest(string _message)
{
    Debug.LogFormat("-- EventController // Received a request to update message: {0}", _message);

    OnMessageUpdateRequest(_message);
}
Enter fullscreen mode Exit fullscreen mode

Final EventController.cs
If you followed the steps correctly, your EventController class should look like this: Gist url.

Back to UIController.cs

Now that our EventController code is ready, it’s time to continue working with our implementation. We now want to subscribe our UpdateMessage method to EventController’s OnMessageUpdateRequest delegate. Inside the Start method, add this line of code:

EventController.OnMessageUpdateRequest += UpdateMessage;

Note: We did not use AddListener because delegates/events can simply add/remove listeners with the +=/-= operators. Also, a delegate can equal (=) one method whereas events cannot, they require use of +=/-= assignment operators.

Inside our SendMessageUpdateRequest method, we will store our message in a temporary string. We do this because we will call EventController’s Event_OnMessageUpdateRequest method:

void SendMessageUpdateRequest()
{
    // Store the input field's text into a string
    string message = InputField.text;

    EventController.Event_OnMessageUpdateRequest(message);
}
Enter fullscreen mode Exit fullscreen mode

If you recall EventController.cs, the contents of Event_OnMessageUpdateRequest method calls the OnMessageUpdateRequest delegate. Because SendMessageUpdateRequest invokes the event, and UpdateMessage is subscribed to the event, it will also be called.

🗳 Method call order

  1. Button 'Send Message’ onClick UnityEvent calls SendMessageUpdateRequest (subscriber).
  2. SendMessageUpdateRequest method calls Event_OnMessageUpdateRequest.
  3. Event_OnMessageUpdateRequest method calls OnMessageUpdateRequest.
  4. OnMessageUpdateRequest delegate calls UpdateMessage (subscriber).

This may be confusing, so let me rephrase them in a more readable way: Because method SendMessageUpdateRequest is subscribed to Button’s onClick UnityEvent, it will be called every time onClick is also called. Same goes for number 4: Because method UpdateMessage is subscribed to delegate OnMessageUpdateRequest, it will be called every time OnMessageUpdateRequest is also called. Hope that eases things up!

Finally, we do follow the last steps: replace the message, and clear up the input field.

void UpdateMessage(string _message)
{
    // Print the message
    Debug.LogFormat("-- UIController // Updating message: {0}", _message);

    // Replace our message display text with the contents of the message variable
    MessageDisplay.text = _message;

    // Reset the text of our input field
    InputField.text = "";
}
Enter fullscreen mode Exit fullscreen mode

Final UIController.cs
If you followed all steps correctly, this is how your UIController class should look like: Gist url

Now, press play and cross your fingers. If all goes well, congratulations! If not, let me know what’s the issue and we’ll figure it out!

💌 Summary


We have learned about delegates, events and UnityEvents in Unity. They are useful variables that can help overcome many programming challenges. I hope you have enjoyed reading this notGDC blog post.

💖 💪 🙅 🚩
timrodz
Juan Alejandro Morais

Posted on September 25, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related