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

Обзор системы UI

Слой UI OpenMon находится в Assets/OpenMon/Core/Runtime/UI/ и полностью построен на UGUI Unity (Canvas + RectTransform). Каждый экран, меню и диалог используют два основополагающих строительных блока: MenuSelector для навигационных списков/сеток и DialogManager для модальных текстовых окон, выбора и сложных всплывающих окон.

Архитектура с высоты птичьего полёта​

UI/
├── MenuSelector.cs # Базовый класс для ВСЕХ навигационных меню
├── ScrollableMenuSelector.cs # MenuSelector + поддержка ScrollRect
├── VirtualizedMenuSelector.cs # MenuSelector + перерабатываемые/пулированные элементы
├── MenuItem.cs # Отдельная кнопка внутри MenuSelector
├── VirtualizedMenuItem.cs # MenuItem, знающий свою строку в пуле
├── UIElementsInstaller.cs # Установщик Zenject для фабрик UI
├── Battle/ # HUD битвы: главное меню, меню атак, выбор цели
├── Bags/ # Экран инвентаря/сумки с вкладками
├── Dex/ # Полноэкранный режим покедекса с вкладками
├── Dialogs/ # DialogManager + меню выбора, ввод текста, всплывающие окна
├── GameMenu/ # Меню паузы в игре (Dex, Mons, Сумка, Карта и т.д.)
├── MainMenu/ # Экран заголовка: Новая игра, Продолжить, Авторы
├── Monsters/ # Экран состава монстров, сводка, хранилище
├── Options/ # Экран настроек (громкость, разрешение, язык и т.д.)
├── Shops/ # Интерфейс магазина (покупка/продажа)
├── Quests/ # Экран журнала заданий
├── Profile/ # Профиль игрока / карточка тренера
├── Map/ # Наложение карты мира
└── ...

Две основы​

MenuSelector — это базовый класс, от которого наследуется почти каждое интерактивное меню. Он обрабатывает:

  • Вертикальную или горизонтальную навигацию через перечисление Navigation.
  • Стрелку селектора, которая анимируется к указанному элементу через встроенный в MCE API твинов.
  • Звуковую обратную связь при навигации и выборе.
  • Интеграцию ввода через интерфейс IInputReceiver и IInputManager.
  • Зацикливание — переход за последний элемент возвращает к первому и наоборот.
  • Удержание для прокрутки — когда CanHoldToScrollFaster включён, удержание ввода повторяет навигацию с настраиваемым интервалом.

Существует две специализированные разновидности:

КлассСценарий использования
ScrollableMenuSelectorДобавляет ScrollRect, который автоматически прокручивается, чтобы выбранный элемент оставался видимым.
VirtualizedMenuSelector<TData, TButton, TFactory>Список с пулом объектов для больших наборов данных (например, Dex, Хранилище). Перерабатывает экземпляры кнопок по мере прокрутки игроком.

См. страницу Меню для подробного изучения MenuSelector.

DialogManager — Модальные диалоги и сложные всплывающие окна​

DialogManager — это Singleton, владеющий всеми модальными наложениями UI в игре. Он управляет:

  • Базовыми текстовыми диалогами с анимацией пишущей машинки, панелями имени говорящего и локализацией.
  • Меню выбора (Да/Нет, несколько вариантов до 11).
  • Полноэкранными экранами, доступными через статические методы: Dex, Сумка, Состав, Магазин, Настройки, Профиль, Задания, Карта.
  • Специализированными всплывающими окнами: панель получения опыта, диалог замены атаки, учитель атак, всплывающее окно нового монстра, диалог ввода текста.
  • Уведомлениями через дочерний NotificationManager.

DialogManager создаётся через фабричный паттерн Zenject:

// DialogManagerInstaller.cs
Container.BindFactory<DialogManager, DialogManager.Factory>()
.FromComponentInNewPrefab(Prefab);

См. страницу Диалоги для полного API.

Внедрение зависимостей​

Все фабрики UI регистрируются через два Zenject ScriptableObjectInstaller:

  • DialogManagerInstaller — Привязывает DialogManager.Factory из префаба.
  • UIElementsInstaller — Привязывает фабрики для кнопок предметов, кнопок монстров, кнопок dex, кнопок атак, кнопок заданий, кнопок сохранений и других. Каждая привязка ограничена .WhenInjectedInto<T>(), чтобы использовался правильный вариант префаба для каждого экрана.
