База знаний ЯЮниор
  • C#
    • 🗃️Ссылочные и значимые типы в C# Ключевые слова ref, out, in Что нужно знать новичку
    • 📋Использование констант
    • 🔖Snippet или фрагмент кода
    • 📝Пустые строки
    • 🛡️Инкапсуляция
  • Unity
    • 🚶‍♂️Управление Параметрами Аниматора
    • ⚙️Динамическое изменение объектов
      • 🛠️Создание объектов
      • 🪛Изменение объектов
      • 🪚Добавление и изменение компонентов
      • 🔥Удаление объектов и компонентов
    • 🔊События
      • 🧬Параметризация
      • 🔗Совмещение событий
      • 📡Action и UnityAction
      • 🕹️UnityEvent
    • 🔌Подключение среды разработки к Unity
    • ⌚Корутины
      • 🪄Управление корутинами
      • ⏰Yield Instruction
      • 🕵️‍♂️Как устроены корутины?
  • Git и GitHub
    • 🗃️Git
    • 🗄️GitHub
    • 🖥️GitHub Desktop
Powered by GitBook
On this page
  • Что такое Action и UnityAction?
  • В чем разница между Action и UnityAction?
  • Как вызвать объявленное событие?
  • Как получать информацию из события?

Was this helpful?

  1. Unity
  2. События

Action и UnityAction

Автор: Дмитрий Прокопьев

Что такое Action и UnityAction?

Action и UnityAction это типы делегатов C#, которые можно использовать в качестве типа события. Посмотрим на пример объявления события:

public event Action AmountChanged;
  1. Объявление начинается с модификатора доступа. Так как события используются как средство передачи информации, это всегда public

  2. Ключевое слово event инкапсулирует событие, ограничивает количество доступных операций. Подробнее при это здесь

  3. Тип делегата для этого события определяет поведение и передаваемые параметры события

  4. Название события указывает, какую информацию оно передает. События всегда именуются в прошедшем времени, так как указывают на уже произошедшее.

Именование происходит в формате Существительное (какой объект изменился?) + Глагол (как объект изменился?). Например, MoneyAdded, EnemyKilled, DoorOpened, PaymentReceived и.т.д.

В чем разница между Action и UnityAction?

Эти типы отличаются несколькими параметрами:

  1. Название

  2. Action находится в namespace System, а UnityAction в namespace UnityEngine

  3. Action может принимать от 0 до 16 параметров, а UnityAction от 0 до 4.

  4. UnityAction может в некоторых обстоятельствах работать некорректно, в отличие от Action

По указанным выше причинам рекомендуем использовать только Action.

Как вызвать объявленное событие?

Нам известно, что событие передает информацию своим слушателям. Поэтому возникает вопрос, как запустить процесс передачи информации?

public class Wallet : MonoBehaviour
{
    public int Money { get; private set; }
    
    public event Action AmountChanged;
    
    public void AddMoney(int amount)
    {
        if (amount <= 0)
            return;
        
        Money += amount;
    }
    
    public bool TrySpendMoney(int amount)
    {
        if (amount <= 0)
            return;
        
        bool isEnough = Money >= amount;
        
        if (isEnough)
            Money -= amount;
        
        return enough;
    }
}

Мы добавили кошельку событие AmountChanged, через которое он сообщает, что количество денег изменилось. Для начала подумаем, когда об этом нужно сообщать. Количество денег изменяется только в методах AddMoney и TrySpendMoney, поэтому вызывать событие будет логично именно в них. Сделать это можно c помощью метода Invoke:

AmountChanged.Invoke();

Однако есть одна проблема. Так как такое событие это делегат, то до первого подписанного метода оно не будет проинициализировано, а значит будет равно null. Из за этого в момент вызова метода Invoke мы получим NullReferenceException. Решение простое, для этого нужно проверить событие на null перед вызовом:

if (AmountChanged != null)
    AmountChanged.Invoke();

Правда каждый раз расписывать это заново неудобно, поэтому для этого есть краткая форма записи:

AmountChanged?.Invoke();

Как получать информацию из события?

Для этого другим компонентам потребуется подписаться на событие. Но как указать, что именно им делать при вызове события? Чтобы разобраться, попробуем привязать к кошельку компонент его отображения - WalletView. Так он реализован сейчас:

public class WalletView : MonoBehaviour
{
    [SerializeField] private Text _amountView;
    [SerializeField] private Wallet _wallet;
    
    private void DisplayAmount()
    {
        float amount = _wallet.Money;
        _amountView.text = amount.ToString();
    }
}

Мы хотим, чтобы при срабатывании события AmountChanged вызывался метод DisplayAmount. Тогда каждый раз, когда количество денег в кошельке изменится, оно будет выведено на экран, чего мы и хотели.

Для этого мы можем подписаться на это событие методом DisplayAmount. Сделать это нам поможет оператор +=, вот так:

_wallet.AmountChanged += DisplayAmount;

По техническим причинам необходимо всегда отписываться от событий, когда мы больше не хотим принимать информацию из них. Для этого есть оператор -=:

_wallet.AmountChanged -= DisplayAmount;

Но когда это делать? Подписываться нужно с того момента, когда мы хотим начать слушать событие, а отписываться нужно, когда мы больше не хотим это делать. Но в этом случае мы всегда хотим получать информацию с кошелька!

Тогда будет удобно сделать это в методах OnEnable и OnDisable, так как они выполняются при включении и выключении объекта, включая начало и конец рантайма:

WalletView.cs
public class WalletView : MonoBehaviour
{
    [SerializeField] private Text _amountView;
    [SerializeField] private Wallet _wallet;
    
    private void OnEnable()
    {
        _wallet.AmountChanged += DisplayAmount;
    }

    private void OnDisable()
    {
        _wallet.AmountChanged -= DisplayAmount;
    }
    
    private void DisplayAmount()
    {
        float amount = _wallet.Money;
        _amountView.text = amount.ToString();
    }
}

А так теперь выглядит сам кошелек:

Wallet.cs
public class Wallet : MonoBehaviour
{
    public int Money { get; private set; }
    
    public event Action AmountChanged;
    
    public void AddMoney(int amount)
    {
        if (amount <= 0)
            return;
        
        Money += amount;
        AmountChanged?.Invoke();
    }
    
    public bool TrySpendMoney(int amount)
    {
        if (amount <= 0)
            return;
        
        bool enough = Money >= amount;
        
        if (enough) 
        {
            Money -= amount;
            AmountChanged?.Invoke();
        }
        
        return enough;
    }
}

И вот, это наконец-то работает! Теперь WalletView реагирует на каждое изменение в Wallet и отображает это на экране!

PreviousСовмещение событийNextUnityEvent

Last updated 1 year ago

Was this helpful?

Продолжим рассматривать пример с кошельком из :

🔊
📡
введения в тему событий