13/09/2006
Crear herramientas de editor personalizadas en Unity es una de las habilidades más potentes que un desarrollador puede adquirir. Sin embargo, una de las tareas más tediosas es posicionar cada elemento de la interfaz de usuario (UI) de forma manual. Calcular un `Rect` para cada botón, campo de texto o etiqueta puede volverse rápidamente un proceso engorroso y propenso a errores, especialmente cuando se busca un diseño que se adapte a diferentes tamaños de ventana. Aquí es donde entran en juego dos clases que cambiarán tu forma de trabajar: `GUILayout` y `EditorGUILayout`.

Estas clases son la respuesta de Unity al diseño de UI automático. En lugar de especificar las coordenadas exactas de cada elemento, simplemente los declaramos en el orden en que queremos que aparezcan, y Unity se encarga de calcular su posición y tamaño. Este enfoque, conocido como diseño de retención inmediata, nos permite construir interfaces complejas de manera mucho más rápida y legible. En este artículo, exploraremos a fondo cómo utilizar estas herramientas para crear layouts verticales, horizontales, anidados y responsivos, transformando por completo tu flujo de trabajo de scripting del editor.
El Diseño Vertical: El Comportamiento por Defecto
Cuando comienzas a utilizar `GUILayout` o `EditorGUILayout`, notarás que el comportamiento predeterminado es apilar los elementos uno debajo del otro. Esto se conoce como un diseño vertical. La mayoría de los métodos de dibujo en estas clases tienen nombres idénticos a sus contrapartes en `GUI` y `EditorGUI`, con la gran diferencia de que no requieren un `Rect` como primer parámetro.
Veamos un ejemplo sencillo en el método `OnGUI` de una ventana de editor:
private void OnGUI() { // Estos campos se dibujarán uno debajo del otro automáticamente exampleFloat = EditorGUILayout.FloatField(new GUIContent("Mi Flotante", "Tooltip para el flotante."), exampleFloat); exampleTransform = EditorGUILayout.ObjectField("Mi Transform", exampleTransform, typeof(Transform), true) as Transform; }Como puedes ver, sin especificar ninguna posición, Unity los organiza verticalmente. Sin embargo, para tener un mayor control y mejorar la legibilidad del código, es una buena práctica definir explícitamente los bloques de layout. Para un diseño vertical, usamos `GUILayout.BeginVertical()` y `GUILayout.EndVertical()`.
private void OnGUI() { GUILayout.BeginVertical(); { // El resultado es el mismo, pero el código es más claro exampleFloat = EditorGUILayout.FloatField(new GUIContent("Mi Flotante", "Tooltip para el flotante."), exampleFloat); exampleTransform = EditorGUILayout.ObjectField("Mi Transform", exampleTransform, typeof(Transform), true) as Transform; } GUILayout.EndVertical(); }Es obligatorio cerrar cada bloque `Begin...` con su correspondiente `End...`. Si no lo haces, Unity lanzará un error. El uso de llaves `{}` es una convención personal que ayuda a visualizar claramente dónde empieza y termina un bloque de layout.
Organización Horizontal: Elementos Lado a Lado
Ahora, ¿qué pasa si queremos colocar elementos uno al lado del otro? Para esto, utilizamos un diseño horizontal con `GUILayout.BeginHorizontal()` y `GUILayout.EndHorizontal()`. Todos los elementos que dibujes entre estas dos llamadas se distribuirán horizontalmente.
private void OnGUI() { GUILayout.BeginHorizontal(); { exampleFloat = EditorGUILayout.FloatField(new GUIContent("Mi Flotante"), exampleFloat); exampleTransform = EditorGUILayout.ObjectField("Mi Transform", exampleTransform, typeof(Transform), true) as Transform; } GUILayout.EndHorizontal(); // Este botón vuelve al layout por defecto (vertical) if (GUILayout.Button("Mi Botón")) { Debug.Log("¡Botón presionado!"); } }En el ejemplo anterior, el campo flotante y el campo de objeto aparecerán en la misma línea. El botón, al estar fuera del bloque horizontal, se dibujará debajo, siguiendo el flujo vertical predeterminado. Recuerda siempre que un `BeginHorizontal` debe cerrarse con un `EndHorizontal`, no con un `EndVertical`.
Añadiendo Espacios para Respirar
Un diseño sin espaciado puede verse abarrotado y poco profesional. `GUILayout` nos ofrece una forma sencilla de añadir espacio: `GUILayout.Space(float pixels)`. Este método añadirá un espacio vertical si se encuentra dentro de un `BeginVertical` o un espacio horizontal si está en un `BeginHorizontal`. También existe `EditorGUILayout.Space()`, que no toma parámetros y utiliza el espaciado estándar del editor.
private void OnGUI() { GUILayout.BeginHorizontal(); { exampleFloat = EditorGUILayout.FloatField(new GUIContent("Mi Flotante"), exampleFloat); GUILayout.Space(20f); // Añade 20 píxeles de espacio horizontal exampleTransform = EditorGUILayout.ObjectField("Mi Transform", exampleTransform, typeof(Transform), true) as Transform; } GUILayout.EndHorizontal(); EditorGUILayout.Space(); // Añade un espacio vertical estándar if (GUILayout.Button("Mi Botón")) { Debug.Log("¡Botón presionado!"); } }Layouts Anidados: La Clave para Interfaces Complejas
La verdadera potencia de `GUILayout` se revela cuando comenzamos a anidar layouts. Podemos tener un diseño vertical dentro de uno horizontal, y viceversa. Piensa en ello como un sistema de pilas: el último layout que abres (`Begin...`) es el primero que debes cerrar (`End...`). Esto nos permite crear estructuras de UI muy sofisticadas, como columnas y filas complejas.

Imaginemos que queremos crear dos columnas, cada una con su propia etiqueta y campo:
private void OnGUI() { // Layout principal horizontal para contener las dos columnas GUILayout.BeginHorizontal(); { // Primera columna (Vertical) GUILayout.BeginVertical(); { EditorGUILayout.LabelField("Columna 1"); exampleFloat = EditorGUILayout.FloatField(new GUIContent("Flotante"), exampleFloat); } GUILayout.EndVertical(); GUILayout.Space(50f); // Espacio entre columnas // Segunda columna (Vertical) GUILayout.BeginVertical(); { EditorGUILayout.LabelField("Columna 2"); exampleTransform = EditorGUILayout.ObjectField("Transform", exampleTransform, typeof(Transform), true) as Transform; } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); }Con este código, hemos creado una estructura de dos columnas de forma limpia y escalable. Los diseños anidados son fundamentales para organizar la información de manera efectiva en tus herramientas de editor.
Definiendo un Área de Dibujo con `BeginArea`
Por defecto, `GUILayout` utiliza toda la ventana del editor como su área de dibujo. Sin embargo, a veces necesitamos restringir nuestra UI a una sección específica de la ventana, por ejemplo, para crear márgenes o paneles fijos. Para ello, usamos `GUILayout.BeginArea(Rect screenRect)` y `GUILayout.EndArea()`. Todo lo que se dibuje entre estas llamadas estará contenido dentro del `Rect` que especifiquemos, y sus coordenadas serán relativas a esa área, no a la ventana completa.
private void OnGUI() { // Creamos un área con 10 píxeles de margen en todos los lados Rect areaRect = new Rect(10, 10, position.width - 20, position.height - 20); GUILayout.BeginArea(areaRect); { // Todo nuestro código de layout anterior iría aquí dentro EditorGUILayout.LabelField("Contenido con margen"); if (GUILayout.Button("Botón dentro del área")) {} } GUILayout.EndArea(); }Opciones de Layout: Personalización y Diseño Responsivo
Hasta ahora, hemos dejado que Unity decida el tamaño de los elementos. Pero `GUILayout` nos da un control granular a través de `GUILayoutOption`. Casi todos los métodos de `GUILayout` y `EditorGUILayout` aceptan un array de `GUILayoutOption` como último parámetro. Esto nos permite especificar anchos, altos, expansiones y más.
Algunas de las opciones más comunes son:
- `GUILayout.Width(float width)`: Especifica un ancho fijo.
- `GUILayout.Height(float height)`: Especifica una altura fija.
- `GUILayout.MinWidth(float width)` / `GUILayout.MinHeight(float height)`: El elemento no se encogerá más allá de este tamaño.
- `GUILayout.MaxWidth(float width)` / `GUILayout.MaxHeight(float height)`: El elemento no se estirará más allá de este tamaño.
- `GUILayout.ExpandWidth(bool expand)`: Permite o prohíbe que el elemento se expanda horizontalmente para rellenar el espacio disponible.
- `GUILayout.ExpandHeight(bool expand)`: Permite o prohíbe que el elemento se expanda verticalmente.
Además, existe una herramienta muy útil: `GUILayout.FlexibleSpace()`. Este método crea un espacio vacío que se expande para ocupar todo el espacio sobrante en un layout, empujando a los otros elementos. Es perfecto para alinear elementos a la derecha o para distribuir varios elementos de manera uniforme.
Combinemos todo lo aprendido en un ejemplo final y avanzado:
private void OnGUI() { GUILayout.BeginArea(new Rect(10, 10, position.width - 20, position.height - 20)); GUILayout.BeginHorizontal(); { // Columna 1 con un ancho máximo GUILayout.BeginVertical(GUILayout.MaxWidth(250)); { EditorGUILayout.LabelField("Columna Izquierda"); exampleFloat = EditorGUILayout.FloatField("Flotante", exampleFloat); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); // ¡Este espacio empuja la siguiente columna a la derecha! // Columna 2 con un ancho máximo GUILayout.BeginVertical(GUILayout.MaxWidth(300)); { EditorGUILayout.LabelField("Columna Derecha"); exampleTransform = EditorGUILayout.ObjectField("Transform", exampleTransform, typeof(Transform), true) as Transform; } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); // Empuja el botón hacia la parte inferior if (GUILayout.Button("Botón Expansible", GUILayout.Height(40))) { Debug.Log("¡Click!"); } GUILayout.EndArea(); }Si pruebas este código y redimensionas la ventana del editor, verás cómo el espacio flexible se adapta, manteniendo las columnas en sus respectivos lados y el botón en la parte inferior. Este es el poder del diseño automático y responsivo.
Tabla Comparativa: GUI vs. GUILayout
| Característica | GUI / EditorGUI | GUILayout / EditorGUILayout |
|---|---|---|
| Posicionamiento | Manual (requiere un Rect) | Automático (basado en el flujo) |
| Complejidad | Alta para layouts dinámicos | Baja, ideal para layouts simples y complejos |
| Flexibilidad | Control absoluto a nivel de píxel | Alta, con opciones para diseños responsivos |
| Caso de Uso Principal | Interfaces superpuestas, HUDs, control total | Ventanas de editor, inspectores personalizados |
Preguntas Frecuentes (FAQ)
¿Cuál es la diferencia principal entre EditorGUILayout y GUILayout?
Ambas clases gestionan el layout automático. La principal diferencia es que `EditorGUILayout` contiene controles específicos del editor de Unity, como `ObjectField`, `LayerField`, `TagField`, etc., que están diseñados para interactuar con Assets y GameObjects. `GUILayout` contiene controles más genéricos como `Button` y `Label`. Generalmente, para scripting del editor, preferirás usar `EditorGUILayout` siempre que sea posible.
¿Es realmente obligatorio usar `EndVertical` si usé `BeginVertical`?
Sí, absolutamente. El sistema de layouts de Unity funciona como una pila. Cada llamada a `Begin...` empuja un nuevo grupo de layout a la pila. La llamada a `End...` correspondiente lo saca. Si no cierras un grupo que abriste, la pila se corromperá y Unity lanzará un error `ArgumentException: Getting control...`. Siempre asegúrate de que cada `Begin` tenga su `End`.
¿Cuándo debería seguir usando el sistema manual con `Rect`?
Aunque `GUILayout` es increíblemente útil, hay situaciones donde el control manual es preferible. Por ejemplo, si necesitas superponer elementos, crear una interfaz de arrastrar y soltar muy específica, o tener un control total a nivel de píxel sobre la posición de cada elemento, el sistema basado en `Rect` de `GUI` y `EditorGUI` te dará esa flexibilidad que el sistema automático no puede ofrecer.
Si quieres conocer otros artículos parecidos a Domina GUILayout y EditorGUILayout en Unity puedes visitar la categoría Juegos.
