На главную > Блог > Категория > 🧩 Делегаты и события в Unity: полное руководство
C# Unity делегаты события паттерны архитектура
Если ты уже прошёл основы Unity и начал писать более-менее сложные скрипты, ты обязательно столкнулся с ситуацией, когда одному объекту нужно сообщить другому о событии: игрок наступил на триггер, здоровье упало до нуля, закончился таймер. Делегаты и события — главный инструмент для слабосвязанной архитектуры, который избавляет тебя от спагетти-кода и прямой зависимости между классами.
В этой статье мы разберём всё, что нужно знать о делегатах и событиях в Unity: от базового синтаксиса до продвинутых паттернов Action, UnityAction и лучших практик.
Делегат — это ссылка на метод. Простыми словами: это «контейнер», в который ты можешь положить любой метод с подходящей сигнатурой и вызвать его, даже не зная, какой именно метод вызываешь.
Делегат определяет тип метода:
// Объявление делегата
public delegate void MyDelegate(string message);
// Использование
public MyDelegate OnSomethingHappened;
public class SimpleDelegateExample : MonoBehaviour
{
public delegate void GreetingDelegate(string name);
private GreetingDelegate _greeting;
void Start()
{
// Подписываем методы
_greeting = SayHello;
_greeting += SayGoodbye;
// Вызываем всех подписчиков
if (_greeting != null)
_greeting("Игрок");
}
void SayHello(string name) => Debug.Log($"Привет, {name}!");
void SayGoodbye(string name) => Debug.Log($"Пока, {name}!");
}
Событие — это делегат с ограничениями. Он позволяет классу оповестить внешний мир о том, что что-то произошло, но запрещает внешнему коду вызывать это событие — только сам класс может это сделать.
public class Player : MonoBehaviour
{
// Объявляем событие
public event Action OnPlayerDeath;
public void TakeDamage(int damage)
{
// ... логика получения урона
if (health <= 0)
{
// Вызываем событие (ТОЛЬКО ИЗ ЭТОГО КЛАССА!)
OnPlayerDeath?.Invoke();
}
}
}
Player). Внешний код не сможет сделать player.OnPlayerDeath.Invoke() — компилятор выдаст ошибку.
В C# уже есть готовые, обобщённые делегаты, чтобы ты не писал свои каждый раз.
| Тип | Описание | Пример |
|---|---|---|
Action |
Не возвращает значение (void), может иметь до 16 параметров | Action<int, string> |
Func<T> |
Возвращает значение T, может иметь до 16 параметров | Func<int, bool> — принимает int, возвращает bool |
Predicate<T> |
Возвращает bool, принимает один параметр | Predicate<int> — проверяет условие для int |
public class ActionExample : MonoBehaviour
{
public event Action OnHealthChanged;
public event Action<int, int> OnDamageTaken; // (текущее, максимальное)
void TakeDamage(int damage)
{
// ... логика
OnHealthChanged?.Invoke();
OnDamageTaken?.Invoke(currentHealth, maxHealth);
}
}
Unity предоставляет свой делегат — UnityAction и UnityAction<T>. Главное преимущество: он сериализуется в инспекторе.
using UnityEngine.Events;
public class UnityActionExample : MonoBehaviour
{
public UnityAction OnJump;
public UnityAction<float, float> OnHealthChanged;
void Jump()
{
OnJump?.Invoke();
}
}
Action / Func — для чистого C# кода, без привязки к инспектору.UnityAction / UnityEvent — если хочешь видеть событие в инспекторе и привязывать методы через интерфейс Unity.
Это один из самых частых источников ошибок. Всегда отписывайся от событий в OnDisable или OnDestroy, чтобы избежать утечек памяти.
public class Subscriber : MonoBehaviour
{
[SerializeField] private Player _player;
void OnEnable()
{
// Подписываемся
_player.OnPlayerDeath += HandleDeath;
_player.OnDamageTaken += OnDamageTakenHandler;
}
void OnDisable()
{
// Отписываемся!
_player.OnPlayerDeath -= HandleDeath;
_player.OnDamageTaken -= OnDamageTakenHandler;
}
void HandleDeath()
{
Debug.Log("Игрок умер!");
}
void OnDamageTakenHandler(int current, int max)
{
Debug.Log($"Здоровье: {current}/{max}");
}
}
OnDisable.
UnityEvent — это сериализуемое событие, которое отображается в инспекторе. Ты можешь привязать методы прямо в редакторе, не писав код.
using UnityEngine.Events;
public class UnityEventExample : MonoBehaviour
{
// Отображается в инспекторе
public UnityEvent OnJump;
public UnityEvent<float> OnScoreChanged;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
OnJump?.Invoke();
OnScoreChanged?.Invoke(100);
}
}
}
| Характеристика | Делегат | Событие (event) |
|---|---|---|
| Можно вызвать извне? | ✅ Да | ❌ Только изнутри класса |
| Можно подписаться извне? | ✅ Да | ✅ Да |
| Сериализация в инспекторе | ❌ Нет | ❌ Нет (кроме UnityEvent) |
| Безопасность | 🟡 Средняя | ✅ Высокая (защита от вызова) |
public class GameEventArgs
{
public int Score;
public string PlayerName;
}
public class GameManager : MonoBehaviour
{
public event Action<GameEventArgs> OnGameOver;
public void EndGame(int score, string name)
{
var args = new GameEventArgs
{
Score = score,
PlayerName = name
};
OnGameOver?.Invoke(args);
}
}
public class AsyncEventExample : MonoBehaviour
{
public event Action OnDownloadComplete;
public async void DownloadData()
{
await Task.Delay(2000);
OnDownloadComplete?.Invoke();
}
}
OnEvent?.Invoke() спасает от NullReferenceException.Start, отписываешься в OnDestroy — если объект выключается, OnDestroy не вызывается. Используй OnEnable/OnDisable.Action или UnityAction.Action<T> или UnityAction<T>.UnityEvent.Делегаты и события — это фундамент любой архитектуры в Unity. Они позволяют создавать слабо связанные системы, легко расширять код и избегать спагетти-кода.
Главные выводы:
OnDisable.
Дата размещения статьи: 24-06-2026 в 08:20:25