Null в Unity
Часть раздела Основы скриптинга в Unity путешествия по Unity на Coddy. Урок 46 из 67.
У объекта Unity есть две половины: объект C#, на который ссылается ваш скрипт, и лежащий за ним объект движка. Destroy(obj) удаляет половину движка в конце кадра, но ваша переменная по-прежнему указывает на половину C#, поэтому с точки зрения обычного C# она не равна null. Unity переопределяет ==, чтобы учесть это: после уничтожения объекта выражение obj == null истинно, как и короткая проверка !obj. Это фальшивый null в Unity.
?., ?? и is null не используют этот ==. Они проверяют ссылку C#, которая всё ещё существует, поэтому позволяют выполнить вызов, а обращение к уничтоженному объекту вызывает исключение MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it. Эта метка показывает имя лидера гонки, которого могут выбить из гонки (уничтожить):
[SerializeField] private Transform leader;
void Update()
{
// вызывает исключение после уничтожения leader: string shown = leader?.name ?? "nobody";
string shown = leader != null ? leader.name : "nobody";
Debug.Log("Leader: " + shown);
}Для всего, что происходит от UnityEngine.Object (GameObjects, компоненты, ваши скрипты), используйте проверки == null, != null или if (obj), а ?. и ?? оставьте для обычных объектов C#, например строк. Также помните, что Destroy ждёт конца кадра: до этого момента объект всё ещё существует, а == null возвращает false.
Задание
СреднеДрон преследует цель, и цель уничтожается через 2 секунды после начала игры. После этого в консоли появляются ошибки MissingReferenceException, потому что target?.position не замечает, что цель исчезла.
Исправь Update(), используя собственную проверку null в Unity: пока цель существует, дрон летит к ней со скоростью speed; после её уничтожения дрон остаётся на месте, без ошибок.
Нажми Play и следи за консолью, исправь скрипт, затем нажми Check.
Попробуйте сами
using UnityEngine;
public class Chaser : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float speed = 2f;
void Start()
{
Destroy(target.gameObject, 2f); // target уничтожается через 2 секунды
}
void Update()
{
Vector3 goal = target?.position ?? transform.position;
transform.position = Vector3.MoveTowards(transform.position, goal, speed * Time.deltaTime);
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Основы скриптинга в Unity
1Ваш первый скрипт
Привет, UnityРазместите кубДвигайте каждый кадрПоворачивайте каждую секундуУправляйте клавишамиПовторение: управляйте кубом2Инспектор
Поля инспектораSerializeFieldЧисла с плавающей точкой и MathfАтрибуты инспектораПовторение: лифт5Преобразования и иерархия
Родители и детиМасштабПосмотри на объектыДвигайся к точкеПовторение: Луна на орбите8Поиск объектов
Поиск по имениТегиПоиск по типуСсылки лучше поискаNull в UnityПовторение: ближайшая монета6Игровые объекты и компоненты
Ссылки в InspectorПолучение компонентаОтсутствующие компонентыДобавление компонентаRequireComponentПоказать и скрытьПовторение: выключатель света