Comprender los comportamientos de la dirección evitar colisiones

La navegación decente de NPC a menudo requiere la habilidad de evitar obstáculos. Este tutorial cubre el evitación de colisiones comportamiento de la dirección, que permite a los personajes esquivar con gracia cualquier cantidad de obstáculos en el entorno.

Nota: Aunque este tutorial está escrito con AS3 y Flash, debería poder utilizar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos. Debes tener una comprensión básica de vectores matemáticos.


Introducción

La idea básica detrás de la prevención de colisiones es generar una fuerza de dirección para esquivar los obstáculos cada vez que uno esté lo suficientemente cerca para bloquear el paso. Incluso si el entorno tiene varios obstáculos, este comportamiento utilizará uno de ellos a la vez para calcular la fuerza de evitación..

Solo se analizan los obstáculos por delante del personaje; El más cercano, que se dice que es el más amenazante, se selecciona para evaluación. Como resultado, el personaje es capaz de esquivar todos los obstáculos en el área, pasando de uno a otro con gracia y sin problemas..


Se analizan los obstáculos por delante del personaje y se selecciona el más cercano (el más amenazante)..

El comportamiento de evitar colisiones no es un algoritmo de búsqueda de ruta. Hará que los personajes se muevan a través del entorno, evitando obstáculos y, finalmente, encontrando una ruta para recorrer los bloques, pero no funciona realmente bien con los obstáculos "L" o "T", por ejemplo..

Propina: Este comportamiento de evitar colisiones puede parecer similar al de huir, pero existe una diferencia importante entre ellos. Un personaje que se mueve cerca de una pared lo evitará solo si está bloqueando el camino, pero el comportamiento de huir siempre lo alejará de la pared..

Viendo adelante

El primer paso para evitar obstáculos en el entorno es percibirlos. Los únicos obstáculos que debe preocupar al personaje son los que están frente a él y bloqueando directamente la ruta actual..

Como se explicó anteriormente, el vector de velocidad describe la dirección del personaje. Se utilizará para producir un nuevo vector llamado adelante, que es una copia del vector de velocidad, pero con una longitud diferente:


los adelante Vector es la línea de visión del personaje..

Este vector se calcula de la siguiente manera:

 adelante = posición + normalizar (velocidad) * MAX_SEE_AHEAD

los adelante longitud del vector (ajustado con MAX_SEE_AHEAD) define qué tan lejos "verá" el personaje.

El mayor MAX_SEE_AHEAD Es decir, cuanto antes el personaje comience a actuar para esquivar un obstáculo, porque lo percibirá como una amenaza, incluso si está muy lejos:


Cuanto mayor sea la longitud de adelante, más temprano comenzará a actuar el personaje para esquivar un obstáculo..

Comprobando la colisión

Para verificar la colisión, cada obstáculo (o su caja delimitadora) debe describirse como una forma geométrica. El uso de una esfera (círculo en dos dimensiones) da los mejores resultados, por lo que cada obstáculo en el entorno se describirá como tal.

Una posible solución para verificar la colisión es la intersección entre la línea y la esfera, la línea es la adelante Vector y la esfera es el obstáculo. Ese enfoque funciona, pero voy a utilizar una simplificación de lo que es más fácil de entender y tiene resultados similares (incluso mejores a veces).

los adelante El vector se utilizará para producir otro vector con la mitad de su longitud:


Misma direccion, la mitad de la longitud.

los adelante2 vector se calcula exactamente como adelante, pero su longitud se corta por la mitad:

 adelante = posición + normalizar (velocidad) * MAX_SEE_AHEAD adelante2 = posición + normalizar (velocidad) * MAX_SEE_AHEAD * 0.5

Queremos realizar una verificación de colisión para comprobar si alguno de estos dos vectores está dentro de la esfera del obstáculo. Esto se logra fácilmente comparando la distancia entre el extremo del vector y el centro de la esfera.

Si la distancia es menor o igual que el radio de la esfera, entonces el vector está dentro de la esfera y se encontró una colisión:


El vector de adelante está interceptando el obstáculo si d < r. The ahead2 vector was omitted for clarity.

Si ya sea De los dos vectores siguientes están dentro de la esfera del obstáculo, entonces ese obstáculo está bloqueando el camino. La distancia euclidiana entre dos puntos puede ser utilizada:

 distancia de función privada (a: Objeto, b: Objeto): Número return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));  función privada lineIntersectsCircle (adelante: Vector3D, adelante2: Vector3D, obstáculo: Círculo): Booleano // la propiedad "centro" del obstáculo es un Vector3D. distancia de retorno (obstacle.center, adelante) <= obstacle.radius || distance(obstacle.center, ahead2) <= obstacle.radius; 

Si hay más de un obstáculo bloqueando el camino, se selecciona el más cercano (el "más amenazante") para el cálculo:


El obstáculo más cercano (el más amenazante) se selecciona para el cálculo..

Cálculo de la fuerza de evitación

La fuerza de evitación debe empujar al personaje lejos del obstáculo, permitiéndole esquivar la esfera. Se puede hacer usando un vector formado usando el centro de la esfera (que es un vector de posición) y el adelante vector. Calculamos esta fuerza de evitación de la siguiente manera:

 avoidance_force = adelante - obstacle_center avoidance_force = normalize (avoidance_force) * MAX_AVOID_FORCE

Después evitación_fuerza Se calcula está normalizado y escalado por. MAX_AVOID_FORCE, que es un número usado para definir el evitación_fuerza longitud. El mayor MAX_AVOID_FORCE Es decir, más fuerte es la fuerza de evitación que empuja al personaje lejos del obstáculo..


