Un HUD (Heads-Up Display) en tu juego comúnmente muestra elementos de UI no interactivos, como el medidor de salud demostrado en Crear medidores HUD. Además de esto, casi todos los juegos requieren UI interactiva como botones que responden a la activación del jugador, animan cuando se activan, hacen tween de menús con otros controles interactivos, etc.

Usando Fundamentos de UI - Medidor HUD como punto de partida y Fundamentos de UI - Interactividad como referencia final, este tutorial demuestra:
- Cómo posicionar un botón de configuración a lo largo del borde superior de la pantalla.
- Diseño de un menú de configuración que contiene deslizadores interactivos arrastrables.
- Uso de un ModuleScripts para formar módulos de "controlador" para el control extensible de objetos de UI con declaraciones de estado.
- Cómo conectar botones a la activación del jugador para alternar el menú de configuración.
- Cómo conectar deslizadores de UI arrastrables para ajustar el volumen de los efectos de sonido y la ambientación de fondo por separado.
Habilitar el Emulador de Dispositivos
Como se menciona en Crear medidores HUD, los teléfonos y tabletas tienen la menor cantidad de espacio en pantalla, por lo que es importante que tus elementos de UI se ajusten a pantallas más pequeñas y que sean claramente visibles para los jugadores. Si aún no lo has hecho, habilita el Emulador de Dispositivos en Studio:
Abre la plantilla Fundamentos de UI - Medidor HUD en Studio.
Desde el menú Prueba de Studio, activa el Emulador de Dispositivos.
Desde la barra directamente encima de la vista principal, selecciona una emulación de teléfono como iPhone X o Samsung Galaxy A51. Luego, establece el tamaño de vista en Ajustar a la Ventana para utilizar el máximo espacio en Studio.

Crear el botón de configuración
GuiButtons son elementos de interfaz de usuario interactivos con funcionalidad incorporada, como el evento multiplataforma Activated que se activa cuando se hace clic o se toca el botón. La clase GuiButton se extiende a dos variaciones, TextButton y ImageButton, y este tutorial utiliza un ImageButton en forma de "engranaje" para alternar el menú de configuración abierto o cerrado.

Para construir el botón de configuración:
En la ventana Explorador, localiza HUDContainer dentro de StarterGui.

Con el nuevo botón seleccionado, establece lo siguiente en la ventana Propiedades:
- AnchorPoint = 0.5, 0.25 (centro horizontal y cuarto superior vertical)
- BackgroundTransparency = 1 (totalmente transparente)
- Position = 0.5, 0, 0, 0 (posicionamiento en el centro superior)
- Size = 0.1, 0, 0.1, 0 (10% de la altura de la pantalla una vez que se restringe la relación de aspecto)
- Image = rbxassetid://104919049969988 (ID de activo del símbolo de "engranaje")
Inserta un UIAspectRatioConstraint en el botón con propiedades predeterminadas (relación de aspecto 1:1).

Para limitar la altura máxima en píxeles del botón, inserta un UISizeConstraint y establece su MaxSize en inf, 44.



Crear el menú de configuración
El nuevo botón de configuración será programado para alternar un menú de configuración abierto y cerrado, dando a los jugadores acceso a la configuración o a otra información bajo demanda. En este tutorial, el menú contendrá deslizadores interactivos para ajustar los niveles de volumen del audio de fondo y de los efectos de sonido de forma independiente.

Crear el marco padre
Como se menciona en Crear medidores HUD, un Frame sirve como contenedor para otros objetos de UI. Todo el menú de configuración se construirá con un solo marco padre, lo que facilita su gestión como un objeto con estado que reacciona a la entrada de manera diferente dependiendo del estado actual.
Inserta un Frame en HUDContainer y renómbralo SettingsMenu.

Con SettingsMenu seleccionado, establece las siguientes propiedades:
- AnchorPoint = 0.5 (ancla central)
- BackgroundColor3 = 30, 30, 60 (azul marino)
- BackgroundTransparency = 0.25 (75% opaco)
- Position = 0.5, 0, 0.5, 0 (centro de la pantalla)
- Size = 0.75, 0, 0.75, 0 (75% del ancho/alto del área de pantalla interna)

