28 mayo 2025

Cómo ejecutar métodos desde el inspector de Godot

Botón en el inspector
Botón en el inspector
Hace unos días publiqué un artículo explicando cómo hacer en Unity para activar la ejecución de métodos desde el inspector. Vimos la posibilidad de utilizar un atributo que generaba una entrada en un menú del editor; y también cómo crear un editor personalizado para que el inspector del componente mostrase un botón con el que activar el método.

¿En Godot se puede hacer lo mismo? Bueno, hasta hace realmente poco no. No había una manera fácil que permitiese añadir un botón al inspector sin meterse a desarrollar un plugin y sin que el resultado fuera discutible. En lo que se refiere a personalización del GUI, a Godot aún le queda un largo camino por recorrer para estar al nivel de Unity. 

El atributo [ExportToolButton]

Sin embargo, Godot añadió recientemente un nuevo atributo, @export_tool_button, y su equivalente en C# [ExportToolButton]. Este atributo permite exportar al inspector un campo de tipo Callable y mostrarlo como un botón. Al pulsar el botón se activa el método al que apunte el Callable.

Vamos a ver un ejemplo en Godot C#. Supongamos que tenemos un método ResetBoxManager en nuestro script:

El método que queremos activar al pulsar el botón
El método que queremos activar al pulsar el botón

Lo que hace el método da igual. Se trata sólo de un ejemplo. Muestro una captura de su contenido para que veas que la declaración e implementación del método no tiene nada de particular.

Y ahora el botón. Para declararlo, sólo tienes que decorar un campo de tipo Callable con una etiqueta [ExportToolButton].

Declaración del botón con [ExportToolButton]
Declaración del botón con [ExportToolButton]

Entre los paréntesis del atributo pondremos el texto que queremos que muestre el botón. 

Por otro lado, en la captura puedes ver cómo inicializar el Callable. He llamado al campo ResetButton (línea 107) y lo he inicializado con una nueva instancia de Callable que apunta, por sus parámetros, al método ResetBoxManager de esa misma clase (por eso el "this"), tal y como se puede ver en la línea 108.

Con eso, tu inspector mostrará el botón en el lugar que le habría correspondido al campo, y cuando lo pulses se activará el método enlazado. Tienes una captura de cómo queda en la imagen que abre este artículo.

Conclusión

Como puedes ver, el atributo [ExportToolButton] hace realmente fácil añadir botones a tus inspectores. Combina la sencillez del atributo [ContextMenu] que vimos en Unity, con la apariencia visual de sus editores personalizados. Con esto podrás dar un paso adelante a la hora de dotar a tus inspectores de funcionalidad que agilice el desarrollo y te facilite la depuración de tus proyectos.

24 mayo 2025

Cómo ejecutar métodos desde el inspector de Unity

Imagen de portada del artículo
Unity es una de las herramientas más populares para el desarrollo de videojuegos, gracias a su flexibilidad y su entorno visual intuitivo. Una de las características más útiles de Unity es su inspector. El inspector es una ventana en Unity que muestra las propiedades de los componentes de un GameObject seleccionado y nos permite ajustarlas. Por ejemplo, si un GameObject tiene un script asociado, el Inspector mostrará las variables públicas definidas en ese script, permitiéndote modificarlas directamente en el editor. 

Sin embargo, lo que muchos desarrolladores novatos desconocen es que también puedes configurar el inspector para ejecutar métodos específicos de tus scripts, ya sea para probar funcionalidades o para configurar flujos de trabajo más eficientes. En este artículo, te explicaré cómo ejecutar métodos desde el inspector de Unity de manera sencilla, una técnica que puede ahorrarte tiempo y facilitar la configuración y la depuración de tu proyecto.

El atributo [ContextMenu]

Una forma directa de ejecutar métodos desde el Inspector es utilizando el atributo [ContextMenu]. Este atributo añade una entrada al final del menú contextual del script en el inspector, lo que te permite invocar métodos con un solo clic. El menú contextual es el que sale al pulsar con el botón derecho en la barra del nombre del componente o a los tres puntos que hay en la esquina superior derecha del script en el inspector.

Menú contextual de un script en el inspector
Menú contextual de un script en el inspector

Si decoras el método que quieres activar con este atributo:

Ejemplo de uso del atributo
Ejemplo de uso del atributo

La entrada aparecerá al final del menú contextual, con el texto que hayas incluido entre paréntesis en el atributo.

Menú contextual resultante
Menú contextual resultante


Creación de un editor personalizado

La opción anterior es la más rápida de implementar, pero puede no ser la más cómoda ya que implica dos clicks para ejecutar el método en cuestión. 

Otra alternativa, más visual, pero que más laboriosa, es incluir un botón en el inspector para activar el método. Para ello, tienes que crear un editor propio que muestre una vista personalizada del componente en el editor.

