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

TypeSelector

Окно выбора типа, которое вы настраиваете под каждое поле.

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

[TypeSelector(typeof(ITwoHanded), Allow = TypeAllow.None)]
[SerializeField] private SerializableType<Weapon> _weapon;

Поле предлагает только конкретное двуручное оружие: наследников Weapon, реализующих ITwoHanded.

Без атрибутаС [TypeSelector]
Все классы Weapon в окне выбора, включая абстрактныеВсе классы Weapon в окне выбора, включая абстрактныеВ окне выбора только Axe и BowВ окне выбора только Axe и Bow

Где применяется​

ПолеРезультат выбора
stringЗаписывается assembly-qualified name
Serializable TypesНастраивается выбор обёртки
[SerializeReference]Создаётся экземпляр выбранной реализации — см. SerializeReference Selector
внимание

В плеере строковое поле находит тип по имени, как Serializable Types: класс, который используется только через такой выбор, может быть вырезан при Managed Stripping Level Low и выше.

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

public interface ITwoHanded { }

public abstract class Weapon { }
public abstract class MeleeWeapon : Weapon { }
public abstract class RangedWeapon : Weapon { }

public sealed class Sword : MeleeWeapon { }
public sealed class Axe : MeleeWeapon, ITwoHanded { }
public sealed class Bow : RangedWeapon, ITwoHanded { }

Типы в атрибуте сужают список: остаются только классы, совместимые со всеми сразу.

Аргументы [TypeSelector] на поле stringВ списке
typeof(Weapon)Weapon, MeleeWeapon, RangedWeapon, Sword, Axe, Bow
typeof(Weapon), typeof(ITwoHanded)Axe, Bow
typeof(Sword), typeof(Axe)Пусто
typeof(MeleeWeapon), Allow = TypeAllow.NoneSword, Axe

T обёртки и тип поля [SerializeReference] работают как ещё один тип в атрибуте:

// Axe, Bow
[TypeSelector(typeof(ITwoHanded))]
[SerializeField] private SerializableType<Weapon> _twoHandedType;

// Axe, Bow
[TypeSelector(typeof(ITwoHanded))]
[SerializeReference] private Weapon _twoHandedWeapon;
примечание

В инспекторе runtime-объекта селектор не предлагает типы из editor-only сборок (UnityEditor, asmdef только для Editor и папки Editor): в билде плеера они не найдутся.

Свойства​

СвойствоПо умолчаниюПоведение
AllowTypeAllow.AllПускает в список абстрактные классы (Abstract), интерфейсы (Interface), оба вида или ни один. На [SerializeReference] игнорируется
RequiredfalseПредупреждает о пустом имени типа или null в managed-ссылке

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

[TypeSelector(typeof(Weapon), Required = true)]
[SerializeField] private string _secondaryWeapon;

Пустое обязательное поле показывает предупреждение под селекторомПустое обязательное поле показывает предупреждение под селектором

С Required = true пункт <None> остаётся доступным. Потерянный тип с сохранённым именем проверку проходит.

Настройка проверки по всему проекту и в CI описана в разделе проверки обязательных полей.

Ограничение из другого поля​

Передайте nameof(...), чтобы текущее значение поля или свойства управляло списком кандидатов:

[SerializeField] private SerializableType<Weapon> _weaponClass;

[TypeSelector(nameof(_weaponClass), Allow = TypeAllow.None)]
[SerializeField] private string _weaponName;

Выберите MeleeWeapon в Weapon Class — Weapon Name предложит его конкретных наследников. Смена ограничения не очищает ранее выбранное имя.

Выбор MeleeWeapon в Weapon Class оставляет в Weapon Name только Axe и SwordВыбор MeleeWeapon в Weapon Class оставляет в Weapon Name только Axe и Sword

Источник ограниченияБазовый тип
System.TypeЗначение поля
stringТип по имени через Type.GetType()
SerializableType / SerializableMonoScriptЗначение .Type
Массив этих значенийКаждый элемент; List<T> не поддерживается
  • У поля внутри [Serializable]-класса или элемента списка источник читается из того же экземпляра.
  • Пока источник пуст или не разрешился, ограничения от него нет: строковый Weapon Name предложит все конкретные типы. У обёртки остаётся её собственный T.

