19 abril 2026

Prevención de colisiones con otros agentes en Godot


En otro artículo expliqué cómo usar el nodo NavigationAgent2D de Godot para obtener la ruta para llegar a cualquier destino, atravesando un escenario. En aquel caso, la ruta obtenida permitía evitar el choque con los obstáculos estáticos del escenario. Sin embargo, en aquel artículo no vimos cómo evitar el choque con otros agentes. Dado que los agentes son dinámicos, no cabe la preparación previa de un NavigationPolygon de navegación, como hacíamos con los elementos estáticos. Aunque, el mismo nodo NavigationAgent2D permite gestionar también la prevención de colisiones con otros agentes, lo hace siguiendo mecanismos independientes a los usados para elaborar las rutas para desplazarse por el escenario.

En el caso de los escenarios estáticos, NavigationAgent2D creaba un grafo basado en el NavigationPolygon generado a partir del escenario estático. Sobre ese grafo ejecutaba el algoritmo A* para encontrar la ruta hasta el destino.

Para evitar la colisión con otros agentes, NavigationAgent2D mantiene una simulación en la que ubica a los distintos agentes del juego (aquellos objetos con un nodo NavigationAgent2D) y utiliza sus posiciones, velocidades y radios para prever cuándo hay riesgo de que se produzca una colisión. Esto lo hace utilizando un algoritmo denominado Reciprocal Velocity Obstacle (RVO). Para que el nodo NavigationAgent2D sepa a dónde nos gustaría ir, debemos pasarle el vector de velocidad ideal (derecho a nuestro siguiente objetivo). Con esa información, el nodo hará sus cálculos y nos recomendará el vector de velocidad más cercano al ideal que nos garantiza no chocar con ninguno de los agentes cercanos. Nos corresponderá a nosotros utilizar ese vector como mejor veamos para mover al agente. Al igual que pasaba con la búsqueda de rutas, el nodo NavigationAgent2D es pasivo. Facilita información, pero no mueve al agente, a diferencia del componente equivalente en Unity que sí que mueve por defecto al agente que lo porta.

Al ser pasivo, el nodo puede estar en cualquier punto de la arquitectura de un objeto. Como pasa con el resto de los nodos de Godot, podemos adosarle un script que herede de él y extienda su funcionalidad. A la hora de configurar su uso para prevenir las colisiones con otros agentes, tenemos que centrarnos en la sección Avoidance del nodo.

Configuración de la prevención de colisiones con otros agentes en el nodo NavigationAgent2D
Configuración de la prevención de colisiones con otros agentes en el nodo NavigationAgent2D

Para empezar, la simulación para prevenir las colisiones se activa con el check que hay en el título de la sección. Si no está marcado, el nodo se limitará a trabajar en la resolución de rutas.

Una vez marcado, se mostrarán el resto de las opciones de la captura. La primera, Radius, sirve para definir el tamaño del agente. Como mínimo, deberías fijarlo al radio del collider de tu agente, pero no está de más añadir algo más para evitar que los agentes rocen entre sí. Ten en cuenta que este parámetro sólo sirve para la prevención de colisiones con otros agentes y no para la búsqueda de rutas. En ese último caso, el radio que se utiliza es el que aparece en la sección Agents del NavigationPolygon.

La opción Neighbor Distance define el alcance de nuestra detección de otros agentes. Cualquier agente que esté a más distancia no será tenido en cuenta por el algoritmo.

Como te puedes imaginar, Max Neighbors nos permite fijar la cantidad máxima de agentes que se tendrán en cuenta en los cálculos. Al final, es una manera de fijar la carga máxima del algoritmo en el rendimiento del juego.

La rapidez de reacción del agente se configura con el parámetro Time Horizon Agents. En realidad, se trata del tiempos de colisión mínimo a partir del cual el algoritmo empieza a calcular vectores de evasión. Si el algoritmo calcula que la colisión con otro agente tardará en llegar más de lo que fija este parámetro, descartará al agente como fuente de peligro. Si reducimos mucho este parámetro, el agente esperará mucho más antes de iniciar las maniobras evasivas. De hecho, si nos pasamos reduciendo este parámetro, puede que empiece a esquivar tan tarde que acabe estrellándose. Por contra, si ampliamos demasiado este parámetro, el agente empezará a condicionar su velocidad demasiado pronto y empezará a esquivar exageradamente antes.

