Перейти к основному содержимому

SerializeReference Selector

Реализацию выбирают прямо в инспекторе — из списка с поиском, без своего редактора.

Быстрый старт​

До — Unity APIПосле — FastTools
[SerializeReference]
private IWeapon _primary =
new Pistol();
[TypeSelector]
[SerializeReference]
private IWeapon _primary;

Какие классы в списке​

Список строится по типу поля; атрибут может сузить его дополнительными типами.

public interface IMelee : IWeapon { }
public sealed class Sword : IMelee { }

public abstract class StatusEffect { }

public class Modifier<T> : IModifier { }
public sealed class DamageModifier : Modifier<float> { }
Тип поляЧто в списке
Интерфейс IWeaponКлассы, реализующие его: Crossbow, Pistol, Railgun, Shotgun, Sword
IWeapon и typeof(IMelee) в атрибутеКлассы, подходящие под оба: Sword
Абстрактный класс StatusEffectЕго наследники: BurnEffect, FreezeEffect
Modifier<float>Наследник DamageModifier и сам Modifier<Single>
List<IModifier>AmmoModifier, DamageModifier, NameModifier и Modifier<T> с выбором T
  • В инспекторе runtime-объекта нет классов из editor-only сборок (UnityEditor, asmdef только для Editor, папки Editor): билд плеера не сможет их создать.
  • Аргументы generic-класса выводятся из типа поля; если вывести их нельзя, окно спрашивает каждый и предлагает только типы, которые Unity умеет сериализовать.
  • Ограничение можно взять и из другого поля.
  • [TypeSelectorDisplay] настраивает строку класса в списке или скрывает класс.

Обязательное поле​

[TypeSelector(Required = true)]
[SerializeReference] private IWeapon _primary;

С Required = true пустое поле показывает Required reference is not set; подробнее — в разделе Обязательное поле.

Списки​

В списке с [TypeSelector] кнопка «+» открывает выбор класса и добавляет новый экземпляр, а <None> — пустой элемент. При нескольких выбранных объектах каждый получает свой экземпляр, всё в одной группе Undo.

«+» у Sidearms открывает выбор класса и добавляет Shotgun«+» у Sidearms открывает выбор класса и добавляет Shotgun

Смена класса​

Новый экземпляр получает значения полей с теми же именами:

ПолеPistol→ Shotgun
Damage3737
Magazine Size12—
Pellets—8, начальное значение

Смена Pistol на Shotgun сохраняет Damage = 37Смена Pistol на Shotgun сохраняет Damage = 37

примечание

Вложенная ссылка с тем же именем переходит в новый класс тем же экземпляром, а не копией.

Скрипт .cs, перетащенный из Project на заголовок поля, так же меняет класс поля на свой.

Меню заголовка​

Правый клик по заголовку поля:

ПунктЧто делает
Copy Serialize ReferenceКопирует класс и данные поля; копия живёт до перезагрузки домена
Paste Serialize ReferenceВставляет копию в поле подходящего типа; копия пустого поля очищает его
Make Unique ReferenceДаёт полю собственную копию общей ссылки; у необщей ссылки пункта нет
Find Usages of PistolИщет класс в проекте через Unity Search
Link to Existing → …Указывает поле на экземпляр из другого поля того же объекта
Create New Script…Создаёт [Serializable] класс под тип поля и назначает его после компиляции
Save as Template…Сохраняет значение под именем; шаблоны хранятся в настройках редактора на этом компьютере, не в проекте
Paste Template → …Создаёт экземпляр из шаблона; в списке только подходящие полю
Paste Template → Remove Missing (N)…Удаляет шаблоны, класс которых не загружается; виден, только если такие шаблоны есть
внимание

Copy/Paste и шаблоны не переносят вложенные поля [SerializeReference]: скопированный Railgun вставится без _chargeEffect.

Общие ссылки​

Два поля объекта могут указывать на один экземпляр: правка через одно видна в другом. Такие поля помечены Shared reference #N, а Make unique даёт полю собственную копию вместе с вложенными ссылками.

Make unique создаёт независимую копию общей ссылкиMake unique создаёт независимую копию общей ссылки

Продублированный элемент списка получает собственный экземпляр, а не ссылку на тот же. За это отвечает настройка Auto de-alias duplicated list elements в общих настройках, по умолчанию она включена.

Восстановление потерянного типа​

После переименования, переноса или удаления класса у поля появляется Missing type, а данные остаются в ассете.

Потерянная ссылка с Fix и подсказкой → Pistol в инспектореПотерянная ссылка с Fix и подсказкой → Pistol в инспекторе

ДействиеЧто делает
FixОткрывает выбор класса, включая скрытые через Hidden
→ PistolНазначает предложенный класс; причина в подсказке: то же имя, то же имя в другом регистре или похожее имя
внимание

На ассете Fix переписывает файл, и Undo его не отменит.

В сцене и Prefab Mode исправление остаётся в памяти: Undo его отменяет, а сохранение делает окончательным и очищает историю Undo объекта. Такое исправление возвращает только простые поля верхнего уровня: вложенные объекты, массивы, списки, векторы, цвета и ссылки на объекты получают значения по умолчанию.

Когда Fix нет​

СлучайЧто делать
Выбрано несколько объектовВыберите один: до этого Missing type не показывается
В сцене или Prefab Mode есть несохранённые измененияСохраните: до этого поле показывает <None> без Missing type
Экземпляр префаба, класс хранится в исходном префабеИсправьте исходный префаб, его имя в подсказке
Экземпляр префаба, класс задан overrideВыберите новый класс на экземпляре или отмените override

Остальное исправляет SerializeReference Tooling.

Собственный инспектор​

Поле с [TypeSelector] в своём редакторе рисует обычный PropertyField: выбор класса и «+» списка появляются сами, вызывать пакет не нужно.

UI Toolkit — CreateInspectorGUIIMGUI — OnInspectorGUI
new PropertyField(
serializedObject
.FindProperty("_sidearms"))
EditorGUILayout.PropertyField(
serializedObject
.FindProperty("_sidearms"));

Если у поля [SerializeReference] нет атрибута [TypeSelector] или элемент списка рисуется отдельно через GetArrayElementAtIndex, PropertyField выбора класса не покажет. Его можно нарисовать в своём редакторе, вызвав один из методов:

МетодЧто рисует
SerializeReferenceEditorGUI.CreateField()Поле в CreateInspectorGUI
SerializeReferenceEditorGUI.CreateList()Список в CreateInspectorGUI
SerializeReferenceEditorGUI.DrawFieldLayout()Поле в OnInspectorGUI
SerializeReferenceIMGUIList.Draw()Список в OnInspectorGUI

Ограничения поверх типа поля передаются аргументом baseTypes, как типы в [TypeSelector(...)].

Ограничения​

  • Allow. На [SerializeReference] не действует — анализатор AFT0002 предупредит об этом.
  • Несовместимые ограничения. Если ни один класс не подходит сразу под тип поля и все типы из атрибута, список классов будет пустым: например, [TypeSelector(typeof(Sword))] на поле StatusEffect _onHit;. Анализаторы AFT0003, AFT0005 и AFT0009 предупредят об этом при компиляции.

Пример в пакете​

Поля Loadout с этой страницы и ассеты с потерянными типами для Fix есть в примере SerializeReferences.