Menu
CoddyTech

Subscribe in OnEnable

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 13 of 64.

A listener that is destroyed does not leave the event by itself. The event still holds its method, and the next Invoke calls it on the destroyed component. The first use of transform in that method throws MissingReferenceException, and since an exception ends the Invoke, the listeners after it are skipped too. So every += needs a -=, and Unity's pair of methods for that is OnEnable and OnDisable:

public class Guard : MonoBehaviour
{
    public Alarm alarm;

    void OnEnable()
    {
        alarm.OnTriggered += RunTo;
    }

    void OnDisable()
    {
        alarm.OnTriggered -= RunTo;
    }

    void RunTo(Vector3 spot)
    {
        transform.LookAt(spot);
    }
}

Unity calls OnDisable when the object is switched off with SetActive(false), when the script is disabled, and when the object is destroyed, before OnDestroy. OnEnable runs each time it comes back on. With this pair a guard listens exactly while it is active: a destroyed guard is gone from the event, a guard switched off for a cutscene ignores the alarm, and once switched on again it listens again.

Two other pairings go wrong. With += in Start and -= in OnDisable, a guard switched off and on never listens again, because Start runs only once. With += in OnEnable and no -=, every return adds RunTo again, and one alarm calls it twice. Removing a method that is not subscribed does nothing, so -= in OnDisable never needs a check.

challenge icon

Challenge

Easy

Make each villager listen to the bell only while it is active.

  1. In Villager.cs, move bell.OnRing += Hop; from Start() into a new void OnEnable(), and delete Start().
  2. Add void OnDisable() with bell.OnRing -= Hop;. The Check switches Cleo off and on to test this.

Press Play and ring four times: Ana goes home after ring 2, and no error shows. Then Check.

Try it yourself

using UnityEngine;

public class Villager : MonoBehaviour
{
    public Bell bell;
    public int ringsToLeave = 3;
    public int hops;

    void Start()
    {
        bell.OnRing += Hop;
    }

    void Update()
    {
        // fall back to the ground after a hop
        Vector3 p = transform.position;
        transform.position = new Vector3(p.x, Mathf.MoveTowards(p.y, 0.5f, 4f * Time.deltaTime), p.z);
    }

    void Hop(int ring)
    {
        hops++;
        transform.position += Vector3.up * 0.8f;
        if (hops == ringsToLeave)
        {
            Debug.Log(name + " goes home");
            Destroy(gameObject);
        }
    }
}

All lessons in Game Architecture in Unity

Practice on your own: Unity playground