Algoritmo dinámico de ruta para el juego de defensa de torres

16

Estoy haciendo un Tower Defense y necesito un buen algoritmo de ruta para ello.

He pensado en Dijkstra pero necesito uno que pueda ser dinámico; debe poder actualizarse cuando se elimina o agrega un borde sin un recálculo completo.

Estoy codificando en C # si ayuda.

Dani
fuente

Respuestas:

8

En general, querrá usar A *, a menos que esté buscando algo muy diferente.

John Haugeland
fuente
Si no estoy tan familiarizado con A *, ¿cómo abordaría entornos que cambian dinámicamente? ¿Volver a calcular en cada cuadro? ¿En colisiones?
Justin L.
1
En general, volvería a enrutar cada cuadro en un juego simple como este. La cuadrícula del mapa probablemente será muy pequeña y la cantidad de agentes en los cientos como máximo. Si terminó siendo un gran problema de rendimiento de alguna manera, podría optimizarlo más tarde al no reconstruir el árbol, excepto cuando sea necesario.
coderanger
66
Si es lento, mis recomendaciones: no calcules el camino para cada mafia. Probablemente, todos están tomando el mismo camino. Solo recuerdo la nueva ubicación de la torre, y la torre está en el camino actual . Incluso entonces, solo vuelve a calcular para los monstruos en puntos en el camino antes del cuadrado del bloque, no para las turbas que lo han pasado y, por lo tanto, no les importa. Y luego, para acortar el camino, puede limitarlo para encontrar el camino al cuadrado después del cuadrado bloqueado, ya que ya conoce el camino desde allí.
seanmonstar
44
En realidad, "mob" es la jerga de la industria (y el jugador MMO / MUD) para "objeto móvil".
3
De todos modos, es de larga data, data de MUD1, y lo suficientemente estándar como para haber aparecido en muchas publicaciones. en.wikipedia.org/wiki/Mob_%28computer_gaming%29
19

Necesitaba resolver un problema similar: encontrar caminos en una gran cuadrícula tipo laberinto con "costos" y barreras en constante cambio.

La cuestión es que, en el juego de defensa de la torre, el número de entidades que necesitan que se les resuelva el camino suele ser mucho mayor que el número de nodos en el gráfico. A * no es el algoritmo más apropiado para manejar esto, porque necesitará resolverlo nuevamente cada vez que se cambie algo. Bueno, es apropiado si necesita encontrar solo una ruta, pero en mi caso necesitaba poder manejar entidades que pueden aparecer en diferentes ubicaciones y cada una tiene su propia ruta.

El algoritmo de Floyd-Warshall es mucho más apropiado, aunque para mi caso escribí un algoritmo personalizado que cada vez que cambia un nodo, vuelve a calcular el costo para ese nodo de todos sus vecinos, y luego, si los vecinos han cambiado, se invoca recursivamente sobre ellos.

Entonces, al comienzo del juego, simplemente enciendo este algoritmo en todos mis nodos de "objetivo". Luego, cada vez que cambia un solo nodo (por ejemplo, no se puede pasar), simplemente lo enciendo en ese nodo y el cambio se propaga a todos los nodos que se verán afectados, y luego se detiene. Por lo tanto, no es necesario un recálculo global, y el algoritmo es completamente independiente del número de entidades que requerirán la búsqueda de rutas.

Mi algoritmo era básicamente algo como (pseudocódigo):

update_node method in Node class:
    $old <- $my_score
    find $neighbor node among all neighbors such that
        $neighbor.score + distance_to($neighbor) is minimal
    $my_score <- $neighbor.score + distance_to($neighbor)
    $next_node <- $neighbor
    if ($my_score != $old)
        for each $neighbor
            $neighbor.update_node()

Con el puntaje inicial dependiendo de si el nodo es un objetivo o algún tipo de barrera.

Roble
fuente
1
También sería fácil distribuir esto dinámicamente en tramas a medida que cambia la carga de la CPU.
Tenpn
+1 para A * no es el algoritmo correcto para usar.
Ricket
5

El algoritmo de ruta que utilicé en mi TD era anterior a la ruta A * normal debido a la cantidad de entidades que tenía en el juego. En lugar de enrutar desde la portería a los malos, me dirigí desde la portería a cada cuadro vacío en el tablero.

Esto no toma mucho tiempo, solo sigue iterando el tablero hasta que haya encontrado sus "costos", y proporciona una buena respuesta para las rutas bloqueadas (si las está haciendo). El uso del algoritmo de Floyd es rápido y amigable con la memoria caché en comparación con A *, ya que no está haciendo búsquedas dependientes de datos, solo está cargando y operando datos en flujos.

Comience con su tablero configurado en costo infinito, luego establezca el cuadrado del objetivo en costo cero, luego repita el control del tablero para ver si una celda vecina cuesta menos que el costo actual más el costo de viaje. El costo de viaje es donde pones tu heurística (en mi caso, el costo de viajar en diagonal era infinito, pero el costo de viajar a través de una torre era alto, por lo que se les permitió comer a través de torres, pero no a menos que no tuvieran elección)