Ошибки в строковых аргументах​

Если имя типа записано верно, но такой тип не загружен, инспектор показывает предупреждение:

[TypeSelector("Spear, Assembly-CSharp")]
[SerializeField] private string _weaponName;

Ограничение не разрешилось — инспектор показывает предупреждение под полемОграничение не разрешилось — инспектор показывает предупреждение под полем

TypeSelectorDisplay​

[TypeSelectorDisplay] на классе меняет только его строку в окне выбора:

Параметр на SwordВ окне выбора
Name = "Longsword"Longsword в списке и в закрытом поле; поиск находит и по Sword
Group = "Weapons/Melee"Weapons → Melee → Longsword вместо namespace
Tooltip = "A balanced blade"Подсказка при наведении
Icon = "d_ScriptableObject Icon"Иконка: встроенная по имени, ассет по пути с расширением или из Resources без расширения
Hidden = trueНет в списке; присваивание из кода и уже сохранённое значение работают

Подклассы настроек не наследуют.

Имя Longsword, иконка и группа Weapons/Melee в окне выбораИмя Longsword, иконка и группа Weapons/Melee в окне выбора

примечание

[TypeSelector] и [TypeSelectorDisplay] помечены [Conditional("UNITY_EDITOR")]. В классах из внешней DLL, собранной без этого символа, их настроек нет, включая Hidden.

Окно выбора​

Окно группирует типы по namespace или Group и различает одинаковые имена по сборкам.

Избранные и недавние типы на корневой странице окна выбораИзбранные и недавние типы на корневой странице окна выбора

На корневой странице окно держит типы, которые нужны чаще других:

  • Favorites — избранное. Чтобы добавить тип, нажмите звёздочку справа от его строки или Space, когда строка выделена.
  • Recent — последние выбранные типы.

Показ Favorites и длина Recent (0 скрывает раздел) настраиваются во вкладке Settings окна FastTools. Её открывает шестерёнка в правом нижнем углу окна выбора, там же оба списка очищаются.

Generic-типы​

При выборе открытого generic-типа окно предлагает выбрать аргументы и возвращает сконструированный закрытый тип:

public abstract class Enchantment { }
public sealed class Fire : Enchantment { }
public sealed class Frost : Enchantment { }

public sealed class Enchanted<T> : Weapon
where T : Enchantment { }

Выберите Enchanted<T> в поле SerializableType<Weapon> — окно предложит наследников Enchantment, а после выбора Fire запишет Enchanted<Fire>.

Выбор аргумента generic-типа в окне выбораВыбор аргумента generic-типа в окне выбора

  • Generic-аргумент может сам быть generic-типом: окно сначала спросит его аргументы.
  • Если все аргументы выводятся из типа поля, закрытый тип возвращается сразу.
  • Интерфейсы, абстрактные классы и скрытые типы в аргументах не предлагаются.

TypeSelectorWindow​

TypeSelectorWindow открывает то же окно из кастомного инспектора или окна редактора, например по кнопке UI Toolkit:

using Aspid.FastTools.Types.Editors;

var button = new Button { text = "Select weapon" };
button.clicked += () => TypeSelectorWindow.Show(
screenRect: GUIUtility.GUIToScreenRect(button.worldBound),
filter: new TypeSelectorFilter
{
Types = new[] { typeof(Weapon) }
},
currentAqn: selectedTypeName,
onSelected: aqn => selectedTypeName = aqn);

Обработчик получает assembly-qualified name или null при выборе <None>.

примечание

У TypeSelectorFilter Allow по умолчанию TypeAllow.None, а у [TypeSelector] — TypeAllow.All: окно выше предлагает только конкретное оружие.

Свойства фильтра и параметры окна — в справочнике API: TypeSelectorFilter, TypeSelectorWindow.

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

Зависимый список, имена из [TypeSelectorDisplay] и обязательное поле показаны в примере Types, а окно выбора из редакторского кода — в EditorTools.

Волна обычных и элитных врагов в сцене TypesВолна обычных и элитных врагов в сцене Types