Menu
CoddyTech

Subscribe in OnEnable

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 11 of 50.

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

The village bell rings each time you press Space (Bell.cs, ready), and its event OnRing tells the villagers. A villager hops at each ring and goes home after ringsToLeave rings: Hop destroys it. Ana goes home after 2 rings. Ring a third time and the Console shows a MissingReferenceException.

Fix Villager.cs so a villager listens to the bell exactly while it is active:

  • Subscribe Hop to the bell's event each time the villager is switched on, and unsubscribe it when the villager is switched off or destroyed. Keep Hop and Update as they are.
  • A villager switched off with SetActive(false) does not hop. Switched on again, it hops once per ring.

Press Play, ring the bell four times and watch the Console, 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);
        }
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Game Architecture in Unity