Подписка в 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 для -= никогда не нужна проверка.
Задание
ЛегкоДеревенский колокол звонит каждый раз, когда ты нажимаешь пробел (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);
}
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Архитектура игр в Unity
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыСовместная работа компонентовПовторение: модульная турель4Сериализованные данные
Сериализуемые классыСписки в InspectorПеречисления в InspectorПовторение: таблица лута2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействия5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиВолны как данныеПовторение: база предметов3События
События C# в UnityПодписка в OnEnableПоля UnityEventСобытия управляют интерфейсомПовторение: всплывающее окно достижения6Сохранение с помощью JSON
JsonUtilityСохранение игрыЗагрузка и значения по умолчаниюПовторение: слоты сохранения