How to Use Events in Unity with C#
Coding with Less Dependencies
Introduction
Often when programming a game, you will run into instances where something will happen in one script/class that needs to trigger something to happen in another script/class. For example, an enemy hits the player and the player’s health bar updates to show damage. We could have the player’s health bar check the player’s health in the update method but checking every frame would be a waste of resources. A more efficient way to handle these events is with… Events
About
- Subjects: C# and Unity
- Objective: To show how to implement cleaner code that is easier to maintain through the use of Events.
- Development Time: 10 min
Getting Started
Events
Events are used to alert other C# classes that something has happened. An event works basically the same way a newsletter works for a business. The company creates it, writes down the information they want people to know, and sends it out to a list of contacts. To build the list of contacts the business has to have people subscribe to the newsletter, but the business does not know who is subscribing, only that people have subscribed. The subscribers have the option to subscribe when they want info or unsubscribe when they no longer want the newsletter.
To demonstrate this, we will create a class called Damageable.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Damageable : MonoBehaviour
{
}
Add the Fields
To create our event, we must first declare our fields.
public delegate void Damaged();
public static event Damaged OnDamage;
This is the equivalent of creating our newsletter.
First thing we do is create a delegate of type Damaged. We will discuss delegates a little later but for now just know we need a delegate.
Next we create a static event with the same type as our delegate, Damaged, and assign it the variable name of OnDamage. We declare this as static so that it will be accessible outside of this class without having to have a reference to an instance of this class. Again, we do not know who is going to subscribe to this event
Now that we have our event declared we need to decide if we want to send any information with it. What do we want our subscribers to know? For now, we will send a damage amount. To do so we just pass an integer into the delegate like so.
public delegate void Damaged(int damageAmount);
public static event Damaged OnDamage;
Place the Trigger
The next thing we need to decide is where we want to trigger this event. Since this is for damage let us trigger it when we have a collision with an object that has the tag “DamageZone”. We also need to pass in a damage amount with our trigger. Here we will take 10 damage.
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "DamageZone")
OnDamage(10);
}
We have one problem. If no one has subscribed to our event we will get an error. To avoid this, we can trigger the event only if there are subscribers like so.
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "DamageZone")
{
if(OnDamage != null)
OnDamage(10);
}
}
Let us add another tag for objects with a “BigDamage” tag that has a damage amount of 50.
Our Damageable class should now look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Damageable : MonoBehaviour
{
public delegate void Damaged(int damageAmount);
public static event Damaged OnDamage;
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "DamageZone")
{
if(OnDamage != null)
OnDamage(10);
}
if(col.gameObject.tag == "BigDamage")
{
if(OnDamage != null)
OnDamage(50);
}
}
}
Subscribe to the Event
Now that we have finished setting up our event and trigger, let us have another class subscribe to it.
For this example, I have created the following Health class.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Health : MonoBehaviour
{
private int currentHealth = 100;
void Start()
{
Debug.Log("My starting health is: " + currentHealth);
}
public void DamageHealth(int damageAmount)
{
currentHealth = currentHealth - damageAmount;
Debug.Log("My health is: " + currentHealth);
}
}
Here we can see we have 100 health and when the class is started it should print to the Unity console “My starting health is: 100”.
There is also a method called DamageHealth that takes a damage amount and subtracts it from the current health and prints the updated health to the console.
Subscribing to our event is simple. We need to grab the event and then add the method we want to respond to the event.
Damageable.OnDamage += DamageHealth;
We will place this subscription inside the OnEnable method so that it will be subscribed to during the script’s initialization.
void OnEnable()
{
Damageable.OnDamage += DamageHealth;
}
If we disable this script it is good practice to unsubscribe from the event. We can do that simply inside an OnDisable method.
void OnDisable()
{
Damageable.OnDamage -= DamageHealth;
}
Inside of our editor I have set up a player with a Rigidbody2D and Collider2D and attached a simple movement script to walk left and right. I have also added two sprites with the tags “DamageZone” and “BigDamage” each with their own Collider2D.
I have attached our Damageable and Health script to our player.
Now when we start we can see that Health prints to the console the expected “My starting health is: 100”.
If I walk into the “DamageZone” object we can see that the player has now taken 10 damage.
If we walk into the “BigDamage” object we will take 50 damage.
And that is it. We can now subscribe any class to our event.
Delegates Vs Events
When we declared our event, we declared a delegate with the name Damaged beforehand and then declared our event with the type Damaged and gave it the name OnDamage.
Technically we could declare the delegate and then a static Damaged like so
public delegate void Damaged(int damageAmount);
public static Damaged OnDamage;
If you modify your working code to this you can see that it still works exactly the same. So why even use event?
Event adds a layer of protection for our subscription list. If, for instance, you had a health bar that also subscribed to the OnDamage delegate you could initiate the subscription with = instead of +=. This will compile and not throw any warnings or errors, but the side effect is that it overwrites the entire subscription list to only notify UpdateHealthBar.
void OnEnable()
{
Damageable.OnDamage = UpdateHealthBar;
}
By using event you will force subscribers to use only += or -= effectively stopping other classes from accidentally overriding other subscribers.
Thank you for stopping by. Stick around and check out more of our tutorials or posts like our piece on Encapsulation. Also, leave a comment telling us what you liked or did not like about the tutorial. Was it easy to follow along? What do you want to learn next? As always check out some of our published apps below.