// Пример из UIElementsInstaller
Container.BindFactory<ItemButton, ItemButton.Factory>()
.FromComponentInNewPrefab(BagItemButtonPrefab)
.AsCached()
.WhenInjectedInto<BagTab>()
.Lazy();

Выделенные контроллеры экранов​

Каждый основной экран расширяет MenuSelector (для навигации по сетке/списку), VirtualizedMenuSelector (для больших пулированных списков) или HidableUiElement (для пользовательских макетов). Все запускаются через DialogManager:

ЭкранКонтроллерБазовый классЗапускается через
Меню игрыGameMenuScreenMenuSelectorDialogManager.ShowGameMenu(...)
Состав монстровMonstersMenuScreenHidableUiElementDialogManager.ShowPlayerRosterMenu(...)
Сумка / ИнвентарьBagScreenHidableUiElementDialogManager.ShowBag(...)
ПокедексDexScreenVirtualizedMenuSelectorDialogManager.ShowDexScreen(...)
Одиночный покедексSingleMonsterDexScreenHidableUiElementDialogManager.ShowSingleMonsterDexScreen(...)
МагазинShopDialogHidableUiElementDialogManager.ShowShop(...)
НастройкиOptionsScreenMenuSelectorDialogManager.ShowOptionsMenu()
ПрофильProfileScreenHidableUiElementDialogManager.ShowProfileScreen(...)
ЗаданияQuestsScreenVirtualizedMenuSelectorDialogManager.ShowQuestsScreen(...)
Ввод текстаTextInputDialogHidableUiElementDialogManager.RequestTextInput(...)
Учитель атакMoveTutorDialogHidableUiElementDialogManager.ShowMoveTutorDialog(...)

UI битвы​

HUD битвы имеет собственный набор подклассов MenuSelector, которые НЕ управляются DialogManager:

КонтроллерБазовый классНазначение
MainBattleMenuMenuSelectorВыбор Атака / Сумка / Монстр / Бегство
BattleMoveMenuSelectorMenuSelectorВыбор атаки во время битвы
TargetMonstersMenuSelectorMenuSelectorВыбор цели в многопользовательских битвах
BattleMonstersMenuHidableUiElementСмена монстра во время битвы
BattleInfoPanelHidableUiElement<MoveInfoPanel>Детальная накладка состояния битвы
LastBallMenuMenuSelectorБыстрый бросок последнего использованного мяча

Интеграция системы ввода​

Все меню реализуют IInputReceiver, который расширяет интерфейсы действий новой системы ввода Unity:

public interface IInputReceiver : MCEInputActions.IMainActions,
MCEInputActions.IUIActions,
MCEInputActions.ITextInputActions
{
InputType GetInputType();
void OnStateEnter();
void OnStateExit();
string GetDebugName();
}

IInputManager поддерживает стек получателей. Когда меню вызывает RequestInput(), оно помещает себя в стек и становится активным получателем. Вызов ReleaseInput() извлекает его. Это естественным образом обрабатывает вложенные меню — открытие Сумки из Игрового меню помещает получатель ввода Сумки сверху, а закрытие восстанавливает ввод для Игрового меню.

Лучшие практики для пользовательских экранов UI​

  1. Наследуйте от MenuSelector (или ScrollableMenuSelector / VirtualizedMenuSelector для больших списков).
  2. Добавьте компоненты MenuItem к каждой кнопке в вашем меню. Установите трансформ ArrowSelectorPosition для стрелки селектора.
  3. Зарегистрируйте фабрики в UIElementsInstaller, если ваш экран использует пулированные/виртуализированные элементы.
  4. Запускайте из DialogManager, добавив статический метод и сериализованную ссылку, следуя существующему шаблону.
  5. Используйте [SerializeField] private для всех ссылок инспектора — никогда не делайте поля public, если они не являются частью API.
  6. Используйте ключи локализации для всего текста, видимого игроком. Компоненты LocalizedTextMeshPro и LocalizedTypeWriterTMP обрабатывают это автоматически.
  7. Предпочитайте UI Toolkit для новых инструментов редактора, но придерживайтесь UGUI для игрового UI во время выполнения, чтобы сохранить согласованность с существующей системой.