Подписка в OnEnable
Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 13 из 64.
Уничтоженный слушатель не удаляет себя из события. Событие по-прежнему хранит его метод, и следующий вызов 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 никогда не нужна проверка перед -=.
Задание
ЛегкоПусть каждый житель деревни слушает колокол только пока он активен.
- В
Villager.csперенесиbell.OnRing += Hop;изStart()в новый методvoid OnEnable()и удалиStart(). - Добавь
void OnDisable()сbell.OnRing -= Hop;. Кнопка «Проверить» выключает и включает Клео, чтобы проверить это.
Нажми «Играть» и позвони четыре раза: Ана уйдёт домой после второго звонка, и ошибок не появится. Затем нажми «Проверить».
Попробуйте сами
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Сериализованные данные
Сериализуемые классыСписки в инспектореПеречисления в инспектореПовторение: таблица добычиПовторение: определяем добычу2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействияПовторение: ближайший в пределах досягаемости5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиСохраняем ветерВолны как данныеПовторение: база предметовПовторение: покупка в магазинеПотренируйтесь самостоятельно: Песочница Unity