Cálculo de fuerza de evitación. La línea naranja discontinua muestra el camino que tomará el personaje para evitar el obstáculo. Propina: La posición de cualquier entidad puede describirse como un vector, por lo que puede usarse en cálculos con otros vectores y fuerzas..

Evitando el obstáculo

La implementación final para el evitación de colisiones() El método, que devuelve la fuerza de evitación, es:

 Función privada collisionAvoidance (): Vector3D ahead =…; // calcula el vector adelante ahead2 =…; // calcula la variable de vector ahead2 most mostThreatening: Obstacle = findMostThreateningObstacle (); evitación de var: Vector3D = nuevo Vector3D (0, 0, 0); if (mostThreatening! = null) avoidance.x = ahead.x - mostThreatening.center.x; Avoidance.y = ahead.y - mostThreatening.center.y; Avoidance.normalize (); Avoidance.scaleBy (MAX_AVOID_FORCE);  else avoidance.scaleBy (0); // anular la fuerza de evitación retorno de evitación;  función privada findMostThreateningObstacle (): Obstacle var mostThreatening: Obstacle = null; para (var i: int = 0; i < Game.instance.obstacles.length; i++)  var obstacle :Obstacle = Game.instance.obstacles[i]; var collision :Boolean = lineIntersecsCircle(ahead, ahead2, obstacle); // "position" is the character's current position if (collision && (mostThreatening == null || distance(position, obstacle) < distance(position, mostThreatening)))  mostThreatening = obstacle;   return mostThreatening; 

La fuerza de evitación debe agregarse al vector de velocidad del personaje. Como se explicó anteriormente, todas las fuerzas de dirección se pueden combinar en una sola, produciendo una fuerza que representa todo el comportamiento activo que actúa sobre el personaje..

Dependiendo del ángulo y la dirección de la fuerza de evitación, no interrumpirá otras fuerzas de dirección, como buscar o vagar. La fuerza de evitación se agrega a la velocidad del jugador como de costumbre:

 dirección = nada (); // el vector nulo, que significa "magnitud de fuerza cero" dirección = dirección + buscar (); // suponiendo que el personaje está buscando algo direction = direction + collisionAvoidance (); dirección = truncar (dirección, fuerza máxima) dirección = dirección / velocidad de la masa = truncar (velocidad + dirección, velocidad máxima) posición = posición + velocidad

Dado que todos los comportamientos de dirección se vuelven a calcular cada actualización del juego, la fuerza de evitación permanecerá activa mientras el obstáculo esté bloqueando el camino..

Tan pronto como el obstáculo no esté interceptando el adelante línea vectorial, la fuerza de evitación se volverá nula (sin efecto) o se volverá a calcular para evitar el nuevo obstáculo amenazador. El resultado es un personaje que es capaz de evitar obstáculos:


Mueve el cursor del ratón. Haga clic para mostrar fuerzas.

Mejora de la detección de colisiones

La implementación actual tiene dos problemas, ambos relacionados con la detección de colisiones. El primero sucede cuando el adelante los vectores están fuera de la esfera del obstáculo, pero el personaje está demasiado cerca (o dentro) del obstáculo.

Si eso sucede, el personaje tocará (o ingresará) el obstáculo, saltándose el proceso de evitación porque no se detectó colisión:


A veces el adelante Los vectores están fuera del obstáculo, pero el personaje está dentro..

Este problema se puede solucionar agregando un tercer vector a la verificación de colisión: el vector de posición del personaje. El uso de tres vectores mejora notablemente la detección de colisiones..

El segundo problema ocurre cuando el personaje está cerca del obstáculo, alejándose de él. A veces, la maniobra causará una colisión, aunque el personaje simplemente esté girando para mirar en otra dirección:


Las maniobras pueden provocar una colisión, aunque el personaje esté simplemente girando.

Ese problema se puede solucionar escalando el adelante Vectores de acuerdo a la velocidad actual del personaje. El código para calcular el adelante vector, por ejemplo, se cambia a:

 dynamic_length = longitud (velocidad) / MAX_VELOCITY ahead = posición + normalizar (velocidad) * dynamic_length

La variable dynamic_length oscilará entre 0 y 1. Cuando el personaje se mueve a toda velocidad, dynamic_length es 1; cuando el personaje está desacelerando o acelerando, dynamic_length es 0 o mayor (por ejemplo, 0.5).

Como consecuencia, si el personaje está maniobrando sin moverse, dynamic_length tiende a cero, produciendo un nulo adelante Vector, que no tiene colisiones..

A continuación se muestra el resultado con estas mejoras:


Mueve el cursor del ratón. Haga clic para mostrar fuerzas.

Demostración: es tiempo de zombie!

Para demostrar el comportamiento de evitación de colisión en acción, creo que una horda de zombies es el ajuste perfecto. A continuación se muestra una demostración que muestra varios zombies (con diferentes velocidades) buscando el cursor del mouse. Arte de SpicyPixel y Clint Bellanger, de OpenGameArt.


Mueve el cursor del ratón. Haga clic para mostrar fuerzas.

Conclusión

El comportamiento de evitar colisiones permite a cualquier personaje esquivar obstáculos en el entorno. Dado que todas las fuerzas de dirección se recalculan en cada actualización del juego, los personajes interactúan a la perfección con diferentes obstáculos, analizando siempre el más peligroso (el más cercano).

A pesar de que este comportamiento no es un algoritmo de búsqueda de caminos, los resultados obtenidos son bastante convincentes para los mapas llenos..