Inserta un UIAspectRatioConstraint en SettingsMenu y establece su AspectRatio en 2.5. (2.5:1 ancho a alto).


Inserta un modificador UICorner en SettingsMenu y establece su CornerRadius en 0.1, 0.


Para restringir el ancho y la altura en píxeles de SettingsMenu, inserta un UISizeConstraint. Establece su MaxSize en 800, inf y su MinSize en 350, 0.



Construir un deslizador
Para permitir que los jugadores ajusten los niveles de volumen, el menú de configuración contendrá dos widgets deslizadores arrastrables impulsados por UIDragDetector, un objeto conveniente que facilita la interacción con elementos de interfaz de usuario 2D.
Para crear un contenedor padre para el primer deslizador:
Inserta un nuevo Frame en el contenedor SettingsMenu y renómbralo EffectsVolumeSlider.

Con el marco EffectsVolumeSlider seleccionado, establece las siguientes propiedades:
AnchorPoint = 0.5 (ancla central)
BackgroundTransparency = 1 (totalmente transparente)
Position = 0.5, 0, 0.35, 0 (centro horizontal y 35% desde la parte superior del contenedor)
Size = 0.8, 0, 0.1, 0 (80% del ancho del contenedor y 10% de su altura)

Inserta un nuevo UIListLayout en EffectsVolumeSlider. Este modificador de diseño es una forma poderosa de organizar automáticamente los GuiObjects hermanos en filas horizontales o columnas verticales dentro de su contenedor padre, incluyendo la capacidad de aplicar conceptos de flex.

Establece las siguientes propiedades para el nuevo UIListLayout:
- Padding = 0.06, 0 (6% de relleno entre los elementos de la lista)
- FillDirection = Horizontal (organización de elementos de izquierda a derecha)
- HorizontalFlex = Fill (redimensionar elementos horizontalmente para llenar todo el contenedor padre, sobrescribiendo su ancho definido)
- VerticalAlignment = Center (alinear elementos a lo largo de su centro vertical)
Icono del deslizador
Un icono simple que incluye una nota de audio y un símbolo de "explosión" ayuda a los jugadores a identificar el propósito del deslizador cuando abren el menú de configuración.

Inserta un ImageLabel en el marco EffectsVolumeSlider y renómbralo a Icon. Este icono ayudará a los jugadores a entender qué aspecto del juego controla el deslizador.

Con Icon seleccionado, establece las siguientes propiedades:
- BackgroundTransparency = 1 (totalmente transparente)
- Size = 2.5, 0, 2.5, 0 (250% de la altura del marco padre una vez que se restringe la relación de aspecto)
- Image = rbxassetid://90019827067389 (ID de activo de la nota de audio y símbolo de "explosión")
Inserta un UIAspectRatioConstraint en Icon y deja sus propiedades como los valores predeterminados (1:1 ancho a alto).


Marco de rango
Directamente a la derecha del icono, la porción interactiva del deslizador debe estar contenida dentro de otro Frame.

Inserta un nuevo Frame en el marco EffectsVolumeSlider y renómbralo SliderFrame. Nota que debe ser un hermano directo del modificador de diseño UIListLayout.

Con SliderFrame seleccionado, establece las siguientes propiedades:
- BackgroundColor3 = 0 (negro)
- BackgroundTransparency = 0.75 (25% opaco)
- LayoutOrder = 1
- Size = 1, 0, 1, 0 (100% del ancho/alto del marco padre)
Inserta los siguientes modificadores en SliderFrame:
- UIStroke con su Color establecida en 0, Thickness en 3, y Transparency en 0.25 (75% de contorno negro opaco)

Mango interactivo
Con el contenedor del deslizador construido, ahora puedes crear un mango arrastrable para que los jugadores interactúen durante el juego.

Inserta un nuevo Frame en el contenedor SliderFrame y renómbralo a Handle.

Con Handle seleccionado, establece las siguientes propiedades:
- AnchorPoint = 0.5 (ancla central)
- Position = 0.5, 0, 0.5, 0 (centro horizontal y vertical del marco padre)
- Size = 1.2, 0, 1.2, 0 (120% de la altura del marco padre una vez que se restringe la relación de aspecto)
- ZIndex = 3 (capa del mango visualmente frente a otros elementos del deslizador)
Inserta los siguientes modificadores en Handle:
- UIAspectRatioConstraint con sus propiedades predeterminadas (1:1 ancho a alto)
Inserta un objeto UIDragDetector en Handle. Este objeto conveniente facilita y fomenta la interacción con elementos de interfaz de usuario 2D.

