We can use delegates and events to create a messaging or broadcasting system that is much more robust and versatile than direct script communication via references.
Think of an event as a variable that is called like a static method. As soon as an event is raised from one script, we can listen to it from other scripts and perform corresponding actions. To do this, we want to subscribe another method to our event, which in turn is called. This functionality is given to us with delegates. A delegate holds a list of methods and when the delegate is called, all subscribing methods are called as well.
Best, we look at an example:
Creating a health display with events
This section will show you how to set up a HUD that displays the player’s health whenever it changes. Since there might be more objects that are interested in the player’s health, we will build an event system, that fires whenever the health changes and can be received by any number of other scripts easily.
- Create a Player C# script and attach it to a GameObject in the scene.
- Create a UI Text object on a Canvas in the scene. I’m using the Unity 4.6 upwards GUI system, but the old system or plain debug log messages will work fine for testing purposes.
Start with the player script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using UnityEngine; public class Player : MonoBehaviour { public int health; void Start() { UpdateHealth(15); } void UpdateHealth(int amount) { health += amount; //Raise OnHealthUpdate event } } |
Our player has a health variable that is going to be initialized in the Start() method. The UpdateHealth() method will be called to add or subtract from health and then call the appropriate event, which can be received by different listeners in the scene.
Next, add in a delegate and an event before line 7:
1 2 |
public delegate void HealthUpdate(); public static event HealthUpdate OnHealthUpdated; |
This is a typical delegate and event pair. Watch the official Unity tutorials on delegates and events for a detailed explanation.
In short: The event OnHealthUpdated is declared as static, so that we can call it from anywhere. It needs a type, which we declare with the delegate HealthUpdate. Note, that the type is HealthUpdate, but the actual delegate is a method, so it needs a return type of void and the parenthesis.
Let’s raise the OnHealthUpdated event in our player script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using UnityEngine; public class Player : MonoBehaviour { public int health; public delegate void HealthUpdate(); public static event HealthUpdate OnHealthUpdated; void Start() { UpdateHealth(15); } void UpdateHealth(int amount) { health += amount; if(OnHealthUpdated != null) OnHealthUpdated(); } } |
If an event is raised without having subscribers, it will throw an error.
Before we call our event like a method we should include a null-check to avoid errors.
An event without subscribers returns null.
Be mindful of the parenthesis: the event itself is called OnHealthUpdated, but when we want to raise it, we use it like a method OnHealthUpdated().
Now that we have a player that raises an event as soon as its health is initialized, we can build the receiving end, the HUD script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using UnityEngine; public class HUD : MonoBehaviour { void OnEnable() { //Subscribe to HealthUpdate event Player.OnHealthUpdated += HandleOnHealthUpdated;; } void HandleOnHealthUpdated () { Debug.Log ("HealthUpdated was received."); } } |
It is a good idea to subscribe to events only when an object is enabled, so we place our code in the OnEnable() method from MonoBehaviour. We can access our static event and assign a method from HUD to it with the += operator. This method can be any custom function that is already declared in your script, but it’s also very convenient to use auto completion and let MonoDevelop suggest an implementation (often called an EventHandler).

It’s time for our first test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using UnityEngine; public class HUD : MonoBehaviour { void OnEnable() { Player.OnHealthUpdated += HandleOnHealthUpdated;; } void HandleOnHealthUpdated () { Debug.Log ("HealthUpdated was received."); } } |
If you’ve done everything correctly (and both scripts are attached to GameObjects in the scene), the debug message will be printed to the console as soon as the player calls the HealthUpdated event.
Great, now there’s only one thing left to figure out: How to send parameters with events, so that our HUD knows what the player’s health is.
Change line 7 of the player script from:
1 |
public delegate void HealthUpdate(); |
to:
1 |
public delegate void HealthUpdate(int newHealth); |
Now our delegate expects all subscribing methods to receive an integer parameter and we can send one from our event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using UnityEngine; public class Player : MonoBehaviour { public int health; public delegate void HealthUpdate(int newHealth); public static event HealthUpdate OnHealthUpdated; void Start() { UpdateHealth(15); } void UpdateHealth(int amount) { health += amount; if(OnHealthUpdated != null) OnHealthUpdated(health); } } |
Adjust the HUD script to accept the incoming newHealth parameter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using UnityEngine; public class HUD : MonoBehaviour { void OnEnable() { Player.OnHealthUpdated += HandleOnHealthUpdated;; } void HandleOnHealthUpdated (int newHealth) { Debug.Log ("HealthUpdated was received. New Health is: " + newHealth); } } |
One more important thing:
Whenever you subscribe a method to an event, be sure to unsubscribe it as well to prevent memory leaks and possibly errors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using UnityEngine; public class HUD : MonoBehaviour { void OnEnable() { Player.OnHealthUpdated += HandleOnHealthUpdated;; } void HandleOnHealthUpdated (int newHealth) { Debug.Log ("HealthUpdated was received. New Health is: " + newHealth); } void OnDisable() { Player.OnHealthUpdated -= HandleOnHealthUpdated; } } |
That’s all there is to a basic messaging system! Events can be used for anything from updating stats over input to artificial intelligence. They can also include more interesting parameters like a reference to the event raising object, but that goes beyond this introduction.
Here are some more ideas how to continue:
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 37 38 39 40 41 42 43 44 45 |
using UnityEngine; public class Player : MonoBehaviour { //Variable to set in the inspector public int maxHealth; //Readonly property public int Health {get;private set;} //Create delegate and event pair public delegate void HealthUpdate(int newHealth); public static event HealthUpdate OnHealthUpdated; void Start() { //Initialize Health, but be sure that Health is currently at zero Health = 0; UpdateHealth(maxHealth); //Test the event system by decreasing Health every second InvokeRepeating("TestHealthChange", 1f, 1f); } void TestHealthChange() { UpdateHealth(-1); } void UpdateHealth(int amount) { //Cancel this method if the incoming amount is zero if(amount == 0) return; //If its not zero, then add or subtract from Health Health += amount; //Raise OnHealthUpdated if it is not null if(OnHealthUpdated != null) OnHealthUpdated(Health); Debug.Log ("OnHealthUpdate was fired."); } } |
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 |
using UnityEngine; using UnityEngine.UI; //important! //You need to be using namespace UI to have access to the Text component! public class HUD : MonoBehaviour { //Populate healthText with a GUI Text in the inspector public Text healthText; void OnEnable() { //Subscribe to OnHealthUpdated event Player.OnHealthUpdated += HandleOnHealthUpdated; } //Event handler void HandleOnHealthUpdated (int newHealth) { //Update text or graphics and give debug feeedback healthText.text = "Player health: " + newHealth; Debug.Log("Health update was received. New health: " + newHealth); } void OnDisable() { //Unsubscribe from HealthUpdate Player.OnHealthUpdated -= HandleOnHealthUpdated; } } |
Additional info: If you pay close attention to the console output from the last two scripts, you might notice, that the messages seem to come in the wrong order:

How can the event be received before it was fired? Of course, it can’t. Printing to the console is a very slow operation and due to many things going on at the same time, Debug.Log() might be too slow to accurately depict what is going on. Don’t worry, just be aware that it’s only the console messages that are slow, your event system is still working correctly.