Vamos a ver analizar el ejemplo más sencillo posible. Supongamos que tenemos un script MonoBehavior llamado BoxRangeManager, con un método ResetBoxManager() (el de la captura anterior) que es el que queremos activar. Para crear un editor que muestre una vista personalizada de BoxRangeManager tienes que crear una carpeta Editor dentro de la principal Assets. En la carpeta Editor es donde debes meter todos los scripts que dediques a personalizar el editor de Unity. Es muy importante que esos scripts no acaben en la carpeta Scripts habitual o si no puedes tener graves problemas de compilación cuando quieras construir el ejecutable final del juego. Recuerda: los scripts del juego en la carpeta Scripts, y los scripts de personalización del editor en la carpeta Editor.

Continuando con nuestro ejemplo, una vez creada la carpeta Editor, tienes que crear un script como el de la siguiente captura:

El código de nuestro editor personalizado
El código de nuestro editor personalizado

Lo primero reseñable del código anterior es que importa el espacio de nombres UnityEditor (línea 2). Esa es la primera señal de que tu script debería estar en la carpeta Editor o bien tendrás problemas al construir tu paquete ejecutable.

Incluir el código en un namespace propio (línea 5) es una buena práctica, pero no imprescindible.

Entramos en harina: para que una clase sirva para implementar un editor personalizado, debe heredar de UnityEditor.Editor (línea 8); y para que Unity sepa con qué MonoBehaviour usar el editor personalizado, hay que identificar a este en una etiqueta [CustomEditor] que decore la clase (línea 7).

A partir de ahí, pera personalizar cómo se muestra BoxRangeManager en el inspector, hay que reimplementar el método OnInspectorGUI() de UnityEditor.Editor (línea 10).

Nuestro ejemplo es el más sencillo posible. Sólo queremos mostrar el inspector por defecto y añadir al final de él un botón que active el método ResetBoxManager() al ser pulsado. Así que lo primero que hacemos es dibujar el inspector como lo habría hecho el inspector por defecto (línea 13). Luego añadimos un espacio para no pegar demasiado el botón a todo lo anterior (línea 16). Finalmente añadimos nuestro botón en la línea 22, pasando como parámetros el texto que queremos que aparezca en el botón y la altura que queremos que tenga.

El botón devuelve verdadero cuando es pulsado. Gracias a eso, en las líneas 24 y 25 podemos definir la reacción cuando se pulse. En ese caso, lo primero es ejecutar el método ResetBoxManager() que queríamos (línea 24) y luego llamar al método EditorUtility.SetDirty() (línea 25) para avisar al editor de que hemos hecho cambios en el inspector del componente y forzar que lo vuelva a redibujar.

Fíjate en que el editor personalizado contiene una referencia al MonoBehavior cuyo inspector muestra en el campo target. Sólo tienes que hacer una cast a la clase que sabes que estás mostrando (línea 19) para tener acceso a sus campos y métodos públicos.

Y ya está. No tienes que hacer nada más. Una vez que Unity recargue el dominio, se mostrará el editor personalizado con nuestro nuevo y flamante botón.

Aspecto de nuestro editor personalizado
Aspecto de nuestro editor personalizado

Conclusión

Ejecutar métodos desde el Inspector de Unity es una técnica poderosa para agilizar el desarrollo y la depuración de tus proyectos. Ya sea usando el atributo [ContextMenu] para pruebas rápidas,o con editores personalizados, estas herramientas te permiten interactuar con tu código de manera más dinámica y visual. 

Experimenta con estas opciones y descubre cuál se adapta mejor a tu flujo de trabajo. ¡El Inspector de Unity es mucho más que un simple editor de propiedades!

17 mayo 2025

Sensores volumétricos con dimensiones dinámicas en Unity

Crear un sensor volumétrico en Unity es sencillo: añades un componente Collider a un Transform, lo configuras como trigger, le das forma al Collider para definir el alcance del sensor, y añades un script al Transform que implemente los métodos OnTriggerEnter y OnTriggerExit. El Collider se encargará de activar esos métodos cuando otro collider entre o salga de su alcance, respectivamente.

Eso es todo, y suele bastar en la mayor parte de las ocasiones, pero hay otras en las que tenemos que definir la forma del sensor volumétrico en tiempo real, durante la ejecución del juego. Imagina, por ejemplo, que quieres dotar de un sensor volumétrico a un agente móvil para que detecte obstáculos que se puedan interponer en su rumbo. Lo normal será que pongas un sensor volumétrico con forma de caja delante del agente y que dicha caja se alargue hacia delante del agente a lo largo de una distancia proporcional a la velocidad del agente. Si la caja es demasiado corta, es probable que se detecten los obstáculos demasiado tarde como para que el agente pueda evitar la colisión. Si la caja es demasiado larga, el agente puede reaccionar a obstáculos demasiado lejanos, lo que sería poco realista.

