Delegates and Events
Delegates
Imagine an office where you have three employees: Bob, Sue, and Mark (yeah, Mark).
One day, Boss Man calls Mark into his office and assigns him an important task.
Well, Mark, being who he is, decides to delegate his work to Sue. Sue, already swamped, tries her best but eventually delegates half the work to Bob.
In the end, Mark returns to Boss Man, and the job is done.
Boss Man is happy, oblivious to the fact that the task was actually delegated multiple times.
This is exactly how delegates in programming work! When you assign a delegate, you are essentially giving a task to a method. That method can then pass it to another method, allowing for flexible execution.
Delegates in Action
// Define a delegate type for work
delegate void DoWork();
// Declare a delegate variable (who is work assigned to)
DoWork Mark;
void Start() {
// Mark delegates work to Sue
Mark = Sue;
}
void Update() {
// Invoke the delegate (whoever is currently assigned the task will do the work.
// Originally, the task was assigned to Mark, but since Mark delegates the work,
// the method that actually runs will be Sue's or Bob's, depending on reassignment.)
Mark?.Invoke();
if (_isSueOverWorked) {
// If Sue is overworked, Mark's work is now delegated to Bob
Mark = Bob;
}
}//end update
void Sue() {
if (!_isSueOverWorked) {
Debug.Log("Sue is working...");
} else {
_isSueOverWorked = true;
}
}//end Sue()
void Bob() {
Debug.Log("Bob is taking over the work!");
}//end Bob()
Method Variables
Another way to think of Delegates is as variables for methods. First, you declare the type of method the delegate can reference. In the example above, our delegate is assigned to the DoWork()
method. However, DoWork
isn't the specific method being executed—rather, it defines the expected return type and parameters.
Next, we assign a variable to the delegate. In our office analogy, "Mark" serves as the variable name representing a method of type DoWork()
. Later, in the Start
method, we assign Mark
to Sue
, a method that matches the return type and parameters of DoWork()
but may perform different tasks within its implementation.
Events
Imagine your favorite YouTuber is hosting a special livestream event, but only subscribers get notified when it starts.
When the stream goes live, each subscriber, Gregory, Samantha, and Valerie each get a notification. However, they don’t all react the same way, instead each subscriber takes a different action.
- Gregory gets the notification while on his lunch break. He pops in his earbuds and casually watches on his phone.
- Samantha sees the alert and rounds up a group of friends so they can watch and discuss the stream together.
- Valerie gets the notification on her laptop and immediately joins the live chat to interact with other viewers.
Even though they all received the same notification, their responses are completely different.
The YouTuber has no idea who their subscribers are or what they’ll do when notified. They simply send out the alert, and each subscriber reacts in their own way.
This is exactly how events in programming work! When an event is triggered, any method listening for that event will execute its own unique action—just like each subscriber reacting differently to the livestream notification.
Events In Action
In this example our YouTuber
will act as one class who will declare (announce) the event LiveStreamNotification
and on the Start
will call the StartLiveStream
method which will boradcast (invoke) the LiveStreamNotification
to all subscribers.
using System;
using UnityEngine;
public class YouTuber : MonoBehaviour
{
// Define an event that subscribers can listen to
public static event Action LiveStreamNotification;
void Start()
{
// Simulating a livestream starting after 3 seconds
Invoke("StartLiveStream", 3f);
}
void StartLiveStream()
{
Debug.Log("The YouTuber has started a livestream!");
// Trigger the event (notify all subscribers)
LiveStreamNotification?.Invoke();
}
}
Each subscriber listens for the event and runs its own action.
In this first case Gregory class, call the WatchCasually
method.
using UnityEngine;
public class Gregory : MonoBehaviour
{
void OnEnable()
{
// Subscribe to the livestream event
YouTuber.LiveStreamNotification += WatchCasually;
}
void OnDisable()
{
// Unsubscribe to prevent memory leaks
YouTuber.LiveStreamNotification -= WatchCasually;
}
void WatchCasually()
{
Debug.Log("📱 Gregory gets the notification and watches casually on his phone.");
}
}
The Samantha class preforms the WtachWithFriends
method.
using UnityEngine;
public class Samantha : MonoBehaviour
{
void OnEnable()
{
// Subscribe to the livestream event
YouTuber.LiveStreamNotification += WatchWithFriends;
}
void OnDisable()
{
// Unsubscribe to prevent memory leaks
YouTuber.LiveStreamNotification -= WatchWithFriends;
}
void WatchWithFriends()
{
Debug.Log("Samantha gets the notification and gathers friends to watch together.");
}
}
And the Valerie class runs the JoinLiveChat
method.
using UnityEngine;
public class Valerie : MonoBehaviour
{
void OnEnable()
{
// Subscribe to the livestream event
YouTuber.LiveStreamNotification += JoinLiveChat;
}
void OnDisable()
{
// Unsubscribe to prevent memory leaks
YouTuber.LiveStreamNotification -= JoinLiveChat;
}
void JoinLiveChat()
{
Debug.Log("Valerie gets the notification and joins the live chat on her laptop.");
}
}
Observer Pattern
Events in programming are a foundational tool for implementing the Observer Pattern, a widely used design pattern that enables one-to-many communication between objects.
At its core, the Observer Pattern consists of:
- A Subject (or Publisher) who sends notifications
- Observers that automatically react when the subject notifies them of a change.
In our YouTuber example, the YouTuber class acts as the Subject, and Gregory, Samantha, and Valerie are the Observers. When the livestream starts, all subscribers are notified at the same time, each performing their own unique action.
This pattern is essential in game development for handling things like UI updates, game events, and AI behavior, ensuring that different parts of a program remain loosely coupled while still reacting dynamically to changes.
In the anology above our subscriber names weren’t chosen at random! Since events naturally establish an Observer Pattern, we thought it would be fun to give our subscribers names that reflect the idea of watching, listening, and staying alert—just like real observers do in programming!
- Valerie – Derived from Latin, meaning “watchful” or “vigilant.”
- Samantha – With Hebrew roots, this name is believed to mean “listener”, perfect for someone attentively observing events.
- Gregory – A classic Greek name meaning “watchful” or “alert”, which is exactly what event subscribers do!
Just like in programming, where observers react when an event occurs, our characters live up to their names by tuning in the moment the livestream notification arrives!