Descripción General del Sistema UI
La capa de UI de OpenMon vive en Assets/OpenMon/Core/Runtime/UI/ y está construida enteramente sobre UGUI de Unity (Canvas + RectTransform). Cada pantalla, menú y diálogo comparte dos pilares fundamentales: MenuSelector para listas/cuadrículas navegables y DialogManager para cuadros de texto modales, opciones y ventanas emergentes complejas.
Arquitectura de un Vistazo
UI/
├── MenuSelector.cs # Clase base para TODOS los menús navegables
├── ScrollableMenuSelector.cs # MenuSelector + soporte ScrollRect
├── VirtualizedMenuSelector.cs # MenuSelector + elementos reciclados/pooled
├── MenuItem.cs # Botón individual dentro de un MenuSelector
├── VirtualizedMenuItem.cs # MenuItem que conoce su fila en un pool
├── UIElementsInstaller.cs # Instalador Zenject para fábricas de UI
├── Battle/ # HUD de combate: menú principal, menú de movimientos, selector de objetivo
├── Bags/ # Pantalla de inventario/mochila con pestañas
├── Dex/ # Pantalla completa estilo Pokédex con pestañas
├── Dialogs/ # DialogManager + menú de opciones, entrada de texto, popups
├── GameMenu/ # Menú de pausa en juego (Dex, Mons, Mochila, Mapa, etc.)
├── MainMenu/ # Pantalla de título: Nueva Partida, Continuar, Créditos
├── Monsters/ # Menú de roster de monstruos, resumen, almacenamiento
├── Options/ # Pantalla de configuración (volumen, resolución, idioma, etc.)
├── Shops/ # Interfaz de tienda comprar/vender
├── Quests/ # Pantalla de registro de misiones
├── Profile/ # Perfil del jugador / tarjeta de entrenador
├── Map/ # Superposición del mapa del mundo
└── ...
Los Dos Pilares
MenuSelector — Navegación Genérica de Menús
MenuSelector es la clase base de la que hereda casi todo menú interactivo. Maneja:
- Navegación Vertical u Horizontal mediante el enum
Navigation. - Flecha de selección que se anima hacia el elemento señalado, mediante la API de tweening incluida en MCE.
- Retroalimentación de audio al navegar y seleccionar.
- Integración de entrada a través de la interfaz
IInputReceiveryIInputManager. - Wrap-around — navegar más allá del último elemento vuelve al primero y viceversa.
- Hold-to-scroll — cuando
CanHoldToScrollFasterestá habilitado, mantener la entrada repite la navegación a un intervalo configurable.
Existen dos variantes especializadas:
| Clase | Caso de Uso |
|---|---|
ScrollableMenuSelector | Agrega un ScrollRect que se desplaza automáticamente para mantener visible el elemento seleccionado. |
VirtualizedMenuSelector<TData, TButton, TFactory> | Lista con object pooling para conjuntos de datos grandes (ej. Dex, Almacenamiento). Recicla instancias de botones mientras el jugador se desplaza. |
Ver la página Menús para una profundización en MenuSelector.
DialogManager — Diálogos Modales y Ventanas Emergentes Complejas
DialogManager es un Singleton que posee cada superposición modal de UI en el juego. Gestiona:
- Diálogos de texto básicos con animación de máquina de escribir, paneles de nombre del hablante y localización.
- Menús de opciones (Sí/No, múltiples opciones hasta 11).
- Pantallas completas accesibles mediante métodos estáticos: Dex, Mochila, Roster, Tienda, Opciones, Perfil, Misiones, Mapa.
- Ventanas emergentes especializadas: panel de ganancia de XP, diálogo de reemplazo de movimiento, tutor de movimientos, popup de nuevo monstruo, diálogo de entrada de texto.
- Notificaciones a través de un
NotificationManagerhijo.
DialogManager se instancia mediante el patrón de fábrica de Zenject:
// DialogManagerInstaller.cs
Container.BindFactory<DialogManager, DialogManager.Factory>()
.FromComponentInNewPrefab(Prefab);
Ver la página Diálogos para la API completa.
Inyección de Dependencias
Todas las fábricas de UI se registran a través de dos ScriptableObjectInstallers de Zenject:
DialogManagerInstaller— VinculaDialogManager.Factorydesde un prefab.UIElementsInstaller— Vincula fábricas para botones de objeto, botones de monstruo, botones de dex, botones de movimiento, botones de misión, botones de partida guardada y más. Cada vinculación tiene alcance con.WhenInjectedInto<T>()para que se use la variante de prefab correcta por pantalla.
// Ejemplo de UIElementsInstaller
Container.BindFactory<ItemButton, ItemButton.Factory>()
.FromComponentInNewPrefab(BagItemButtonPrefab)
.AsCached()
.WhenInjectedInto<BagTab>()
.Lazy();
Controladores de Pantalla Dedicados
Cada pantalla principal extiende MenuSelector (para navegación de cuadrícula/lista), VirtualizedMenuSelector (para listas grandes con pool) o HidableUiElement (para diseños personalizados). Todas se lanzan a través de DialogManager:
| Pantalla | Controlador | Clase Base | Lanzada Mediante |
|---|---|---|---|
| Menú de Juego | GameMenuScreen | MenuSelector | DialogManager.ShowGameMenu(...) |
| Roster de Monstruos | MonstersMenuScreen | HidableUiElement | DialogManager.ShowPlayerRosterMenu(...) |
| Mochila / Inventario | BagScreen | HidableUiElement | DialogManager.ShowBag(...) |
| Dex | DexScreen | VirtualizedMenuSelector | DialogManager.ShowDexScreen(...) |
| Dex de un Monstruo | SingleMonsterDexScreen | HidableUiElement | DialogManager.ShowSingleMonsterDexScreen(...) |
| Tienda | ShopDialog | HidableUiElement | DialogManager.ShowShop(...) |
| Opciones | OptionsScreen | MenuSelector | DialogManager.ShowOptionsMenu() |
| Perfil | ProfileScreen | HidableUiElement | DialogManager.ShowProfileScreen(...) |
| Misiones | QuestsScreen | VirtualizedMenuSelector | DialogManager.ShowQuestsScreen(...) |
| Entrada de Texto | TextInputDialog | HidableUiElement | DialogManager.RequestTextInput(...) |
| Tutor de Movimientos | MoveTutorDialog | HidableUiElement | DialogManager.ShowMoveTutorDialog(...) |
UI de Combate
El HUD de combate tiene su propio conjunto de subclases de MenuSelector que NO son gestionadas por DialogManager:
| Controlador | Clase Base | Propósito |
|---|---|---|
MainBattleMenu | MenuSelector | Selección de Atacar / Mochila / Monstruo / Huir |
BattleMoveMenuSelector | MenuSelector | Selección de movimiento durante el combate |
TargetMonstersMenuSelector | MenuSelector | Selección de objetivo en combates múltiples |
BattleMonstersMenu | HidableUiElement | Cambiar monstruo durante el combate |
BattleInfoPanel | HidableUiElement<MoveInfoPanel> | Superposición de estado detallado de combate |
LastBallMenu | MenuSelector | Lanzamiento rápido de la última bola usada |
Integración del Sistema de Entrada
Todos los menús implementan IInputReceiver, que extiende las interfaces de acción del Nuevo Sistema de Entrada de Unity:
public interface IInputReceiver : MCEInputActions.IMainActions,
MCEInputActions.IUIActions,
MCEInputActions.ITextInputActions
{
InputType GetInputType();
void OnStateEnter();
void OnStateExit();
string GetDebugName();
}
El IInputManager mantiene una pila de receptores. Cuando un menú llama a RequestInput(), se coloca a sí mismo en la pila y se convierte en el receptor activo. Llamar a ReleaseInput() lo saca de la pila. Esto maneja naturalmente los menús anidados — abrir la Mochila desde el Menú de Juego coloca el receptor de entrada de la Mochila encima, y al cerrarlo se restaura la entrada al Menú de Juego.
Buenas Prácticas para Pantallas UI Personalizadas
- Heredar de
MenuSelector(oScrollableMenuSelector/VirtualizedMenuSelectorpara listas grandes). - Agregar componentes
MenuItema cada botón en tu menú. Establecer el transformArrowSelectorPositionpara la flecha de selección. - Registrar fábricas en
UIElementsInstallersi tu pantalla usa elementos pooled/virtualizados. - Lanzar desde
DialogManageragregando un método estático y referencia serializada, siguiendo el patrón existente. - Usar
[SerializeField] privatepara todas las referencias del inspector — nunca exponer campos comopublica menos que sean parte de la API. - Usar claves de localización para todo el texto visible al jugador. Los componentes
LocalizedTextMeshProyLocalizedTypeWriterTMPmanejan esto automáticamente. - Preferir UI Toolkit para nuevas herramientas de editor, pero mantener UGUI para la UI runtime del juego para mantener consistencia con el sistema existente.