Un agente móvil con un sensor volumétrico para detectar obstáculos
Un agente móvil con un sensor volumétrico para detectar obstáculos

Si el agente mantiene siempre una velocidad constante, nos bastará fijar manualmente el tamaño del sensor al diseñar el agente. Pero si el agente varía su velocidad, dependiendo de su comportamiento, es probable que no nos valga un único tipo de sensor para todas las posibles velocidades. En ese casos podríamos dotar al agente de varios sensores, con diferentes tamaños, y activar unos u otros en función de la franja de velocidad en la que se encuentre el agente. Sin embargo, esa solución será muy compleja y muy difícil de escalar si acabamos cambiando el abanico de velocidades del agente. Es mucho mejor si modificamos el tamaño del sensor durante la ejecución y lo adaptamos a la velocidad del agente en cada momento.

Te voy a explicar cómo lo hago yo con un BoxCollider2D. No digo que sea la mejor manera de hacerlo, pero sí que es la mejor manera con la que he dado hasta el momento. Te resultará fácil adaptar mis ejemplos a otras formas de collider y usarlo como punto de partida para encontrar el mecanismo que mejor se adapte a tu caso.

Un BoxCollider2D permite cambiar su tamaño a través de su propiedad size. Esta consiste en un Vector2 cuya primera componente define el ancho y la segunda la altura de la caja. Podemos cambiar el valor de esta propiedad en cualquier momento y el collider adaptará su forma a ella. El problema es que el BoxCollider se mantendrá centrado y crecerá en ambas direcciones de la componente que hayamos modificado. Si tenemos un caso como el de la captura de antes, lo que querremos es que el collider crezca hacia adelante, cuando aumentemos la velocidad, y no que el collider crezca también hacia la parte trasera del agente.

La manera de resolverlo es modificar la dimensión que nos interese, en el caso del agente de la captura será la altura de la caja, y al mismo tiempo desplazar la caja, manipulando su offset, para que parezca que sólo crece por uno de sus lados. En el ejemplo que nos ocupa, para hacer crecer el sensor, aumentaríamos la altura de la caja y al mismo tiempo desplazaríamos su offset hacia arriba para mantener el lado inferior de la caja en el mismo punto y que pareciese que sólo crece por el lado superior. 

Queremos que el collider crezca sólo por uno de sus lados
Queremos que el collider crezca sólo por uno de sus lados

Partiendo de lo anterior, voy a mostrarte el código que uso para generalizar los diferentes casos posibles para una caja. Para un BoxCollider, las modificaciones de tamaño posible son las del enum de la siguiente captura:

Posibles direcciones de crecimiento de una caja
Posibles direcciones de crecimiento de una caja

Las opciones se explican por si mismas: "Up" implica que queremos que la caja crezca solamente por su lado superior, "Down" por el inferior, "Left" por el lado de la izquierda y "Right" por el de la derecha. "Symmetric" viene a ser el comportamiento por defecto según el cual, al cambiar la altura la caja crecería tanto por su lado superior como por el inferior.

La cuestión es que, cuando aumentas el tamaño de una caja en una de sus dimensiones, dicho aumento se reparte equitativamente en un crecimiento de los dos lados de esa dimensión. Por ejemplo, si aumentas el tamaño de la altura de una caja en 2 unidades, puedes esperar que el lado superior ascienda una unidad y el inferior baje otra. Por tanto, si quisieras que la caja aparentase crecer sólo por el lado superior, deberías mantener el inferior en el mismo punto haciendo que la caja ascendiese una unidad.

La manera de generalizarlo es que la caja debe moverse la mitad del aumento de tamaño en la dirección de crecimiento que le hayamos marcado. 

Cómo calcular el vector de movimiento de la caja
Cómo calcular el vector de movimiento de la caja

El vector de movimiento de la caja se puede obtener de un método como el anterior (GetGrowOffsetVector). En el caso de nuestro ejemplo, en el que queremos que la caja aparente crecer por el lado superior, y que el inferior permanezca en su posición, el growDirection sería "Up" por lo que el método devolvería un Vector2 con los valores (0, 0.5). Fíjate que he definido OffsetBias como una constante de valor 0.5. Ese vector se multiplicará luego por el vector de crecimiento de la caja, lo que nos dará el desplazamiento de la caja.

El vector de crecimiento es el vector resultante de restarle al nuevo tamaño de la caja el tamaño que tenía inicialmente.

Cálculo del vector de crecimiento
Cálculo del vector de crecimiento

