Getting started with C# events in Unity (delegate + event)

Update: I have a more recent article about Events and the Action delegate. Read that before or after this one.

This is a simplified version of using C# / .NET events in Unity. Unity API provides a way to send messages between GameObjects, it’s called SendMessage(string methodName) but it is pretty clunky. It uses reflection to try to find a method with that name on the target GameObject. It’s not clear what parameters are accepted when you’re listening, typos aren’t noticed except at runtime when it fails, there’s no code-completion, you have to implement your own method of adding and removing listeners etc. Writing your own Observer pattern is fun once but as C# already provides the functionality, it makes sense to use the built-in version.

Using events allows you to easily communicate things from n objects to n listeners. It scales without any effort and decouples the reaction from the action. When an enemy dies, the score counter wants to know so it can add one point, nearby enemies want to know so they can come help, the sound system wants to know so it can play a new sound, the particle system wants to know so it can create some blood splatter particles on the spot and so on. All the interested parties can subscribe to the events and get notified when they happen. The enemy doesn’t have to keep track of who to inform since anyone can subscribe or unsubscribe as they please.

The C# way is to use delegates and events. Delegates have many uses and are worth reading more about but you can think of them as a single method interface. Any method with the same signature can be used as the type defined by the delegate. When you declare a delegate you’re not declaring a reference to a instance but a type (just like a class or interface).

Here we can see two different methods (Addition and Substraction) morph into the MathMagic type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public delegate int MathMagic(int value1, int value2);
    
    public int Addition(int value1, int value2)
    {
        return value1 + value2; 
    }
 
    public int Substraction(int value1, int value2)
    {
        return value1 - value2;
    }
 
    private void Start () {
        MathMagic method1 = Addition;
        MathMagic method2 = Substraction;
 
        Debug.Log(method1(1, 5));
        Debug.Log(method2(1, 5));
    }
 
 
// ... prints out:
//    6 
//   -4
	public delegate int MathMagic(int value1, int value2);
	
	public int Addition(int value1, int value2)
	{
		return value1 + value2;	
	}

	public int Substraction(int value1, int value2)
	{
		return value1 - value2;
	}

	private void Start () {
		MathMagic method1 = Addition;
		MathMagic method2 = Substraction;

		Debug.Log(method1(1, 5));
		Debug.Log(method2(1, 5));
	}


// ... prints out:
//    6 
//   -4

Here’s a concrete example of Unity code. I tried to make this as simple and readable to as many skill-levels of programmers as possible. Once you understand this, you can create your own Generic implementation or use the built-in .NET/Mono libraries. The Action<T> delegate is popular for example.

You can copy these two classes into Unity and play around with them. Just create two gameobjects, one with the ExplosiveBehavior and one with the ListenerBehavior.

The listening objects have a behavior called ListenerBehavior.

ListenerBehavior.cs

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
using UnityEngine;
using System.Collections;
 
public class ListenerBehavior : MonoBehaviour {
 
    private void Start () {
        // Find all the exploders in the scene and add listeners to each
        ExplosiveBehavior[] exploders = FindObjectsOfType<ExplosiveBehavior>();
        foreach(ExplosiveBehavior exploder in exploders)
        {
            AddListener(exploder);
        }
    }
 
    private void AddListener(ExplosiveBehavior exploder)
    {
        // For the "event" type, + and - operators have been overloaded. "+" adds
        // a method reference to the list of methods to call when the event is invoked.
        // "-" removes the reference from the list.
        exploder.OnUnitExploded += HandleOnUnitExploded;
    }
 
    private void RemoveListener(ExplosiveBehavior exploder)
    {
        exploder.OnUnitExploded -= HandleOnUnitExploded;
    }
 
    private void HandleOnUnitExploded (GameObject unit, Vector3 explodedPosition)
    {
        // Do something useful here
        Debug.Log(unit.name + " exploded at: " + explodedPosition);
    }
 
}
using UnityEngine;
using System.Collections;

public class ListenerBehavior : MonoBehaviour {

	private void Start () {
		// Find all the exploders in the scene and add listeners to each
		ExplosiveBehavior[] exploders = FindObjectsOfType<ExplosiveBehavior>();
		foreach(ExplosiveBehavior exploder in exploders)
		{
			AddListener(exploder);
		}
	}

	private void AddListener(ExplosiveBehavior exploder)
	{
		// For the "event" type, + and - operators have been overloaded. "+" adds
		// a method reference to the list of methods to call when the event is invoked.
		// "-" removes the reference from the list.
		exploder.OnUnitExploded += HandleOnUnitExploded;
	}