Establece las siguientes propiedades para el nuevo UIDragDetector:
- ResponseStyle = Scale (moverse por valores de escala de la posición del padre del detector)
Para asegurarte de que el rango de arrastre lineal del mango esté limitado a su contenedor, vincula su propiedad BoundingUI al contenedor SliderFrame:
- Selecciona el UIDragDetector.
- Haz clic en su propiedad BoundingUI en la ventana Propiedades.
- De vuelta en la ventana Explorador, haz clic en el padre SliderFrame del mango.

La propiedad BoundingUI ahora refleja el contenedor SliderFrame:

Si pruebas el juego ahora, podrás arrastrar el mango de izquierda a derecha dentro de su contenedor padre:
Relleno interno
Para indicar más claramente que el deslizador controla un rango del 0% al 100%, puedes agregar un relleno interno al lado izquierdo del contenedor que se sincronizará con la posición variable del mango.

Inserta un nuevo Frame en el contenedor SliderFrame y renómbralo a InnerFill.

Establece las siguientes propiedades para el marco InnerFill:
- AnchorPoint = 0, 0.5 (borde izquierdo y centro vertical)
- BackgroundColor3 = [0, 150, 255] (azul que coincide con el mango del deslizador)
- BackgroundTransparency = 0.35 (65% opaco)
- Position = 0, 0, 0.5, 0
- Size = 0.5, 0, 1, 0 (50% de ancho y 100% de altura del marco padre)
- ZIndex = 2 (capa visualmente frente al relleno/contorno del marco padre)
Para coincidir con la forma de "pastilla" del contenedor padre SliderFrame, inserta un modificador UICorner y establece su propiedad CornerRadius en 0.5, 0.



Duplicar el deslizador
Con el primer deslizador construido, puedes duplicarlo fácilmente y modificar algunos aspectos visuales para indicar otro propósito, en este caso el nivel de volumen del audio de fondo simbolizado por un icono de notas musicales.

Selecciona el objeto EffectsVolumeSlider completado.

Duplicalo (CtrlD o ⌘D) y luego renombra el duplicado a BackgroundVolumeSlider.

Cambia la Position del duplicado a 0.5, 0, 0.7, 0 para moverlo debajo del primer deslizador.

Expande la rama de nivel superior de BackgroundVolumeSlider, selecciona la etiqueta de imagen Icon y cambia su propiedad Image a rbxassetid://101125859760167 (ID de activo del símbolo con notas musicales).



Selecciona el marco InnerFill dentro de SliderFrame y cambia su propiedad BackgroundColor3 al mismo color magenta ([255, 0, 125]).



Crear el botón de cerrar
El último elemento del menú de configuración es un botón de cerrar que proporciona a los jugadores una entrada adicional para cerrar el menú (el SettingsButton en el centro superior servirá para el mismo propósito).

Con CloseButton seleccionado, establece las siguientes propiedades:
- AnchorPoint = 1, 0 (esquina superior derecha)
- BackgroundTransparency = 1 (totalmente transparente)
- Position = 1, -10, 0, 10 (10 píxeles de inserción desde la esquina superior derecha)
- Size = 0.15, 0, 0.15, 0 (15% de la altura del marco una vez que se restringe la relación de aspecto)
- Image = rbxassetid://5577404210 (ID de activo del símbolo del botón de cerrar)
- ImageTransparency = 0.25 (75% opaco)
Crear los módulos de control
Una configuración de módulo de control extensible hace que la gestión de UI interactiva sea más fluida que scripts individuales colocados dentro de cada objeto. ModuleScripts facilitan esta funcionalidad extensible al permitirte reutilizar código entre scripts en diferentes lados de la frontera cliente-servidor o en el mismo lado de la frontera.
Controlador de objeto con estado
El siguiente módulo controlador de objeto con estado te permite adjuntar comportamiento a objetos de UI como SettingsButton y SettingsMenu, y alternar/tween fácilmente entre varios estados. Para crear el módulo:
Inserta un ModuleScript en el contenedor ReplicatedStorage y renómbralo a StatefulObjectController.

