UIBuilder
Namespace: Vortex.Unity.UI.UIBuilder
Сборка: ru.vortex.unity.ui.uibuilder (только Editor)
Назначение
Editor-инструмент вёрстки: создаёт под выбранным объектом готовый UI-слой из префаба-примитива за одно действие — контейнер UIComponent, экземпляр примитива внутри, собранные части и нужные Set*Component со связями.
Возможности:
- Каталог примитивов по папке (с подпапками), выбор из выпадающего списка с группировкой по подпапкам
- Контекстное меню Hierarchy:
Vortex Primitives/Create Text,Vortex Primitives/Create Button - Параметры окна зависят от состава выбранного примитива: секция показывается, только если в нём есть нужная часть
- Настройки модулей на странице
Project Settings → Vortex/UIBuilder, файл внеAssets— в сборку не попадает - Одна запись Undo на всё создание; ошибка сборки откатывает созданное
- Расширение новыми видами элементов и секциями без правки ядра
Вне ответственности:
- Сами примитивы и их структура (проектные префабы)
- Runtime-поведение
UIComponentиSet*Component(UIComponents,Components)
Зависимости
| Зависимость | Назначение |
|---|---|
ru.vortex.unity.ui.misc |
UIComponent и его части (UIComponentText/Graphic/Button) |
ui.vortex.unity.components |
SetTextComponent, SetSpriteComponent, SetActionComponent |
ru.vortex.unity.localization |
[LocalizationKey] — селектор ключа в секции локали |
ru.vortex.unity.editortools |
SearchablePopup — выпадающий список примитивов |
| Odin Inspector | Отрисовка настроек и секций (PropertyTree, [FolderPath], [ValueDropdown]) |
Использование
Project Settings → Vortex/UIBuilder— указать папку примитивов для каждого модуля, при желании префаб, имя и размер по умолчанию.- ПКМ по объекту в Hierarchy →
Vortex Primitives/Create TextилиCreate Button. - В окне выбрать примитив, имя слоя, заполнить секции → «Создать».
Архитектура
UIBuilder/
├── UIBuilderController.cs # Ядро: поиск модулей, пункт меню → окно, сборка слоя
├── UIBuilderSettings.cs # ScriptableSingleton в ProjectSettings/, синхронизация с модулями
├── UIBuilderSettingsProvider.cs # Страница Project Settings → Vortex/UIBuilder
├── UIBuilderCreateWindow.cs # Окно параметров создания
├── UIBuilderCatalog.cs # Каталог префабов папки (с подпапками)
├── Shortcuts/ # Горячие клавиши вёрстки (общий код — EditorTools/HierarchyTools)
│ ├── BackgroundLayerShortcut.cs # Alt+I — слой Background с Image
│ ├── TweenerHubShortcut.cs # Alt+T / Ctrl+Alt+T — TweenerHub на объект / слоем
│ └── UIStateSwitcherShortcut.cs # Alt+S / Ctrl+Alt+S — UIStateSwitcher на объект / слоем
├── Base/
│ ├── UIBuilderModule.cs # Модуль вида элемента (+ generic UIBuilderModule<TSettings>)
│ ├── UIBuilderModuleSettings.cs # Общие настройки модуля
│ └── UIBuilderSection.cs # Секция параметров создания
├── Modules/
│ ├── TextModule.cs # Text + TextModuleSettings
│ └── ButtonModule.cs # Button + ButtonModuleSettings
└── Sections/
├── LocaleSection.cs # UIComponentText → SetTextComponent
├── IconSection.cs # UIComponentGraphic → SetSpriteComponent
└── ActionSection.cs # UIComponentButton → SetActionComponent
Модуль (UIBuilderModule)
Вид элемента: свои настройки (SettingsType), название (Title), набор секций (CreateSections()) и шаг сборки (Apply). Состояния нет, нужен конструктор без параметров. Ядро находит неабстрактных наследников через TypeCache. Пункт меню модуль объявляет сам — статическим [MenuItem], который вызывает UIBuilderController.Open<TModule>.
| Модуль | Меню | Секции | Имя слоя по умолчанию |
|---|---|---|---|
TextModule |
Vortex Primitives/Create Text |
Локаль | Text |
ButtonModule |
Vortex Primitives/Create Button |
Локаль, Иконка, Действие | Button |
Настройки (UIBuilderSettings, UIBuilderModuleSettings)
UIBuilderSettings — ScriptableSingleton, файл ProjectSettings/VortexUIBuilderSettings.asset: вне Assets, в сборку не попадает, хранится в VCS. Содержит по одному UIBuilderModuleSettings на каждый модуль ([SerializeReference]).
Общие поля настроек модуля:
| Поле | Тип | Описание |
|---|---|---|
folder |
string ([FolderPath]) |
Папка примитивов, сканируется с подпапками. Пусто — модуль не настроен |
defaultPrefab |
GameObject ([ValueDropdown]) |
Примитив, выбранный при открытии окна. Выбирается из каталога папки |
defaultLayerName |
string |
Имя создаваемого слоя. Значение по умолчанию задаёт наследник через конструктор |
defaultSize |
Vector2 |
Размер создаваемого слоя (sizeDelta), по умолчанию 240 × 80 |
Кроме настроек модулей, на странице есть блок Background Layer (Alt+I): backgroundColor — цвет Image слоя, создаваемого Add BackgroundLayer, по умолчанию чёрный.
Синхронизация (Sync) — при открытии страницы настроек и при обращении к настройкам отсутствующего модуля:
- добавляет настройки для модулей, у которых их нет;
- удаляет записи, чей класс не найден, настройки модулей, которых больше нет, и дубликаты (остаётся первый);
- каждое удаление пишется в лог, изменения сохраняются на диск.
Секция (UIBuilderSection)
Общий кусок параметров создания, привязанный к типу части UIComponent (PartType). Поля секции ([SerializeField]) рисует Odin. Экземпляры создаются заново при каждом выборе примитива и не сохраняются.
| Секция | Часть | Поля | Добавляет |
|---|---|---|---|
LocaleSection |
UIComponentText |
localeKey ([LocalizationKey]), useLocalization = false |
SetTextComponent с ключом, если ключ задан |
IconSection |
UIComponentGraphic |
icon |
SetSpriteComponent со спрайтом, если спрайт задан |
ActionSection |
UIComponentButton |
addAction = true |
SetActionComponent, если галочка стоит |
AddLinked<T> добавляет компонент и явно проставляет uiComponent и position = -1 (все части типа); собственные поля дописываются через SerializedObject.
Сборка слоя
<активный выделенный объект>
└── <имя слоя> RectTransform (sizeDelta = defaultSize), UIComponent, Set*Component
└── <экземпляр примитива> связь с префабом, без изменений
- Группа Undo.
- Слой создаётся неактивным —
SetTextComponent([ExecuteInEditMode]) не должен включиться без ссылки наUIComponent. - Экземпляр примитива через
PrefabUtility.InstantiatePrefab. - Сбор частей — приватный editor-метод
UIComponent.Initчерез рефлексию (пакетUIComponentsне меняется). module.Apply: секции, у которых после сбора есть части нужного типа.- Слой активируется, группа Undo сворачивается, созданный слой выделяется.
Горячая клавиша Add BackgroundLayer
Alt+I (Tools/Vortex/UI/Add BackgroundLayer) — в каждый выделенный объект сцены или открытого префаба добавляется дочерний слой Background с Image:
- цвет —
backgroundColorиз настроек (по умолчанию чёрный); Maskable выключен, Raycast Target включён; - слой первым в иерархии (рисуется под остальными детьми),
RectTransformрастянут по родителю; - повторное нажатие — ещё один слой; новые слои выделяются; отмена — одним шагом Undo;
- ассеты в Project window не затрагиваются. У родителя без
RectTransformрастягивать не по чему.
Здесь же живут горячие клавиши TweenerHub (Alt+T / Ctrl+Alt+T) и UIStateSwitcher (Alt+S / Ctrl+Alt+S) — описаны в README TweenerSystem и StateSwitcher. Выбор целей, группировка Undo и создание слоёв у всех команд — общий код EditorTools/HierarchyTools/HierarchyLayers.cs.
Расширение
Новый вид элемента — три класса в любой editor-сборке, ссылающейся на ru.vortex.unity.ui.uibuilder:
[Serializable]
public sealed class ImageModuleSettings : UIBuilderModuleSettings
{
public ImageModuleSettings() : base("Image") { }
}
public sealed class ImageModule : UIBuilderModule<ImageModuleSettings>
{
private const string MenuPath = "GameObject/Vortex Primitives/Create Image";
public override string Title => "Image";
public override IEnumerable<UIBuilderSection> CreateSections()
{
yield return new IconSection();
}
[MenuItem(MenuPath, true)]
private static bool OpenValidate() => UIBuilderController.CanOpen();
[MenuItem(MenuPath, false, 3)]
private static void Open(MenuCommand command) => UIBuilderController.Open<ImageModule>(command);
}
Своя секция — наследник UIBuilderSection с PartType, Title и Apply; модуль перечисляет её в CreateSections(). Нестандартная сборка — переопределить UIBuilderModule.Apply.
Граничные случаи
| Ситуация | Поведение |
|---|---|
folder пуст |
Пункт меню открывает страницу Vortex/UIBuilder, предупреждение в лог |
folder не существует |
Окно: «Папка не найдена», создание недоступно; defaultPrefab — пустой список |
| В папке нет префабов | Окно: «В папке нет префабов», создание недоступно |
defaultPrefab пуст или вне каталога |
Примитив не выбран, «Создать» недоступно до выбора |
| Нет активного выделенного объекта | Пункт меню недоступен |
| Выделено несколько объектов | Создание только под активным, повторные вызовы контекстного меню отсекаются |
Родитель без RectTransform |
Слой создаётся, предупреждение в лог |
Секция показана, но части ушли во вложенный UIComponent примитива |
Секция пропускается, предупреждение в лог |
UIComponent.Init не найден (переименован) |
MissingMethodException в лог, созданное откатывается |
У Set*Component не найдено поле |
InvalidOperationException с именем поля, созданное откатывается |
| Перезагрузка домена при открытом окне | Окно закрывается |
| Модуль удалён из кода | Его настройки удаляются при следующей синхронизации, запись в лог |
| Поле, добавленное в настройки после сохранения файла | Может загрузиться значением по умолчанию типа (например, defaultSize = 0 × 0) — выставить вручную |