Como decíamos, el algoritmo sólo tiene en cuenta en su simulación aquellos objetos móviles dotados del nodo NavigationAgent2D. Pero también tiene en cuenta aquellos dotados del nodo NavigationObstacle2D, a los cuales considera objetos inmóviles. A este último caso es al que se aplica el parámetro Time Horizon Obstacles, con las mismas consecuencias que tenía Time Horizon Agents para los objetos móviles.

El parámetro Max Speed define la velocidad máxima del agente. El vector de evasión nunca tendrá una magnitud superior a este valor.

Avoidance Layers, funciona como una capa de colisiones físicas. En este caso sirve para definir a qué capa de evasión pertenece el agente, mientras que el parámetro Avoidance Mask sirve para configurar las capas de los agentes a los que hay que evitar.

Por último, el parámetro Avoidance Priority sirve para configurar la preferencia de paso de este agente frente a otros. Este agente no intentará evitar a otros agentes con menos prioridad, porque estos forzarán aún más sus maniobras de evasión para dejarle pasar.

Con todo lo anterior, el nodo quedará preparado que realizar sus cálculos. Vamos a ver cómo interactuar con él desde el código para darle los datos que necesita y recuperar sus resultados.

Lo primero que hay que tener en cuenta es que el nodo no calcula el vector de evasión inmediatamente. Una vez que le pasamos el vector de velocidad que nos gustaría adoptar, necesita un tiempo para calcular el vector más cercano a él que nos ahorra un choque con otro agente. En el fragor del juego puede que ese tiempo parezca instantáneo, pero en la lógica de tu código ya te advierto que el resultado no estará disponible en el mismo frame. Por eso, una vez que el nodo acaba el cálculo del vector de evasión, emite la señal VelocityComputed. Tendremos que suscribir un callback a esa señal para recoger el vector de evasión calculado. Lo normal será hacer esa suscripción en el _Ready().

Suscripción a la señal VelocityComputed
Suscripción a la señal VelocityComputed

La captura anterior es en Godot C#, pero para GDScript es muy similar. La suscripción se produce en la línea 192, en la que se asocia el método OnAvoidVectorComputed a la señal VelocityComputed.

En mi caso, el citado callback es muy sencillo y se limita a recoger en una variable globas el resultado incluido en la señal.

Recogida del vector de evasión calculado
Recogida del vector de evasión calculado

Ese vector será el que pasemos a nuestro mecanismos de movimiento para avanzar hacia nuestro destino mientras evitamos chocar con otros agentes.

Para que ese vector se mantenga actualizado, tendremos que asegurarnos de facilitarle, en cada frame físico (_PhysicsProcess()) el vector de aproximación ideal a nuestro objetivo.

Cómo pasarle al nodo la velocidad de aproximación ideal al destino

Según la documentación de Godot sobre este nodo, eso es todo lo que tenemos que hacer para desplazar a nuestro agente por un escenario poblado de otros agentes. Sin embargo, el algoritmo RVO empleado por este nodo tiene un punto débil: el caso en el que dos agentes van de frente, el uno contra el otro. En ese caso, el vector de evasión es justamente el opuesto al vector de dirección por lo que ambos se anulan. O bien el agente se queda parado o bien la situación degenera en que uno acaba persiguiendo al otro. La solución que le he encontrado a ese problema es detectarlo con un dot-product y aplicar en ese caso un vector de evasión perpendicular al vector de dirección.

Resolución del problema de los agentes chocando de frente
Resolución del problema de los agentes chocando de frente

Dado que NavigationAgent2D devuelve un vector que aúna la dirección ideal que queremos tomar y el vector de evasión, si sólo queremos la componente del vector del vector de evasión tendremos que restar la de la dirección ideal. Es lo que hago en la línea 333.

Para ver si ese vector es opuesto al de dirección, hacemos un dot-product en la línea 334 con los valores normalizados del vector de evasión y el de la velocidad actual del agente. Si está muy cercano a 1 (en valor absoluto) es que son vectores opuestos. En ese caso, y sólo en ese caso, sustituyo el vector de evasión por una versión perpendicular del vector de velocidad actual. Mantenido durante el suficiente tiempo (de ahí el temporizador, activado en la línea 348) un vector de evasión así debería generar, en el peor de los casos, una situación de persecución de la que el agente delantero acabaría zafándose por un lateral, para acabar dirigiéndose a su objetivo original. No es una maniobra bonita, pero funciona en los contados casos en los que nos veamos con dos agentes dirigiéndose el uno contra el otro.

