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. Кэшируйте часто используемые компоненты
Постоянные GetComponent<>() не только медленные, но и могут вернуть null. Лучше кэшировать ссылку один раз:
Проверь свои знания в нашем бесплатном ТЕСТЕ по Unity! Узнай, насколько хорошо ты его знаешь!
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 используете вы? Делитесь в комментариях!