Pega el siguiente código dentro del módulo:
StatefulObjectControllerlocal TweenService = game:GetService("TweenService")local StatefulObjectController = {}StatefulObjectController.__index = StatefulObjectControllerexport type StateName = stringexport type State = {transition: TweenInfo,properties: { [string]: any },}function StatefulObjectController.hydrate(props: {object: Instance,states: { [StateName]: State },initialStateName: StateName})local object, states, initialStateName = props.object, props.states, props.initialStateNamelocal self = setmetatable({states = states,currentStateName = initialStateName,tweens = {},}, StatefulObjectController)-- Crear tweens para reutilizar y evitar crear nuevos tweens cada vez que se cambia el estadofor stateName, state in states doself.tweens[stateName] = TweenService:Create(object, state.transition, state.properties)endself:setState(self.currentStateName)return selfendfunction StatefulObjectController:setState(stateName: StateName)local stateTween: Tween = self.tweens[stateName]if not stateTween thenwarn(string.format("Se intentó establecer %s en un estado desconocido '%s'", self.object:GetFullName(), stateName))returnendself.currentStateName = stateName-- Asegúrate de que otros tweens no estén en conflictofor _, tween in self.tweens dotween:Cancel()endstateTween:Play()endreturn StatefulObjectControllerExplicación del CódigoLines Propósito 6‑10 Define y exporta los tipos de Luau para mejorar la autocompletación y el linting al usar este módulo. 12‑33 Función utilizada para inicializar y adjuntar comportamiento a un objeto de UI con estado, aceptando un GuiObject como object, una tabla de estados y sus respectivos datos de TweenInfo, y un nombre de estado inicial. Dentro de esta función, las líneas 26‑28 construyen Tweens para cada estado, y la línea 30 llama a la función setState() del módulo para establecer el objeto en el estado inicial. 35‑50 Función utilizada para establecer/cambiar el estado de un objeto con estado. La línea 42 establece el currentStateName del objeto en el estado que se está estableciendo, las líneas 45‑47 cancelan cualquier tween en ejecución para evitar conflictos, y la línea 49 ejecuta el/los tween(s) del estado.
Controlador de deslizador
Un módulo adicional inicializa y controla los dos deslizadores de volumen. También te permite conectar una función de callback a cada deslizador para detectar la interacción del jugador con el deslizador y aplicar los cambios deseados en el juego.
Inserta un ModuleScript en el contenedor ReplicatedStorage y renómbralo a SliderController.

Pega el siguiente código dentro del módulo:
SliderControllerlocal SliderController = {}SliderController.__index = SliderControllerexport type Value = numberexport type OnChanged = (Value) -> ()function SliderController.hydrate(props: {object: Instance,onChanged: OnChanged,initialValue: Value?})local object, onChanged, initialValue = props.object, props.onChanged, props.initialValuelocal handle = object:FindFirstChild("Handle", true)if not handle thenwarn(string.format("Se intentó hidratar el deslizador %s pero no se pudo encontrar el Handle", object:GetFullName()))endlocal innerFill = object:FindFirstChild("InnerFill", true)if not innerFill thenwarn(string.format("Se intentó hidratar el deslizador %s pero no se pudo encontrar el InnerFill", object:GetFullName()))endlocal dragDetector = handle:FindFirstChildWhichIsA("UIDragDetector")if not dragDetector thenwarn(string.format("Se intentó hidratar el deslizador %s pero no se pudo encontrar el UIDragDetector", object:GetFullName()))endlocal self = setmetatable({handle = handle,innerFill = innerFill,dragDetector = dragDetector,value = initialValue or 0.5,onChanged = onChanged,}, SliderController)-- Establecer valor inicialself:setValue(self.value)-- Conectar detector a la manipulación del jugadorself.dragConnection = dragDetector.DragContinue:Connect(function()self:setValue(handle.Position.X.Scale)end)return selfendfunction SliderController:setValue(value: Value)local clampedValue = math.clamp(value, 0, 1)self.value = clampedValue-- Actualizar la posición del mango y el tamaño del marco interno para que coincidanself.handle.Position = UDim2.fromScale(clampedValue, 0.5)self.innerFill.Size = UDim2.fromScale(clampedValue, 1)-- Ejecutar la callback del usuario con el último valorlocal changeSuccess, changeResult = pcall(self.onChanged, clampedValue)if not changeSuccess thenwarn("Error en la callback del deslizador:", changeResult)endendreturn SliderControllerExplicación del CódigoLines Propósito 7‑46 Función utilizada para inicializar y adjuntar comportamiento a un elemento deslizador, aceptando el Frame padre del deslizador como object, una función de callback onChanged para procesar los cambios del deslizador, y un valor inicial para la posición del mango del deslizador. Dentro de la función, las líneas 14‑27 confirman que existen los elementos mango, relleno interno y UIDragDetector del deslizador. La línea 38 llama a la función setValue() del módulo y las líneas 41‑43 conectan el evento DragContinue del detector de arrastre a la función setValue() del módulo (ver la siguiente fila para más detalles). 48‑61 Función utilizada para establecer el valor del deslizador. Dentro de la función, las líneas 53‑54 aseguran que la posición del mango y el tamaño del relleno interno estén sincronizados, y la línea 57 pasa el valor cambiado a la función de callback para que puedas utilizar el valor según sea necesario.
Crear el script de configuración
Con el botón de configuración y el menú de configuración finalizados, puedes unir todo con un solo script que utiliza los módulos de control.
Inserta un nuevo LocalScript en HUDContainer y renómbralo a SettingsScript para describir su propósito. Nota que este script debe estar al mismo nivel (hermano) que SettingsMenu y SettingsButton, los objetos de UI de nivel superior que gestionará.

