11 enero 2025

Creación de Gizmos personalizados en Unity

Llevo varios artículos explicando cómo implementar Gizmos en Godot, pero me he dado cuenta de que no he explicado cómo hacer lo equivalente en Unity. Godot tiene varias ventajas sobre Unity, principalmente su ligereza y la rapidez que permite a la hora de iterar un desarrollo, pero hay que reconocer que Unity está muy maduro, y en la parte de los Gizmos se nota.

Como recordarás, los Gizmos son ayudas visuales que se implementan para representar magnitudes relacionadas con los campos y propiedades de un objeto. Se usan sobre todo en el editor, para ayudar durante la fase de desarrollo. Por ejemplo, siempre es más fácil hacerse una idea del alcance de tiro de un tanque de nuestro juego si dicho alcance se representa como un círculo en torno a él. Otro ejemplo: cuando editas la forma de un Collider, lo que se representa en pantalla es su Gizmo.

En Unity, cualquier MonoBehaviour puede implementar dos llamadas para dibujar Gizmos: OnDrawGizmos() y OnDrawGizmosSelected(). El primero dibuja los Gizmos en todo momento y el segundo sólo cuando se selecciona el GameObject que contiene el script. Es importante señalar una importante salvedad respecto a OnDrawGizmos() y es que no se llama si su script está minimizado en el inspector.

El script SeekSteeringBehavior está minimizado
El script SeekSteeringBehavior está minimizado

En teoría, se puede interactuar con los Gizmos dibujados en OnDrawGizmos(), pero no con los que se dibujan en OnDrawGizmosSelected(). En la práctica, el conocimiento sobre cómo interactuar con los Gizmos se perdió hace mucho. En el pasado se podía hacer click sobre ellos, pero parece que dejó de poder hacerse allá por la versión 2.5 de Unity. La mención que aparece en la documentación de OnDrawGizmos(), acerca de que se puede hacer click sobre los Gizmos, parece más bien un síntoma de que la documentación no está todo lo actualizada que debiera. 

En todo caso, el editor de Unity está lleno de Gizmos con los que se puede interactuar, pero eso es porque incluyen un elemento adicional que son los Handles. En este artículo nos centraremos en los Gizmos, como medio de representación visual pasiva y dejaré para un artículo posterior la explicación sobre cómo hacer Gizmos interactivos mediante Handles. Por simplificar aun más, me referiré sólo a OnDrawGizmos(); el otro método es exactamente igual, pero sólo se llama cuando su GameObject se selecciona en la jerarquía.

El método OnDrawGizmos() sólo se llama desde el editor cuando se produce una actualización o cuando el foco está puesto en la Scene View. No nos conviene sobrecargar el método con cálculos complicados porque podemos tumbar el rendimiento del editor. Aunque sólo se llame desde el editor, lo podríamos implementar tal cual, con la tranquilidad de que los Gizmos no se mostrarían en la versión compilada final del juego; sin embargo, yo prefiero meter la implementación del método entre guardas #if UNITY_EDITOR ... #endif. Manías de la edad. Cuando sólo usas Gizmos es redundante, pero sí es verdad que las necesitarás si incluyes Handles en el método, como veremos en un artículo posterior.

Supongamos, como ejemplo, que estamos diseñando un agente que se interponga entre otros dos (Agente-A y Agente-B). El algoritmo de movimiento del agente de interposición no viene al caso, pero tendrá el efecto de que medirá un vector entre los agentes A y B y situará al de interposición en el punto medio. En un caso así nos interesará dibujar en pantalla dónde está ese punto medio, para comprobar que el agente de interposición se dirige realmente a él. Es un caso ideal para los Gizmos.

El MonoBehaviour que realiza los cálculos para calcular ese punto medio, también implementa el método OnDrawGizmos() con el contenido de la captura:

Ejemplo de implementación de OnDrawGizmos()
Ejemplo de implementación de OnDrawGizmos()

Vamos a analizarla, línea a línea, para entender cómo podemos dibujar cualquier figura que queramos dentro de ese método.

Las líneas 127 y 128 evitan que el método se ejecute si hemos decidido hacer invisibles los Gizmos poniendo a falso la variable predictedPositionMarkerVisible o si _predictedPositionMarker es null. Esta última variable se refiere a la implementación del agente de interposición. Por razones que no vienen al caso, cuando arranca el MonoBehaviour, creo un GameObject que enlazo a _predictedPositionMarker. Conforme el script va calculando los puntos medios entre los agentes A y B, va situando ese GameObject en esos puntos medios. A los efectos que nos interesan en este artículo _predictedPositionMarker es un GameObject que hace de marcador del punto en el que se tiene que situar el agente que ejecuta el script. Por tanto, si fuese null no habría nada que dibujar.

En la línea 130 definimos el color con el que se dibujarán todos los Gizmos hasta que se meta un valor diferente en Gizmos.color.

En la línea 131 a 133 usamos la llamada a Gizmos.DrawLine() para dibujar una línea entre la posición del agente A y la del marcador.

En la línea 134 cambiamos el color de dibujado a magenta (morado para los amigos), para dibujar posteriormente un círculo en la posición del marcador, llamando a Gizmos.DrawSphere(). Este método dibuja un círculo relleno de color. Si sólo quisiésemos una circunferencia, podríamos usar Gizmos.DrawWireSphere().

Por último, en las líneas 137 a 139, llamamos a Gizmos.DrawLine() para dibujar otra línea (con su propio color) entre la posición del agente B y el marcador.

El resultado puede verse al ejecutar el juego desde el editor.

Gizmos dibujados al ejecutar el juego desde el editor
Gizmos dibujados al ejecutar el juego desde el editor

Los agentes A y B son los coloreados de azul y rojo, mientras que el agente de interposición es el de verde. Los gizmos son las líneas azules, roja y el círculo morado.

Y ya está. Usando estas primitivas, y el resto de las que ofrece el módulo Gizmos, podemos dibujar cualquier forma que queramos. Estas formas se irán actualizando en el editor cuando cambiemos en el inspector los campos de los que dependan o, si las ligamos a variables, conforme estas cambien al ejecutar el juego desde el editor.

Otra cosa más, yo suelo usar booleanos como predictedPositionMarkerVisible para poder decidir qué scripts concretos pueden pintar sus Gizmos; pero el editor de Unity permite desactivar el dibujado de todos los Gizmos. Para ello basta pulsar el botón que hay más a la derecha en el toolbar superior de la pestaña Scene.

Botón para activar o desactivar los Gizmos
Botón para activar o desactivar los Gizmos

Te aconsejo que te asegures de que el botón esté pulsado. Internet está lleno de posts de gentes preguntando la razón por la que no se le dibujan los Gizmos... y que luego resultó que habían desactivado sin querer este botón.