Menu
CoddyTech

Подписка в OnEnable

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 11 из 50.

Уничтоженный слушатель не покидает событие сам по себе. Событие всё ещё хранит его метод, и следующий вызов Invoke вызывает его у уничтоженного компонента. Первое использование transform в этом методе вызывает MissingReferenceException, а поскольку исключение прерывает Invoke, пропускаются и следующие слушатели. Поэтому каждому += нужен соответствующий -=, и для этого в Unity есть пара методов — OnEnable и 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 вызывает OnDisable, когда объект выключают с помощью SetActive(false), когда скрипт отключают и когда объект уничтожают — до вызова OnDestroy. OnEnable выполняется каждый раз, когда объект снова включают. С этой парой методов охранник слушает событие, только пока он активен: уничтоженный охранник удаляется из события, охранник, выключенный на время кат-сцены, игнорирует тревогу, а после повторного включения снова начинает слушать.

Два других варианта использования этой пары приводят к ошибкам. Если поместить += в Start, а -= — в OnDisable, охранник, которого выключили и снова включили, больше не будет слушать, потому что Start выполняется только один раз. Если поместить += в OnEnable и не добавить -=, при каждом повторном включении RunTo будет добавляться снова, и один сигнал тревоги вызовет его дважды. Удаление метода, который не был подписан, ничего не делает, поэтому в OnDisable для -= никогда не нужна проверка.

challenge icon

Задание

Легко

Деревенский колокол звонит каждый раз, когда ты нажимаешь пробел (Bell.cs, готов), а его событие OnRing оповещает жителей деревни. Житель подпрыгивает при каждом звоне и уходит домой после ringsToLeave звонов: Hop уничтожает его. Ана уходит домой после 2 звонов. Позвони в третий раз — и в Console появится MissingReferenceException.

Исправь Villager.cs, чтобы житель слушал колокол только пока он активен:

  • Подписывай Hop на событие колокола каждый раз, когда житель включается, и отписывай его, когда житель выключается или уничтожается. Оставь Hop и Update без изменений.
  • Выключенный с помощью SetActive(false) житель не подпрыгивает. После повторного включения он подпрыгивает один раз при каждом звоне.

Нажми Play, позвони в колокол четыре раза и следи за Console, затем нажми Check.

Попробуйте сами

using UnityEngine;

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

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

    void Update()
    {
        // опуститься обратно на землю после прыжка
        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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Архитектура игр в Unity