События C# в Unity
Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 12 из 64.
Когда счёт меняется, это затрагивает сразу несколько вещей: текст со счётом, звук, достижение. Если ScoreKeeper вызывает каждую из них, ему нужно поле для каждой, а каждое новое свойство требует его редактировать. Событие меняет подход: ScoreKeeper объявляет об изменении, а те, кого оно касается, подписываются.
Для этого нужны два слова из C#. Action<int> — это тип метода, который принимает один int и ничего не возвращает. event такого типа — это список таких методов: другой код добавляет свой метод с помощью +=, а владелец вызывает их все с помощью Invoke. На обычном C#:
using System;
class Alarm
{
public event Action<int> Rang;
public void Ring(int hour)
{
Rang?.Invoke(hour);
}
}
class Sleeper
{
public void WakeUp(int hour)
{
Console.WriteLine("Up at " + hour);
}
}
// в другом месте
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7); // Подъём в 7В Unity издатель и его подписчики — это компоненты, обычно расположенные на разных GameObject:
using System;
using UnityEngine;
public class ScoreKeeper : MonoBehaviour
{
public event Action<int> OnScoreChanged;
private int score;
public void AddPoints(int points)
{
score += points;
OnScoreChanged?.Invoke(score);
}
}// ScoreLabel.cs, на текстовом элементе HUD
public ScoreKeeper keeper;
public TMP_Text label;
void Start()
{
keeper.OnScoreChanged += ShowScore;
}
void ShowScore(int score)
{
label.text = "Score: " + score;
}Action<int> означает, что каждый подписчик — это метод, принимающий один int, в данном случае новый счёт. Invoke вызывает все подписанные методы в порядке их подписки. Если подписчиков нет, значение события равно null, поэтому обычный вызов OnScoreChanged(score) вызвал бы исключение NullReferenceException, а ?. вместо этого пропускает вызов. Ключевое слово event ограничивает другие скрипты операциями += и -=: в ScoreLabel выражения keeper.OnScoreChanged(5) и keeper.OnScoreChanged = null не скомпилируются.
У ScoreKeeper нет поля для метки, и он не знает о её существовании. Счётчик комбо или проигрыватель звука может подписаться точно так же, и ScoreKeeper останется без изменений; тестовая сцена без HUD будет работать без ошибок. Подписчик находит издателя через ссылку из Inspector, как здесь, или любым известным тебе способом, например с помощью GetComponent или синглтона. Удалять подписчика с помощью -= важно, когда объекты уничтожаются; этому посвящён следующий урок.
Задание
ЛегкоПусть боец объявляет свою серию ударов.
- В
Fighter.csдобавьusing System;иpublic event Action<int> OnComboChanged;. - Замени строки, в которых используются
comboTextиflame, наOnComboChanged?.Invoke(combo);и удали оба поля. - В
Start()компонентовComboTextиComboFlameнапишиfighter.OnComboChanged += Show;и+= Grow;.
Нажми «Играть» и нажми пробел несколько раз: серия ударов увеличивается, а затем сбрасывается. Затем нажми «Проверить».
Попробуйте сами
using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;
public class Fighter : MonoBehaviour
{
public float comboWindow = 1f;
public TMP_Text comboText;
public Transform flame;
private int combo;
private float sinceHit;
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
combo++;
sinceHit = 0f;
comboText.text = "Combo x" + combo;
flame.localScale = Vector3.one * (0.5f + 0.25f * combo);
}
else if (combo > 0)
{
sinceHit += Time.deltaTime;
if (sinceHit > comboWindow)
{
combo = 0;
comboText.text = "";
flame.localScale = Vector3.one * 0.5f;
}
}
}
}
Все уроки раздела Архитектура игр в Unity
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыКомпоненты работают вместеПовторение: модульная турельПовторение: спаренная пушка4Сериализованные данные
Сериализуемые классыСписки в инспектореПеречисления в инспектореПовторение: таблица добычиПовторение: определяем добычу2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействияПовторение: ближайший в пределах досягаемости5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиСохраняем ветерВолны как данныеПовторение: база предметовПовторение: покупка в магазинеПотренируйтесь самостоятельно: Песочница Unity