	private void RemoveListener(ExplosiveBehavior exploder)
	{
		exploder.OnUnitExploded -= HandleOnUnitExploded;
	}

	private void HandleOnUnitExploded (GameObject unit, Vector3 explodedPosition)
	{
		// Do something useful here
		Debug.Log(unit.name + " exploded at: " + explodedPosition);
	}

}

The units that are sending the events have a component called ExplosiveBehavior.

ExplosiveBehavior.cs

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
using UnityEngine;
 
// A delegate should be declared outside of the class since it is its own type
// and you most likely want to use it from elsewhere and not just in this class
public delegate void UnitExploded(GameObject unit, Vector3 explodedPosition);
 
public class ExplosiveBehavior : MonoBehaviour {
 
    public event UnitExploded OnUnitExploded;
 
    public void Explode(GameObject unit, Vector3 position)
    {
        // Check if there are any listeners. This throws an exception if OnUnitExploded is null.
        if(OnUnitExploded != null)
        {
            OnUnitExploded(unit, position);
        }
    }
 
    private void Update()
    {
        Explode(gameObject, transform.position);
    }
 
}
using UnityEngine;

// A delegate should be declared outside of the class since it is its own type
// and you most likely want to use it from elsewhere and not just in this class
public delegate void UnitExploded(GameObject unit, Vector3 explodedPosition);

public class ExplosiveBehavior : MonoBehaviour {

	public event UnitExploded OnUnitExploded;

	public void Explode(GameObject unit, Vector3 position)
	{
		// Check if there are any listeners. This throws an exception if OnUnitExploded is null.
		if(OnUnitExploded != null)
		{
			OnUnitExploded(unit, position);
		}
	}

	private void Update()
	{
		Explode(gameObject, transform.position);
	}

}

The delegate for this OnUnitExploded-event is defined on line 5 of ExplosiveBehavior. Remember that any method that has the same signature IS a UnitExploded. So when the event is of type UnitExploded, we can write out nice custom listener methods.

1
public delegate void UnitExploded(GameObject unit, Vector3 explodedPosition);
public delegate void UnitExploded(GameObject unit, Vector3 explodedPosition);

In the listener we define a method with the same signature but with a body that has some functionality. Every class that listens to this event can have it’s own method as long as the signature is the same as the delegates. It must return void and take in two parameters; GameObject unit and Vector3 explodedPosition.

1
2
3
4
5
private void HandleOnUnitExploded(GameObject unit, Vector3 explodedPosition)
    {
        // Do something useful here
        Debug.Log(unit.name + " exploded at: " + explodedPosition);
    }
private void HandleOnUnitExploded(GameObject unit, Vector3 explodedPosition)
	{
		// Do something useful here
		Debug.Log(unit.name + " exploded at: " + explodedPosition);
	}
  • vekkna

    Very clear, thanks.

  • scame

    Is it real Observer? Listener knows all about object which will dispatch event. Does Unity have something like this: object subscribes singleton and receives messages from any other object of any type. EventDispatcher and CustomEventListener from cocos2d-x are pretty examples.

    • http://designoidgames.com/ Lauri Hosio

      I’m not familiar with cocos2d. Because C# uses delegates you can pass in anything. String for the event name or a reference to the object being listened to for example. If you can tell me how your cocos2d implementation works, I can try to show how it’s best done in C#

      • scame

        Thank you for answer! I’ll try to explain.
        Cocos2d-x has EventDispatcher class. It is implemented as singleton. We can send and receive events via it. In order to send an event we should create EventCustom class, add (or not) some data to it as a pointer and invoke dispatchEvent method in EventDispatcher object with EventCustom object as parameters.

        EventCustom event(“my_event”);
        _eventDispatcher->dispatchEvent(&event);

        For receiving this event at any place we should create listener class EventListenerCustom with our event name and callback function (this function has pointer to EventCustom as parameter) as parameters and add this class to the global EventDispatcher object class. Our callback will be invoked after dispatchEvent call.

        Of course, with Unity object-tag-component paradigm and delegates I can solve any “events” task. But if Unity has something like “EventDispatcher”, I’ll be grateful for any information.

        • http://designoidgames.com/ Lauri Hosio

          I don’t think there’s anything like that built-in. So you want to be able to pass any type of event to EventDispatcher.DispatchEvent()? In C# it’s mostly built on calling specific events instead of one central dispatcher. But I did find what I think is an example of what you are looking for. ActionScript 3 has that same kind of a dispatcher and someone has implemented that into C# by having a dictionary of Delegates with the event name as keys:

          https://gist.github.com/keless/8727613