Pega el siguiente código dentro del script:
SettingsScriptlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local SoundService = game:GetService("SoundService")local SliderController = require(ReplicatedStorage.SliderController)local StatefulObjectController = require(ReplicatedStorage.StatefulObjectController)local HUDContainer = script.Parent-- Inicializar botón de configuraciónlocal settingsButton = StatefulObjectController.hydrate({object = HUDContainer:FindFirstChild("SettingsButton"),states = {menuOpen = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out),properties = {Rotation = 45,},},menuClosed = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out),properties = {Rotation = 0,},},},initialStateName = "menuClosed"})-- Inicializar marco del menú de configuraciónlocal settingsMenu = StatefulObjectController.hydrate({object = HUDContainer:FindFirstChild("SettingsMenu"),states = {menuOpen = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out),properties = {Position = UDim2.fromScale(0.5, 0.5),Visible = true,},},menuClosed = {transition = TweenInfo.new(0),properties = {Position = UDim2.fromScale(0.5, 0.4),Visible = false,},},},initialStateName = "menuClosed"})-- Inicializar deslizador de volumen de efectoslocal effectsAudio = SoundService:FindFirstChild("Effects")local effectsVolumeSlider = SliderController.hydrate({object = HUDContainer:FindFirstChild("EffectsVolumeSlider", true),initialValue = effectsAudio and effectsAudio.Volume or 0.5,onChanged = function(value: SliderController.Value)if effectsAudio theneffectsAudio.Volume = valueendend,})-- Inicializar deslizador de volumen de fondolocal backgroundAudio = SoundService:FindFirstChild("Background")local backgroundVolumeSlider = SliderController.hydrate({object = HUDContainer:FindFirstChild("BackgroundVolumeSlider", true),initialValue = backgroundAudio and backgroundAudio.Volume or 0.5,onChanged = function(value: SliderController.Value)if backgroundAudio thenbackgroundAudio.Volume = valueendend,})-- Conectar botones a la interacción del jugadorHUDContainer:FindFirstChild("SettingsButton").Activated:Connect(function()local targetState = if settingsButton.currentStateName == "menuClosed"then "menuOpen"else "menuClosed"settingsButton:setState(targetState)settingsMenu:setState(targetState)end)HUDContainer:FindFirstChild("CloseButton", true).Activated:Connect(function()settingsButton:setState("menuClosed")settingsMenu:setState("menuClosed")end)Consulta las siguientes secciones para explorar cómo el script utiliza los módulos de control.
Botón de ConfiguraciónLas líneas 10-27 inicializan y adjuntan comportamiento al botón de configuración de la siguiente manera:
Lines Propósito 10‑11 Se declara una referencia local settingsButton y se hidrata con la función StatefulObjectController.hydrate(), y se referencia el botón SettingsButton dentro de HUDContainer como el objeto a hidratar. 12‑25 Se pasa una tabla states con dos estados únicos, menuOpen y menuClosed. Cada estado contiene una transición para declarar (a través de un TweenInfo) cómo el estado particular alcanzará sus propiedades objetivo definidas en la tabla properties. En este caso, menuOpen declara un cambio de estado a 45° para la Rotation del botón con un tween de Exponential de ½ segundo, mientras que menuClosed efectivamente restaura el botón a su rotación predeterminada de 0. 26 El estado inicial del botón se establece en menuClosed (esto podría establecerse en cualquier nombre de estado dentro de la tabla states). Más adelante en el script (líneas 76-86), los botones se conectan a la interacción del jugador para activar cambios en el estado de la UI:
Lines Propósito 76‑82 SettingsButton está conectado al evento Activated de la clase GuiButton para llamar a una función anónima. Dentro de la función anónima, las líneas 77‑79 alternan una variable targetState entre menuOpen y menuClosed. La línea 80 luego llama a setState() para establecer el estado del botón en targetState y la línea 81 hace lo mismo para SettingsMenu para que se abra o cierre con la activación de SettingsButton. 83‑86 CloseButton está conectado al evento Activated para llamar a una función anónima que establece el estado de ambos SettingsButton y SettingsMenu en menuClosed. Menú de ConfiguraciónLas líneas 30-49 inicializan y adjuntan comportamiento al menú de configuración de la siguiente manera:
Lines Propósito 30‑31 Se declara una referencia local settingsMenu y se hidrata con la función StatefulObjectController.hydrate() y se referencia el marco SettingsMenu dentro de HUDContainer como el objeto a hidratar. 32‑46 Similar al botón de configuración, se pasa una tabla states con dos estados únicos, menuOpen y menuClosed. Aquí, menuOpen declara un cambio de estado a UDim2.fromScale(0.5, 0.5) para la Position del marco con un tween de Bounce de ½ segundo, así como un estado de Visible de true. En contraste, menuClosed declara un cambio de estado posicional a UDim2.fromScale(0.5, 0.4) y Visible a false, pero el TweenInfo con duración 0 en la línea 41 efectivamente hace que el cambio de estado ocurra instantáneamente. 48 El estado inicial del marco se establece en menuClosed. Más adelante en el script (líneas 76-86), el menú de configuración está vinculado a la interacción del jugador:
Lines Propósito 81 Establece el mismo targetState para SettingsMenu para sincronizarlo con la activación de SettingsButton. 85 Establece el estado de SettingsMenu en menuClosed cuando se activa CloseButton. Deslizadores de VolumenLas líneas 51-73 inicializan y adjuntan comportamiento a EffectsVolumeSlider y BackgroundVolumeSlider de la siguiente manera:
Lines Propósito 52 Establece una referencia local al SoundGroup Effects dentro de SoundService. Un SoundGroup te permite interrelacionar múltiples Sounds y controlar el volumen de cada sonido en el grupo a través de la propiedad Volume del grupo. 53‑54 Se declara una referencia local effectsVolumeSlider y se hidrata con la función SliderController.hydrate() y se referencia el marco EffectsVolumeSlider dentro de HUDContainer ⟩ SettingsMenu como el objeto a hidratar. Nota que el segundo parámetro de true para FindFirstChild() le dice al método que busque recursivamente hacia abajo en la rama HUDContainer para localizar EffectsVolumeSlider. 55 El valor inicial del deslizador se establece en el Volume actual del grupo de sonido Effects, o 0.5 como respaldo. 56‑60 Establece la función de callback para aplicar cambios en el valor del deslizador al volumen del grupo de sonido Effects. 64‑73 Esencialmente lo mismo que las líneas 52-61, excepto para hidratar el marco BackgroundVolumeSlider dentro de HUDContainer ⟩ SettingsMenu y aplicar cambios en el valor del deslizador al volumen del grupo de sonido Background.
Con SettingsScript en su lugar, el juego ahora ofrece un ejemplo de UI interactiva completamente operativo que vincula un conjunto relacionado de objetos de UI a la interacción del jugador.








