En esta segunda parte dedicada a Scene2D UI en libGDX, profundizamos en cómo manejar eventos y explorar una amplia variedad de widgets para construir interfaces gráficas interactivas y dinámicas para nuestros juegos.
Comenzamos entendiendo que los elementos de la interfaz, como botones y otros widgets, son actores que añadimos a un Stage. Para gestionar la interactividad, en lugar de usar un ClickListener tradicional, utilizamos la clase ChangeListener, diseñada específicamente para interfaces gráficas. Este listener se activa automáticamente en eventos como pulsar un botón o mover un slider, simplificando la gestión de eventos.
Por ejemplo, podemos crear un botón de texto (TextButton) que cambie su etiqueta cuando lo pulsamos. Es importante recordar que si referenciamos variables externas dentro de un listener anónimo, estas deben ser declaradas como final. Así, al ejecutar el botón, al hacer clic, el texto cambia mostrando un mensaje de agradecimiento.
Para ilustrar un caso más avanzado, podemos crear una pantalla de inicio con varios botones: New Game, Load Game, Settings y Exit. Estos botones se organizan en una tabla con espacio suficiente para que sean grandes y cómodos. Al pulsar New Game, podemos animar la tabla para que se desplace hacia arriba y se desvanezca, simulando una transición antes de cambiar de pantalla. Utilizamos acciones combinadas con Actions.parallel para mover y hacer fade out simultáneamente, y Actions.sequence junto con Actions.delay para escalonar la animación de cada botón, logrando un efecto visual atractivo.
Pasando a los widgets, empezamos con el clásico Label, que simplemente muestra texto en pantalla. Luego, el TextButton es un botón que incluye un label internamente, permitiendo mostrar texto y responder a eventos con listeners.
Para mostrar imágenes, usamos el widget Image, que puede contener texturas simples, y para botones con imágenes, ImageButton. Este último puede construirse con un Drawable, como un TextureRegionDrawable, que convierte una textura en un drawable para el botón. Sin embargo, si solo usamos un drawable, el botón no mostrará bordes ni estilos típicos. Para solucionarlo, podemos crear un ImageButtonStyle personalizado basado en el estilo de botón de la skin, asignándole el drawable de la imagen. Así, el botón tendrá la apariencia y comportamiento esperado, con bordes y colores, pero mostrando la imagen deseada.
En cuanto a widgets para entrada de texto, TextField permite introducir una sola línea de texto, con soporte para selección y portapapeles (aunque en Android el portapapeles puede no funcionar del todo). Es importante asegurarse de importar la clase correcta de libGDX para evitar conflictos con otras librerías de Java. Para textos multilínea, usamos TextArea, que permite escribir varias líneas y desplazarse dentro del área.
El widget CheckBox nos permite añadir casillas de verificación con texto asociado. Podemos detectar cambios en su estado para activar o desactivar funcionalidades según si está marcado o no.
Para selección de opciones, SelectBox ofrece un cuadro desplegable donde elegir un elemento de una lista. Podemos añadir elementos con setItems y ajustar su ancho para que se vea correctamente. Similarmente, las List muestran siempre los elementos desplegados, pero para que funcionen correctamente deben estar dentro de un ScrollPane, que añade barras de desplazamiento para manejar listas largas.
El Slider es un widget para seleccionar valores dentro de un rango, mostrando una barra que podemos arrastrar. Podemos añadir listeners para actualizar otros widgets, como un TextField, con el valor seleccionado. La ProgressBar es similar al slider pero no permite interacción directa; su valor se actualiza programáticamente, por ejemplo, para mostrar progreso en tiempo real. Ambos pueden orientarse horizontal o verticalmente.
Para ventanas dentro del juego, Window permite crear paneles con título y contenido, que pueden moverse y cerrarse, pero no bloquean la interacción con lo que hay detrás. Para diálogos modales que bloquean la interfaz hasta cerrarse, usamos Dialog, que facilita añadir texto y botones, y se muestra con animaciones mediante el método show.
Finalmente, exploramos distintos layouts para organizar widgets. La Table es la más común, permitiendo organizar elementos en filas y columnas. El Container es un layout ligero que contiene un solo actor, útil para aplicar transformaciones como rotaciones o escalados, que están deshabilitadas por defecto para optimizar rendimiento. Activando transformaciones con setTransform(true), podemos rotar un widget y la interacción se ajusta correctamente al nuevo área visible.
El SplitPane divide el espacio en dos áreas ajustables por el usuario, mostrando dos widgets lado a lado o uno encima del otro, con una barra divisoria que permite cambiar el tamaño relativo de cada parte.
El ScrollPane añade barras de desplazamiento a widgets que pueden exceder el espacio visible, como listas largas o imágenes grandes, permitiendo desplazarse vertical u horizontalmente con animaciones de rebote al llegar a los extremos.
Por último, el Tree permite crear estructuras jerárquicas de nodos expandibles y colapsables, ideales para mostrar datos organizados en niveles, como regiones y países. Los nodos pueden contener otros nodos, y el árbol puede integrarse dentro de otros layouts para interfaces complejas.
Existen otros layouts como Stack o grupos verticales y horizontales, pero no se profundizó en ellos por falta de documentación o relevancia para este caso.
Con todo esto, disponemos de un conjunto muy completo para construir interfaces gráficas ricas y funcionales en libGDX usando Scene2D UI, combinando eventos, widgets variados y layouts flexibles para adaptarnos a las necesidades de nuestros juegos.