Una vez que tenga su grilla de costos, puede construir rápidamente una grilla de "flujo" a partir de eso probando el gradiente de costo más pronunciado en las celdas. Esto funciona muy bien para grandes cantidades de tipos malos porque ninguno de ellos tiene que encontrar el camino, solo siguen las señales virtuales.

Otro beneficio es que de esta manera solo tienes que ejecutar esta tarea cada vez que ajustas las obstrucciones (o en mi juego, cuando un arrastramiento se come a través de una de tus torres). No cada vez que un creep / mob ingresa al campo (lo que con miles de mobs y decenas por segundo habría sido un gran dolor de cabeza).

Richard Fabian
fuente
4

Pathfinding es rápido, y en algo del tamaño de un juego de defensa de torre normal, no tendrás problemas para ejecutar un pase A * o Dijkstra completo cada vez que algo cambie. Estamos hablando mucho menos de un milisegundo para una actualización completa. Cualquier tipo de camino adaptativo termina horriblemente complicado. Simplemente hazlo de manera simple, a menos que estés haciendo la cuadrícula de defensa de torre más grande del mundo.

ZorbaTHut
fuente
1
Vale la pena señalar que Tower Defense es popular en los teléfonos móviles, donde la búsqueda de caminos no es rápida. Especialmente en dispositivos ligeramente más antiguos, como Sidekick o dispositivos Android 1.6.
seanmonstar
1
Los desarrolladores han estado utilizando algoritmos de búsqueda de rutas como A * y Dijkstra en hardware mucho menos potente durante décadas (piense en Game Boy). Cualquier teléfono lo suficientemente nuevo como para tener una pantalla adecuada para juegos no debería tener problemas, siempre que la implementación sea razonablemente eficiente. Aquí se analizan algunas optimizaciones ingeniosas: harablog.files.wordpress.com/2009/06/beyondastar.pdf
Mike Strobel
+1. Llegué a la misma conclusión cuando estaba desarrollando mi propio clon DTD. Intenté actualizar las rutas de forma incremental, pero el algoritmo se volvió demasiado complejo. Después de pasar un día en él, cambié a un resumen completo de cada cambio. Eso funcionó, y con algunos ajustes pude hacerlo lo suficientemente rápido.
finnw
2

Esto puede ser excesivo para su solución, pero piense en enrutar hacia atrás en lugar de hacia adelante.

En un juego de TD, los enemigos generalmente intentan llegar a un objetivo específico. Dirígete hacia atrás desde ese objetivo hasta el primer enemigo, luego almacena en caché ese camino. En la generación de rutas para enemigos posteriores, usa la primera ruta como punto de partida, y así sucesivamente. Si tiene múltiples puntos de entrada de enemigos o múltiples objetivos, tenga un rango de caminos pre-almacenados en caché para comenzar.

tenpn
fuente
1

La búsqueda de ruta en el punto de referencia probablemente sería la mejor para un juego de td, porque generalmente, en función del nivel, el ai sigue una ruta directa. Básicamente, establece sus nodos o puntos de referencia, luego tiene el punto "ai" hacia el waypoiny y camina hacia él, una vez que se acerca lo suficiente para que vaya al siguiente punto de referencia, enfréntelo y muévase hacia él.

Loktar
fuente
Esto solo funciona para aquellos juegos de TD en los que solo puedes colocar torretas al costado del camino, no juegos que permiten la colocación de torres en cualquier parte del tablero.
Iain
Sí, inicialmente el autor no especificó qué tipo quería, así que supuse que no era dinámico.
Loktar
1

Como alguien le preguntó, usted se dirige a entornos que cambian dinámicamente al volver a calcular la ruta cada vez que el jugador coloca o elimina una torre, y mientras tanto, simplemente almacena esa ruta en la memoria. El entorno no cambia en cada cuadro.

Tenga en cuenta que algunos juegos de TD tienen una ruta establecida y no le permiten colocar torres en ella, por lo que resuelven la búsqueda de ruta de la manera fácil: codificando una ruta y no permitiéndole bloquearla :)

Ian Schreiber
fuente
1

Una solución simple es hacer trampa.

Diseñe el mapa de antemano para asegurarse de que no haya callejones sin salida. Luego, en cada intersección, agregue un disparador que haga que el personaje elija una ruta (ejemplo: siempre gire a la izquierda, siempre gire a la derecha o al azar).

Mr Valdez
fuente
1
Presumiblemente está hablando de algo como Desktop TD donde el usuario está creando el mapa sobre la marcha. Aún así, para los enemigos "tontos" podría ser una buena solución para que puedas hacer que los enemigos con mejor IA tengan un poco de dificultad.
coderanger