Null Reference Exception — это как грабли в темноте: рано или поздно вы на них наступите. Но в отличие от смешного падения в мультфильме, в коде это приводит к мгновенному краху приложения. Особенно досадно, когда ошибка вылезает у игрока, а не во время тестирования.
Почему так происходит? Как предупредить эти ошибки? И главное — как отловить их до того, как они отловят вас? Давайте разбираться.
Оглавление
Что такое Null Reference Exception и почему она возникает
Представьте, что вы просите друга передать вам яблоко, а его нет рядом. Вы тянете руку в пустоту — вот вам и Null Reference Exception (NRE). В коде это происходит, когда вы пытаетесь обратиться к объекту, которого не существует.
GameObject player = null; player.SetActive(true); // 💥 Boom! Null Reference Exception
Unity выкидывает эту ошибку, когда:
- Вы забыли присвоить значение переменной в Inspector.
- Объект был уничтожен (
Destroy
), но ссылка на него осталась. - Вы пытаетесь получить компонент, которого нет на GameObject.
- Асинхронная загрузка не успела завершиться, а вы уже лезете в данные.
Но хватит теории — давайте перейдём к практике.
5 способов избежать Null Reference Exception
1. Проверяйте ссылки перед использованием
Самый простой и надёжный способ — добавить проверку if (obj != null)
.
if (player != null) { player.SetActive(true); }
Казалось бы, очевидно? Но в пылу разработки такие проверки часто пропускают.
2. Используйте [SerializeField] и Inspector
Если переменная должна быть задана в редакторе, помечайте её [SerializeField]
и проверяйте в Awake()
:
[SerializeField] private GameObject _player; private void Awake() { if (_player == null) { Debug.LogError("Player не назначен в Inspector!"); } }
Это сразу укажет на проблему при запуске, а не в рандомный момент игры.
3. Операторы ?.
и ??
— ваши новые друзья
C# 6.0+ даёт нам крутые возможности:
?.
(null-conditional operator) — выполнит действие, только если объект существует.??
(null-coalescing operator) — подставит значение по умолчанию, если объект null.
// Вместо этого: if (healthBar != null) healthBar.UpdateHealth(100); // Можно так: healthBar?.UpdateHealth(100); // Или даже так: HealthBar healthBar = cachedBar ?? FindObjectOfType<HealthBar>();
4. Кэшируйте часто используемые компоненты
Проверь свои знания в нашем бесплатном ТЕСТЕ по Unity! Узнай, насколько хорошо ты его знаешь!
Постоянные GetComponent<>()
не только медленные, но и могут вернуть null
. Лучше кэшировать ссылку один раз:
private Rigidbody _rb; private void Awake() { _rb = GetComponent<Rigidbody>(); if (_rb == null) { Debug.LogWarning("Rigidbody не найден, добавляем..."); _rb = gameObject.AddComponent<Rigidbody>(); } }
5. Логируйте ошибки осмысленно
Вместо:
«NullReferenceException: Object reference not set to an instance of an object»
Лучше писать:
«Не удалось найти компонент HealthBar на игроке. Проверьте, что он добавлен в префаб.»
Так вы сэкономите часы дебаггинга.


Продвинутые методы защиты
Режим «Пуленепробиваемого кода»
Настройте Custom Exception Handling, чтобы ловить NRE до того, как игра упадёт:
AppDomain.CurrentDomain.UnhandledException += (sender, args) => { Debug.LogError($"Критическая ошибка: {args.ExceptionObject}"); // Сохранить прогресс / отправить отчет / показать экран "Сообщить об ошибке" };
Unit-тесты на наличие null-ссылок
[Test] public void PlayerPrefab_HasAllRequiredComponents() { var player = Instantiate(testPlayerPrefab); Assert.IsNotNull(player.GetComponent<Health>(), "Нет компонента Health!"); Assert.IsNotNull(player.GetComponent<Movement>(), "Нет компонента Movement!"); }
Статический анализ кода
Инструменты вроде JetBrains Rider или Roslyn Analyzers могут находить потенциальные NRE ещё до запуска игры.
А что, если…
…объект удалили в другом скрипте?
Используйте события вместо прямых ссылок:
// Плохо: enemy.TakeDamage(10); // А если enemy уже уничтожен? // Хорошо: public static Action<Enemy, int> OnEnemyDamaged; // Где-то в другом месте: OnEnemyDamaged?.Invoke(targetEnemy, damage);
…нужно работать с Unity-объектами после Destroy?
Помните: Destroy
не делает ссылку null
сразу. Проверяйте через == null
, но учтите, что для Unity-объектов это перегруженный оператор:
Destroy(gameObject); // Через кадр: if (gameObject == null) // Сработает, даже если физически объект ещё не удалён
Как жить без Null Reference Exception
- Проверяйте ссылки перед использованием.
- Кэшируйте компоненты и сложные запросы.
- Логируйте ошибки понятно.
- Тестируйте краевые случаи.
- Проектируйте архитектуру так, чтобы минимизировать риски (например, через события).
NRE — не враг, а сигнал: «Эй, тут что-то пошло не так!» Чем раньше вы его поймаете, тем крепче будет ваш код.
А какие способы борьбы с Null Reference Exception используете вы? Делитесь в комментариях!