1. Основы работы с инспектором Unity: свойства, сериализация и частые проблемы
Основы работы с инспектором Unity: свойства, сериализация и частые проблемы
Инспектор Unity — это основной визуальный интерфейс, через который разработчики и геймдизайнеры взаимодействуют с компонентами игровых объектов. Однако то, что мы видим в окне инспектора, является лишь верхушкой айсберга. Под капотом скрывается сложный механизм преобразования данных, понимание которого критически важно для создания собственных атрибутов и расширения редактора.
Механизм сериализации данных
Ключевым понятием при работе с инспектором является сериализация — процесс преобразования структур данных или объектов в формат, который можно сохранить и затем восстановить. В контексте Unity сериализация выполняет роль моста между управляемым кодом на C# и внутренним ядром движка, написанным на C++.
!Схема процесса сериализации данных между C# и ядром Unity
Когда вы прикрепляете скрипт к GameObject, Unity не читает значения переменных напрямую из оперативной памяти C#. Вместо этого движок сериализует эти данные, сохраняет их в своем внутреннем представлении и уже оттуда отрисовывает в окне инспектора.
> Сериализация в Unity — это не только способ сохранения данных сцены или префаба на жесткий диск, но и единственный механизм передачи данных между вашим скриптом и окном редактора. > > Unity Technologies
Понимание этого факта объясняет, почему некоторые переменные отображаются в инспекторе, а другие — нет. Инспектор показывает только сериализованные данные.
Правила сериализации по умолчанию
Чтобы поле класса было сериализовано (и, как следствие, появилось в инспекторе), оно должно соответствовать строгим правилам. Unity автоматически сериализует поле, если оно:
public.static, const или readonly.int и float, строки, векторы, массивы и списки поддерживаемых типов, а также ссылки на объекты, унаследованные от UnityEngine.Object).Если вам нужно скрыть публичное поле от геймдизайнеров, используется атрибут [HideInInspector]. Данные по-прежнему будут сохраняться в префаб, но визуально исчезнут из редактора.
Управление инкапсуляцией: атрибут SerializeField
Хорошей практикой в объектно-ориентированном программировании является сокрытие внутреннего состояния класса. Делать все поля публичными только ради того, чтобы настроить их в инспекторе — грубое нарушение инкапсуляции.
Для решения этой проблемы применяется атрибут [SerializeField]. Он принудительно указывает движку сериализовать приватное или защищенное поле.
Свойства и автосвойства
Долгое время Unity принципиально не сериализовала C#-свойства (Properties), так как свойство — это по сути пара методов (геттер и сеттер), а не область памяти. Однако с развитием языка C# и появлением автосвойств разработчики стали часто использовать их для лаконичности.
Начиная с новых версий Unity, появилась возможность сериализовать скрытое поле, которое компилятор создает для автосвойства, с помощью конструкции [field: SerializeField].
| Тип члена класса | Поддерживается по умолчанию | Способ принудительной сериализации |
|---|---|---|
| Публичное поле | Да | Не требуется |
| Приватное поле | Нет | [SerializeField] |
| Автосвойство | Нет | [field: SerializeField] |
| Словарь (Dictionary) | Нет | Требуется кастомный класс или пакет |
Проблема полиморфизма и ссылочных типов
Одна из самых частых проблем, с которой сталкиваются разработчики при попытке создать гибкую архитектуру — потеря полиморфизма при сериализации.
Представьте, что у вас есть базовый класс Spell и два наследника: Fireball и Heal. Если вы создадите поле типа Spell и присвоите ему экземпляр Fireball, стандартный сериализатор Unity «обрежет» объект до базового класса. Все уникальные поля Fireball будут потеряны.
Для решения этой проблемы был введен атрибут [SerializeReference]. Он указывает движку сериализовать объект не как значение (по значению), а как ссылку, сохраняя его реальный тип.
При использовании [SerializeReference] в инспекторе появится выпадающий список, позволяющий выбрать конкретную реализацию интерфейса или базового класса.
Частые ошибки и ограничения
Работа с инспектором таит в себе несколько неочевидных подводных камней, которые могут привести к потере данных или падению производительности редактора.
Переименование переменных
Связь между сохраненными данными в сцене и вашим скриптом осуществляется по имени переменной. Если вы переименуете поле health в currentHealth, Unity решит, что старое поле удалено, а новое только что создано. Значение, настроенное в инспекторе, сбросится до значения по умолчанию.
Чтобы безопасно переименовать переменную, необходимо использовать атрибут [FormerlySerializedAs]:
Ограничение глубины сериализации
Unity не поддерживает циклические ссылки при стандартной сериализации (когда Объект А ссылается на Объект Б, а Объект Б — на Объект А). Чтобы избежать зависания редактора из-за бесконечных циклов, движок ограничивает глубину вложенности сериализуемых структур.
Максимальная глубина сериализации строго ограничена математически: , движок остановится на $N
Сложные коллекции
Стандартный инспектор отлично справляется с одномерными массивами и List<T>, но совершенно не понимает многомерные массивы (int[,]) и словари (Dictionary<TKey, TValue>). Если вам нужно отобразить словарь в инспекторе, придется либо использовать два синхронизированных списка (один для ключей, другой для значений), либо писать собственный Property Drawer, к созданию которых мы перейдем в следующих статьях курса.
Понимание того, как данные проходят путь от скрипта до визуального интерфейса, является фундаментом. Зная ограничения стандартной сериализации, вы сможете проектировать архитектуру так, чтобы она была не только правильной с точки зрения кода, но и удобной для настройки в редакторе.