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.
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..
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..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:
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:
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:
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:
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:
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..
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:
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:
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:
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:
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.
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..