Con esto ya tienes todo lo que necesitas para evitar que tus agentes colisionen entre sí cuando se muevan por el escenario. En todo caso, recuerda que todos los agentes deben contar con un nodo NavigationAgent2D o no serán incluidos en la simulación para evitar colisiones. 

26 febrero 2026

Mis impresiones sobre los cursos de Zenva


Como muchos sabréis, en mi perfil de X suelo avisar de los packs de assets, cursos y libros que me encuentro por Internet a buen precio. Hace un tiempo salió un pack de cursos de Zenva en Humble Bundle. Basándome en lo que había leído en foros de Reddit, advertí en la noticia de que la calidad de los cursos podía no ser la esperada. Para mi sorpresa, Pablo Farias (@pablofariasnew), fundador de Zenva, me pidió que le diese una oportunidad a sus cursos porque no creía que merecieran esa crítica. Por eso, decidí comprarme el pack de Humble Bundle y hacer varios de los cursos, para comprobar de primera mano si la idea que me había formado en Reddit era correcta o no.

El pack que me compre contenía un montón de cursos de Godot. También tenía algunos cursos de Unity que había comprado hace bastante tiempo, pero que no había podido hacer. Al final, de todos ellos, he hecho 4 cursos:

  • Tower Defenser Game in Godot - Unit 1 - Base Game
  • Tower Defenser Game in Godot - Unit 2 - Enhanced Towers
  • Intro to Visual Shaders in Godot 4
  • The Complete Procedural Terrain Generation Unity Course.

Los dos primeros son de algo más de una hora y media de duración, el de los shaders tiene apenas una hora, mientras que el de generación procedural de terrenos en Unity abarca más de tres horas y media. Se trata de una muestra muy parecida a los que suelen ser los cursos de Zenva, o al menos los que yo he adquirido en mis packs, situándose todos en la franja entre la hora y las cuatro o cinco.

Para mi sorpresa, he de decir que me han gustado mucho. Todos los cursos están bien estructurados y cubren el alcance que prometen. No profundizan, pero lo que explican es suficiente para que entiendas lo que está pasando y lo practiques con seguridad en el editor por ti mismo. Incluso los conceptos teóricos, aunque se cuentan rápido, lo hacen con claridad y te queda una idea correcta, bien válida para profundizar por ti mismo a partir de ahí.

Por supuesto, son todos en inglés, pero cuentan con subtítulos en múltiples idiomas, incluido el español. Por lo que he podido ver, la traducción de los subtítulos en español es muy buena. De todos modos, yo he preferido verlos con los subtítulos en inglés. La pronunciación de los profesores es muy limpia y se les entiende sin dificultad, así que sólo he usado los subtítulos para comprobar palabras puntuales en las que me despistaba. Con los subtítulos tendrás problema en seguir el curso, sea cual sea tu idioma nativo. Aunque sería genial que esta plataforma, y el resto de las que publican cursos, empezasen a hacer como Youtube y tradujesen también el audio mediante IA. No sé el coste de producción que tendrá, pero cuando te encuentras uno de esos, con el audio traducido son una gozada, porque además les queda hasta natural la voz.

He intentado ver el precio de cursos sueltos, pero aparentemente sólo tienes la opción de suscribirte anualmente, lo que te da acceso a todo el catálogo de cursos. Esa suscripción parece valer unos 59.70 $ anuales. En cuanto a la valoración del precio no sabría decirte, porque eso depende del valor que te aporten, en función del punto del aprendizaje en que te encuentres. En mi caso concreto, ya tengo superada la fase de los cursos generalistas de introducción. Ya suelo ir a cursos intermedios que traten temas concretos. Eso me hace ser más selectivo, por lo que suelo evitar las subscripciones como las de Zenva. Pero si estás empezando, puede ser una buena opción pagar ese precio y tener todos los cursos concentrados y al alcance de la mano, en vez de ir dando tumbos por internet buscando tutoriales. También puedes hacer como yo, y estar atentos a los packs que Zenva suele ofrecer con cierta frecuencia. Eso te permitiría acceder a sus contenidos sin necesidad de estar pagando una subscripción que puede ser que no amortices si no tienes tiempo para estar viendo cursos asiduamente. Si me sigues en X te avisaré cuando me entere de un nuevo pack ;-)

Resumiendo: me han gustado los cursos de Zenva. Son de buena calidad. Los comentarios que había leído en Reddit no están justificados, al menos en mi opinión. Por mi parte, no me cabe duda de que compraré más packs conforme los vayan sacando. Me alegro de haber podido salir de mi error, no hay tantas opciones de calidad como para descartar una equivocadamente.