Events & Delegates for Dummies

Events & Delegates

Events and Delegates were something I struggled with when I was first learning how they worked. Once I figured it out it became one of the most useful and powerful techniques for games. Its especially useful for helping to decouple classes while allowing for messaging.

Delegate - Simply a container for a function that can be used as a variable.

Event - Allows you to specify a delegate that gets called when some event in your code is triggered.

Overview

Objects that want to listen for a specific event can do so by registering as a listener, and when that event is trigged, all the listeners will have their listening function directly called. As many listeners can be registered to each delegate as you want and (heres the best part) each listener can call its own different function from the same event call.

This means if say you have a GUI, another unit and a Controller that are all listening for the same event (eg UnitSpawned), each one can react differently. The GUI may need to update itself, the other unit maybe check distance and the Controller may do something else.

Uses:

The most useful thing I've used them for is to encapsulate my InputController and separate it from all the scripts that might be listening for Input. They can then individually subscribe to listen if the input is triggered without requiring a direct connection. Games are often highly emergent and I sometimes feel enforcing hierarchies or fixed communication channels is a non-intuitive way to structure a world. This lets remote functions on objects get called directly without needing hardcoded references, resorting to Find, or broadcasting (which are all kind of sloppy and clutter your code).

Its a concept worth understanding and once you wrap your head around it, you will wonder how you got by without it.

Code Examples:

// Event Handler/ Event Manager
// 1 & 2 are same script eg DelegatesAndEvents.cs

1. Define delegates and events

// just definition of what the event looks like/arguments 

**// a. Define Delegate **

public delegate void NameOfDelegate ( GameObject unit)

**// b. Define Event **

public static event NameOfDelegate onUnitSpawn;
public static event NameOfDelegate onUnitDestroy;

2. // trigger events

//  This is the same script as #1 

  public static void NewUnitCreated(GameObject unit)
  {
// does this delegate have at least one subscriber? 
// should always check that the delegate actually points to something
if(onUnitSpawn != null){  // lighting bolt icon means event   

  onUnitSpawn(unit);
}
  • this calls the function in all listeners
  • That list of all listener functions is called delegate invocation list
  • when this call is made, all listeners subscribed will call their onUnitSpawn(unit) functions, even though the functions may be completely different ( as long as all the parameter lists are the same as defined in the Delegate )
  • you can have as many subscribers as you'd like subscribed to each event . // Heres another Event that uses onUnitDestroy public static void UnitDeath (GameObject unit){ if(onUnitDestroy != null){
    onUnitDestroy(unit); } }

3. Subscribe GameObjects To Events

Separate Scripts that subscribe want to listen for events. Eg. GUI, HealthManager, Overlord, Other units - Anything that needs to listen for events

// There can be many different scripts that subscribe to each event as long as they use the same calling structure and arguments

// a. Subscribe object to listen to Event

// anytime this event (onUnitSpawn) fires, call this method locally (NewUnitCreated)

void OnEnable(){
    DelegatesAndEvents.onUnitSpawn += this.NewUnitCreated;
  }
  • you don't need to type out arguments because the delegate defines which arguments you need
  • eg. this.NewUnitCreated <- doesnt need () or arguments, must match delegate definition in the Event Manager .

    // Unsubscribe event

    void OnDisable(){

    DelegatesAndEvents.onUnitSpawn -= this.NewUnitCreated;

    }

*// b. Define the function that the subscriber will execute. *

  • can be different for each subscribing script as long as the name and argument list match.
  • create the local function that will be called when the event is triggered . public void NewUnitCreated(GameObject unit){

    // do stuff, update self etc
    // assign stuff to unit, 
     // create gui etc

    }

// each subscriber will react in its own way & you can have as many subscribers you want.

eg. So a UnitManager, GUIManager and OtherUnit can all subscribe to the same delegate event but can call different functions when the event is triggered so long as the function signature matches (eg the arguments match the delegate definition)

Overview Scripts

public class DelegatesAndEvents : MonoBehaviour{

    // i. Define Delegate
    public delegate void NameOfDelegate ( GameObject unit)

    // ii. define events  
    public static event NameOfDelegate onUnitSpawn;
    public static event NameOfDelegate onUnitDestroy;

    // 2 - doesn't need to be in its own function, but good practice
    public static void NewUnitCreated(GameObject unit)
    {
        // does this delegate have at least one subscriber? 
        if(onUnitSpawn != null){        
            onUnitSpawn(unit);
        }
    }

    // Heres another Event that uses onUnitDestroy

      public static void UnitDeath (GameObject unit){
          if(onUnitDestroy != null){
              onUnitDestroy(unit);
          }
      }
  }

  public class GUIManager : MonoBehaviour{

  // subscribe to event
  void Start(){
      DelegatesAndEvents.onUnitSpawn += this.NewUnitCreated;
  }

  // de-subscribe to event
  void OnDisable(){
     DelegatesAndEvents.onUnitSpawn -= this.NewUnitCreated;
  }

  // define method to be called - signature needs to match delegate signature
  public void NewUnitCreated(GameObject unit){
  // create gui of healthbar for unit etc
  }
}

// another subscribing class
public class UnitManager : MonoBehaviour{

  // subscribe to event
  void Start(){
      // note this calls the Class, not the delegate name
      DelegatesAndEvents.onUnitSpawn += this.NewUnitCreated;
  }

  // de-subscribe to event
  void OnDisable(){
      DelegatesAndEvents.onUnitSpawn -= this.NewUnitCreated;
  }

  // define method to be called - signature needs to match delegate signature
  public void NewUnitCreated(GameObject unit){
  // do something else to the same unit on the same event call
  eg . unitCount ++;
  }
}

Additional Resources

This was a very good, clear explanation for Unity https://www.youtube.com/watch?v=ihIOVj9t0_E

Very Good, 1:09 explantation of Delegates, Event Callbacks and Lambda Expressions. Its working on C# in VS and isn't unity specific but was the most clear & helpful explanation I found. https://www.youtube.com/watch?v=8e2GEFNctwQ

Unity's own explanation https://unity3d.com/learn/tutorials/modules/intermediate/scripting/events

Good Explanation, not as clear code https://www.youtube.com/watch?v=N2zdwKIsXJs

Good example, poor explanation of what and why https://www.youtube.com/watch?v=ihIOVj9t0_E