Así que he estado haciendo este juego java 2D de arriba hacia abajo en este marco llamado Greenfoot y he estado trabajando en la IA para los chicos con los que lucharás. Quiero que puedan moverse por el mundo de manera realista, así que pronto me di cuenta, entre otras cosas, que necesitaría algún tipo de búsqueda de caminos.
He hecho dos prototipos A *. Uno está basado en la cuadrícula y luego hice uno que funciona con puntos de referencia, así que ahora necesito encontrar una forma de llegar desde un "mapa" en 2D de los obstáculos / edificios a un gráfico de nodos desde el que pueda hacer un camino. La búsqueda de ruta real parece estar bien, solo mis listas abiertas y cerradas podrían usar una estructura de datos más eficiente, pero llegaré a eso si es necesario.
Tengo la intención de utilizar una malla de navegación por todas las razones descritas en esta publicación en ai-blog.net . Sin embargo, el problema al que me he enfrentado es que lo que A * piensa es que la ruta más corta desde los centros / bordes del polígono no es necesariamente la ruta más corta si viaja a través de cualquier parte del nodo. Para tener una mejor idea, puede ver la pregunta que hice en stackoverflow .
Obtuve una buena respuesta con respecto a un gráfico de visibilidad. Desde entonces compré el libro ( Geometría computacional: algoritmos y aplicaciones ) y leí más sobre el tema, sin embargo, todavía estoy a favor de una malla de navegación (ver " Gestión de la complejidad " de las notas de Amit sobre la búsqueda de caminos ). (Como nota al margen, tal vez podría usar Theta * para convertir varios puntos de referencia en una línea recta si el primero y el último no están ocultos. O cada vez que retrocedo, verifique el punto de referencia antes de la última para ver si puedo ir directamente desde que a esto)
Entonces, básicamente, lo que quiero es una malla de navegación donde una vez que lo haya sometido a un algoritmo de embudo (por ejemplo, este de Digesting Duck ) obtendré la verdadera ruta más corta, en lugar de obtener una que sea la ruta más corta siguiendo solo de nodo a nodo, pero no es el más corto, dado que puede atravesar algunos polígonos y omitir nodos / bordes.
Ah, y también quiero saber cómo sugiere almacenar la información relativa a los polígonos. Para el ejemplo del prototipo de waypoint que hice, solo tenía cada nodo como un objeto y almacené una lista de todos los otros nodos a los que podría viajar desde ese nodo, supongo que eso no funcionará con polígonos. ¿Y cómo puedo saber si un polígono es abierto / transitable o si es un objeto sólido? ¿Cómo almaceno qué nodos forman el polígono?
Finalmente, para que conste: quiero programar esto desde cero a pesar de que ya hay otras soluciones disponibles y no tengo la intención de (re) usar este código en otra cosa que no sea este juego, por lo que no importa inevitablemente será de mala calidad.
fuente
Respuestas:
Aconsejaría que, incluso si va a escribir todo su propio código, descargue Recast y cree la aplicación de muestra, ya que tiene visualizaciones que muestran la malla de navegación generada y le permite probar la búsqueda de ruta con un simple punto y haga clic interfaz. Puedes aprender mucho solo jugando con eso.
Como ya se ha dado cuenta, hay dos pasos para producir una ruta atractiva: el buscador de rutas A * y luego el posterior procesamiento posterior que incluye el enderezado de la ruta (la eliminación de cualquier giro no necesario para evitar obstáculos) y posiblemente también el adición de curvas en los puntos de inflexión.
Encontrar el camino
Has dominado la parte A *, lo cual es genial. También ha observado que A * no siempre encontrará el camino más recto. Es crucial entender que esto se debe a que A * es un algoritmo para encontrar la ruta más corta a través de un gráfico (siendo un concepto matemático donde los nodos no tienen dimensión) cuando lo aplica a una malla, debe mapear los nodos para mallar elementos de alguna manera.
Lo más obvio es navegar del punto central del polígono al punto central y basar el costo en esta distancia, pero esto tiene algunos problemas. Una es que no siempre encontrará la ruta geométricamente más corta y la segunda es que si intenta seguir la ruta que calculó allí, notará que una línea recta desde un centro al siguiente puede cruzar un polígono que no está t parte de la ruta (y podría no ser navegable en absoluto). No es una forma horrible de costar el recorrido del gráfico mientras se realiza A *, pero claramente no es adecuado para ningún propósito transversal.
La siguiente solución más simple es realizar A * de borde a borde a través de la malla. Encontrará esto más simple para pensar si imagina que en lugar de un nodo por polígono, tiene uno por borde por polígono. Por lo tanto, su ruta va desde su punto de inicio hasta el borde más cercano, se cruza justo dentro del borde del polígono adyacente y luego justo dentro del borde siguiente en el mismo polígono y así sucesivamente. Esto produce el camino más corto con más frecuencia y también tiene la ventaja de ser transitable si no desea realizar un paso de enderezado del camino.
Enderezar camino
El algoritmo utilizado en Detour (la biblioteca de navegación que acompaña a Recast) es bastante simple. Debe observar que solo enderezará la ruta dentro de los límites de los polígonos encontrados durante la búsqueda A *. Como tal, si eso no encuentra el camino más estrecho alrededor de un obstáculo, tampoco obtendrá un camino apretado después de ejecutar ese algoritmo. En la práctica, las redes de navegación producidas por la refundición tienden a tener un único polígono que puede atravesar cuando navega por un punto de estrangulamiento (el punto más cercano entre dos obstáculos) y, por lo tanto, A * siempre producirá una lista de nodos que están tan cerca del obstáculo como posible. Si está utilizando mosaicos como la malla de navegación, este no será el caso y este algoritmo muy simple insertará giros espurios.
El algoritmo de enderezamiento de ruta de desvío no es del todo complejo (O) porque cuando determina que necesita insertar un giro, lo inserta en el punto donde apretó el embudo hacia la izquierda por última vez (al girar a la izquierda y viceversa) y luego comienza a trazar los nodos desde ese punto nuevamente.
Si desea enderezar el camino fuera de los polígonos que forman parte de la ruta A *, las cosas se vuelven mucho más complejas. Deberá implementar una rutina de proyección de rayos que pueda probar si dos puntos en su navegador se pueden ver (debe tener esto de todos modos para que pueda ver si necesita usar A *). Para ello, intersecta el segmento de línea formado por origen-> destino con los bordes de conexión del polígono que contiene el origen, luego prueba los bordes de conexión del polígono que te mueve hacia adentro y así sucesivamente. Si intersecta un borde que no conecta (los llamo bordes de borde), entonces ha chocado con un obstáculo.
Luego puede realizar esta prueba de proyección de rayos cada vez que el algoritmo de canalización determine que necesita insertar un turno para ver si realmente lo hace, pero creo que tendrá que seguir realizando esa prueba en cada nodo hasta que inserte un turno (en en qué punto puede volver al algoritmo de embudo simple). Eso va a ser costoso, haciendo que el camino se enderece aproximadamente O (n ^ 2).
Representando el navmesh
Puede representar su malla como una matriz de clases de polígonos. La clase de polígono podría ser tan simple como una matriz de vértices y una matriz de referencias al polígono adyacente para cada borde si hay una. Por supuesto, probablemente pueda pensar en formas de almacenarlo de manera más compacta. Dado que un vértice generalmente es compartido por varios polígonos, es normal tener una gran variedad de vértices y luego hacer que cada polígono almacene índices en esa matriz. Dependiendo de las características de su navegador, es posible que tenga un número promedio de bordes de conexión que es solo el 50% o menos del número de bordes. En ese caso, es posible que desee almacenar un enlace a otro polígono y el índice del borde en lugar de almacenar un enlace para cada borde. También le recomiendo que almacene el índice del polígono en la matriz de polígonos de navmesh en lugar de utilizar una referencia de clase.
fuente
sqrt(x*x + y*y)
, pero no la más barata por nodox*x + y*y
.