Thursday, May 13, 2021

How to use delegates and events in Unity C#

One of the most powerful tools in Unity3D C# coding I've come across so far is the implementation of delegates and events. It may be hard to learn at first, and that's because in order to understand the purpose of delegates, you need to understand how events are handled. And in order to understand how events are handled, you need to understand delegates. 

Sounds confusing so far? Bear with me. It's actually pretty simple, the important thing here is to learn delegates first! Even if its implementation doesn't make any sense to you, if you understand how it works you have a solid foundation to understand events (where the real power lies!). I've seen countless tutorials on delegates and events, but in my opinion they were very hard to understand. Hopefully my explanation will make it a bit easier to understand delegates and events.

This tutorial is for those who already have a basic/intermediate understanding of C# in Unity3D.


Why you should learn it.

Once you understand it, it's an extremely powerful asset in your coding toolbox, which you can pretty much implement in any Unity C# project you're working on. It also makes your code a lot more cleaner and efficient.


What does it do?

Within one click of a mouse button or any assigned key or trigger, you can make multiple things happen at once without having to repeat code.


What are delegates?

When you declare a delegate you can store methods in a variable. 

The way I understood it best is comparing it to a .zip file. One file (method) that hold multiple files (methods) inside, you can add or remove files (methods) as you go along. In the end you can call that zip file (method) and all the files inside will be executed at once.

That's the basic explanation, there's more to it of course but at this point this is all you need to know. Declaring a delegate is easy, for this example I'm going to make a button in the scene.


  1. Add the button in the hierarchy (right-click UI > button). 
  2. Add component and call it "OnButtonClick", or whatever you want.
  3. Assign "OnButtonClick" script to button. (Add component).
Open up your new C# script, in my case OnButtonClick, and declare a delegate named 'Action Click'. (on line 7, you can also name this whatever you want). Then you give the delegate a name (on line 8) and make it a public static event. Make sure you create a public method for your button that also checks to see of the delegate is not equal to null to prevent errors in the future, and assign onClick();

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class OnButtonClick : MonoBehaviour
{
    public delegate void ActionClick();
    public static event ActionClick onClick;
    

    public void OnClick()
    {
        
        if (onClick != null)
        {
            onClick();
        }
        
    }

}

Hook it up to your button:



That's it! You created your first delegate that gets activated when you click the button! But what can you do with it? As of right now, it doesn't do much of course. Nothing is being triggered yet. 

In order to see what it can do you need to create a simple cube in your scene for instance. Create a cube and attach a script called CubeController to it (or whatever you want to name it). Open up CubeController script and add:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CubeController : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        OnButtonClick.onClick += ChangeColor;
        OnButtonClick.onClick += DoSomethingElse;
        OnButtonClick.onClick += DoSomethingElse2;
        OnButtonClick.onClick += DoSomethingElse3;
    }

    private void ChangeColor()
    {
        GetComponent<MeshRenderer>().material.color = Color.red;
    }

    private void DoSomethingElse()
    {
        print("Do something else");
    }

    private void DoSomethingElse2()
    {
        print("Do something else 2");
    }

    private void DoSomethingElse3()
    {
        print("Do something else 3");
    }

}

For this example I'm changing the color of the cube to red, as well as three other methods you can check in the console window in Unity.


At this stage you probably wonder, what's the point? Can't I just call ChangeColor(); DoSomethingElse(); , DoSomethingElse2(); , DoSomethingElse3(); directly in Start();?

Yes you can, but in order to understand how events work, you need to understand this stage first. You're basically assigning ALL these methods to OnButtonClick, so when you click the mouse button, all these methods will be executed at once. You are 'subscribing' the methods into your delegate so to speak.

Now that we know this, let's make another object. How about a capsule this time that falls to the ground when you click the button. Create a capsule in your scene and attach a component script called CapsuleController for instance (Don't forget to add a Rigidbody as well with gravity setting turned off).

 Open up the script and add the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CapsuleController : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        OnButtonClick.onClick += AddGravity; // Subscribing AddGravity Method to 
                                                the 'listener'.
    }

    private void AddGravity()
    {
        GetComponent<Rigidbody>().useGravity = true;        
    }

    private void OnDisable()
    {
        OnButtonClick.onClick -= AddGravity;
    }
}

As you can see I added another method (OnDisable) in the 'event' the gameObject gets disabled, it also unsubscribes from the onClick 'listener'.


Conclusion

In this example I used a button to set off the event, but you can also activate it by any keycode input, or trigger in your game of course!

Hopefully you can see now how powerful this implementation of Unity events are! The gameobjects don't have to know about each other(! In this example, the cube and capsule did not need to know about each other! ), all they do is listening out for the onClick event and see if it get's triggered! 

The cool thing is that you can have any method that also unsubscribes at the same time! That way you don't have to mess around with booleans for instance. Let's say you want a sound to play only once, you can have a method 'void PlaySound()' for example and in that method play a sound and directly unsubscribe from it so it doesn't play again. There are countless of things you can do with delegates and events!

If it's still not clear to you, don't worry. It took me some time to fully understand it as well. In case you are still lost when it comes to delegates and methods in Unity, luckily there are a lot of tutorials out there that can help you. Check YouTube and search for 'Unity delegates and events', it will bring up tons of results. Remember some are able to explain it better than others.

What are some examples you use events for in your game? Let me know.

Keep on coding and creating amazing games!

~Marcus