Por tanto, cada vez que queramos cambiarle el tamaño a la caja tendremos que calcular el vector de crecimiento y multiplicarlo por el vector de movimiento de la caja para obtener el nuevo ofsset de la caja para que aparente que sólo se ha movido por uno de sus lados.

 

Método para cambiar el tamaño de la caja
Método para cambiar el tamaño de la caja

El método SetBoxSize(), en el que implemento lo anterior, no puede ser más sencillo. En sus líneas 153 y 154 reseteo la caja a su tamaño inicial (1,1) y su offset a (0,0). Como el nuevo tamaño se fija acto seguido, el reseteo es instantáneo y no llega a percibirse.

Luego, en la línea 155 ejecuto el método GetGrowOffsetVector(), para obtener el vector de movimiento de la caja. Y en la línea 156, obtengo el vector de crecimiento al llamar al método GetGrowVector(). Ambos vectores se multiplican en la línea 158, para dar lugar al nuevo offset de la caja. Observa en esa misma línea, la 158, que utilizo el campo initialOffset (de tipo Vector2) para definir un offset por defecto de la caja. Ese será el offset que tendrá la caja cuando no se le haya aplicado movimiento alguno.

Fíjate también que utilizo el campo boxCollider para manipular las propiedades del collider. Este campo cuenta con una referencia a dicho collider. Puedes obtener esa referencia, bien exponiendo el campo en el inspector y arrastrando el collider sobre ese campo, o bien usando una llamada a GetComponent() en el Ready() del script.

Si incluyes los métodos anteriores en un script, y haces que este exponga métodos públicos, que acaben llamando a SetBoxSize() podrás manipular el tamaño del collider en tiempo real desde otros scripts del juego.

Y con esto ya lo tienes todo. Es bastante sencillo cuando ya lo tienes claro, pero si partes desde cero te puede llevar un rato hasta averiguar cómo hacerlo. Espero que este artículo te haya ahorrado ese rato y que te haya parecido interesante.

15 mayo 2025

Curso "Beat'em up Godot tutorial"

Hace algún tiempo que completé el tutorial "Beat'em up Godot tutorial", pero no me había dado tiempo a escribir sobre ello hasta ahora. 

Se trata de un video tutorial de 20 episodios, disponible en YouTube completamente gratis. Se hace en aproximadamente unas 10 horas. En él, el autor te guía en el proceso de crear un juego beat'em up, similar a los clásicos "Streets of Rage" o "Double Dragon". Según tengo entendido, el juego se desarrolló primero para una Game Jam y quedó tan redondo que resultaba candidato ideal para un curso.

El contenido empieza por el principio, así que es perfectamente válido para aquellos que quieran iniciarse con Godot, pero también se mete en conceptos más avanzados, pasada la curva inicial de aprendizaje, por lo que también resultará interesante a los alumnos con un nivel intermedio o, sencillamente, a aquellos que quiera un refresco en Godot.

Parte de los conceptos más básicos, como darle forma a los personajes, a sus animaciones y movimientos. Luego trata el sistema de daño, a base de cajas de daños, lo que me ha resultado bastante interesante ya que es un sistema mucho más sencillo y elegante que el que había usado hasta ahora. También implementa máquinas de estado para los personajes, desde cero y a golpe de código. Esto último puede resultar interesante, pero me ha parecido que pecaba de reinventar la rueda. Habría preferido que hubiera utilizado el sistema de máquinas de estado que ya viene incluido con Godot, a través de sus Animation Tree. Hecho eso pasa a la implementación de armas, tanto cuerpo a cuerpo como arrojadizas, así como de otros objetos recogibles como comida y botiquines. También me ha parecido muy interesante y original cómo arma los escenarios y los diferentes spawn points de él para generar los enemigos. Es todo muy modular y reutilizable. En cuanto al apartado de GUI, así como de música y sonido, cuenta lo básico pero tampoco es que un juego de este tipo, con estética retro, pida mucho más.

Ya llevo bastantes cursos, libros y algún desarrollo a mis espaldas, por lo que ya distingo bastante bien las buenas prácticas y lo que es un desarrollo ordenado y limpio, y este lo es. Quizás me dé la sensación de que si hubiera empleado C# el código habría quedado más modular, pero sí que parece que hace buen uso de GDScript hasta donde el lenguaje le permite. Todo el sistema de nodos de Godot ya obliga por si mismo a una altísima modularidad y reutilización, lo que unido a la ligereza del editor hace que el desarrollo del juego sea bastante directo y haya muy pocos momentos de espera o de generar código repetido. Dado que los recursos del juego están públicamente disponibles, quiero implementarlo en Unity a ver cómo queda, comparado con Godot.

Así que se hace muy entretenido y realmente obtienes un alto rendimiento del curso por el tiempo que le dedicas. Resumiendo: un curso muy recomendable.