A * Algoritmo para juegos de rol tácticos?

15

Estoy jugando con escribir un RPG táctico realmente pobre en C ++. Hasta ahora tengo un mapa de mosaico 2D y acabo de hacer funcionar el algoritmo A * basado en el pseudocódigo de la wikipedia .

Pero los juegos de rol tácticos reales no solo encuentran el mejor camino en un avión plano y se mueven allí. Normalmente tienen rangos de movimiento limitados y deben subir o bajar. Si alguna vez jugaste a Final Fantasy Tactics, estas se verían afectadas por las estadísticas Move and Jump. Aquí es donde me pierdo. ¿Cómo modifico el algoritmo A * para que encuentre la mejor ruta hacia un objetivo, pero la ruta solo tiene unas pocas fichas? ¿Cómo debo tener en cuenta las diferencias de altura y las estadísticas de salto? ¿Cómo implemento saltar sobre una brecha?

Si ayuda, en este momento mi mapa está representado por un Vector de objetos de mosaico. Cada mosaico tiene punteros a los mosaicos Norte, Sur, Este y Oeste, que se configuran en nullptr si no existe ningún mosaico allí, como a lo largo del borde del mapa o si un mosaico se configura como no transitable.

usuario137
fuente
55
No sé por qué el rango de movimiento es un problema. Encuentra el camino más corto y luego mueve los cuadrados de 'velocidad' a lo largo de ese camino.
Mooing Duck

Respuestas:

33

La escalada y las brechas son funciones de costo diferentes. Para una unidad que puede saltar la brecha tiene un costo normal (?), Mientras que para una unidad que no salta tiene un costo arbitrariamente alto. Los costos de escalada adicionales, al igual que el terreno difícil, etc. El algoritmo A * es capaz de manejar funciones de costos, por lo que si su implementación aún no lo está haciendo, solo busque en Google cómo implementar A * con una función de costos.

Dicho esto, sin embargo, no creo que A * sea un enfoque particularmente bueno para un juego de rol táctico. O más exactamente, no es una historia completa. No quieres que tus unidades se equivoquen ciegamente hacia su objetivo, quieres que se posicionen para explotar la cobertura, el terreno elevado, lo que sea, mientras avanzan hacia el objetivo final y buscan flanquear a los oponentes, etc. Por lo tanto, el valor táctico del punto final de cada movimiento es de gran importancia, no solo qué tan cerca está el objetivo. Esto requiere una resolución de problemas más profunda que la simple búsqueda de caminos.

Jack Aidley
fuente
10
Buenos puntos sobre el 'posicionamiento táctico', pero esas decisiones podrían aplicarse a un nivel más alto que la orientación básica. Por otro lado, aplicar costos a los nodos en el algoritmo de búsqueda de ruta generado por algún tipo de analizador táctico podría ser una buena opción. Por ejemplo, si el enemigo tiene una línea de visión cruzada con el terreno, haga que los nodos en ese terreno tengan un costo muy alto.
DrMcCleod
1
@DrMcCleod: De hecho, y eso es lo que quise decir con "O más exactamente, no es una historia completa". Ciertamente podría estar usando A * u otro algoritmo para hacer parte del procesamiento, sin embargo, creo que evitaría enfoques como tratar de evitar nodos vigilados a través del costo de movimiento, ya que moverse a través del terreno donde una unidad podría estar bajo fuego se maneja mejor como un cálculo de riesgo / recompensa, IMO.
Jack Aidley
13

Cuando desee todas las opciones de movimiento posibles de una unidad, use el Algoritmo de Dijkstra .

La diferencia entre A * y Dijkstra es que Dijkstra le ofrece todas las rutas más cortas posibles que se pueden lograr con un costo dado, y si ninguna de ellas llega a su destino todavía, aumenta el costo en una y continúa. A *, por otro lado, prefiere calcular primero aquellas rutas que tienen una buena oportunidad de llegar al destino.

Entonces, cuando solo desea la ruta más corta desde el punto A al punto B, entonces A * es una buena opción. Pero si desea todas las opciones de movimiento posibles y el camino más corto para cada una de ellas, entonces Dijkstra es exactamente lo que desea.

Todo lo que necesita hacer es ejecutar el algoritmo de Dijksta sin un nodo de destino específico, pero con un costo máximo que no debe superarse (el rango de movimiento de la unidad). Cuando viajar a un nodo excedería el costo máximo, no lo visite. Cuando el algoritmo termina debido a la falta de bordes no visitados, cada nodo en el conjunto visitado es un posible destino, y los marcadores de nodo anteriores de los nodos forman una lista vinculada que representa la ruta de regreso al nodo inicial.

Con respecto a los saltos: pueden representarse como otra ventaja en A * y Dijkstra. Pueden tener el mismo costo que atravesar un borde regular o uno diferente. También puede pasar un parámetro "jump_height" al algoritmo que le dice al algoritmo que ignore los bordes de salto que exceden una altura dada.

Philipp
fuente
99
Vale la pena mencionar aquí que en A*realidad es solo una generalización de Dijkstra, por lo que si comprende uno, no debería ser demasiado difícil de entender el otro.
Cubic
8
De hecho, si tiene una heurística que solo devuelve 0 en su algoritmo A *, ¡felicidades! Acaba de escribir el algoritmo de Dijkstra.
Yann
99
"Dijkstra le ofrece todas las rutas más cortas posibles que se pueden lograr con un costo dado, y si ninguna de ellas llega a su destino todavía, aumenta el costo en uno y continúa" . No es así como funciona ni lo que genera. En realidad, es solo una generalización de búsqueda de amplitud a gráficos ponderados. Encuentra un solo camino más corto. A * es solo una generalización de eso, para cuando tienes una heurística de distancia disponible.
BlueRaja - Danny Pflughoeft
1
No estoy seguro de por qué esto es tan votado. Desde un punto de vista pragmático, Dijkstra es obsoleto. Se enseña en CS por razones educativas e históricas. Incluso A * es obsoleto para un trabajo serio; Google Maps ciertamente no lo usa. Estaría viendo las variantes de ArcGraph hoy en día.
MSalters
2
@MSalters Dijkstra y A * son algoritmos perfectamente adecuados para problemas simples como los juegos de rol tácticos. Hay un rango muy estrecho de movimientos válidos (mosaicos) y una cantidad muy limitada de formas de moverse a través de dichos mosaicos (ortogonales, a veces diagonales) y generalmente una ruta máxima bastante corta: SQRT (ArenaWidth² * ArenaHeight²). Computacionalmente, la diferencia es insignificante en una máquina moderna para lo que probablemente sean valores bastante pequeños, entonces ¿por qué molestarse con una implementación más compleja cuando una simple es suficiente para los propósitos descritos aquí?
Valthek
2

Las otras respuestas tienen buena información, así que asegúrese de leerlas.

Sin embargo, para responder a su pregunta: según el pseudocódigo al que se vinculó, tiene algún tipo de función heuristic_cost_estimateen la que calcula el costo de tileA a tileB (suponiendo que sean adyacentes). En lugar de usar un plano (1) para ese costo, tendrías que ajustarlo para incluir las estadísticas de mosaico y estadísticas de unidad, y posiblemente estadísticas de borde.

Por ejemplo:

if (edge == JUMP && !unit.canJump()) 
    return INF;
if (tile.Type == Forest && unit.moveType == HORSE) 
    return 2;
//Other cases here
//-----
else 
    return 1;

Esto te dará tu camino. Luego, simplemente movería la unidad a lo largo de su camino mientras consumía puntos de movimiento y los detendría cuando quedara Puntos <edgeCost. Tenga en cuenta que esto puede no ser completamente óptimo si termina con puntos restantes = 1, pero debería ser lo suficientemente bueno para un RPG de práctica. ¡En realidad, querrías más táctica, como señaló Jack Aidley!

Desafío:
si desea avanzar más, probablemente desee usar Djikstras como se sugiere para encontrar todos los espacios dentro de la distancia X, entonces desea evaluar cada espacio en esa lista para un "mejor" lugar, basado en la cercanía al objetivo, defensa poder, si puedes ser atacado o no desde esa posición, etc. Según esa información, elegirías un mosaico y luego te moverías allí siguiendo el camino que acabas de calcular usando Djikstras.

Marte
fuente
1

La escalada y las brechas son bastante triviales ya que solo modifican el costo. Pathfinding (y la mayoría de la IA táctica) se trata de resumir el costo en todos los nodos que se visitarán y minimizar eso. Un acantilado intransitable tendrá un costo infinito (muy, muy alto), las pendientes tendrán un costo más alto de lo normal, etc.

Esto, sin embargo, encuentra el camino óptimo a nivel mundial que no es la mejor solución porque los adversarios reales generalmente no encuentran el camino óptimo. Es muy poco realista, a veces hasta un punto que es obvio para el jugador, y molesto (especialmente cuando la IA como tal es básicamente invencible porque también elige lo óptimo).

Las buenas simulaciones deliberadamente no encuentran el mejor camino. Un algoritmo mucho mejor podría ser encontrar rutas jerárquicas: si no, dibujando una línea recta en el mapa y tomando de 4 a 5 puntos de ruta, busque rutas de un punto de ruta al siguiente, considerando solo los pesos de nodos que están tan lejos conocido, y establecer todos los demás pesos de nodo a "indiferente". Alternativamente, puede ejecutar A * en una cuadrícula más gruesa primero, y luego buscar ruta desde un nodo grande al siguiente (pero supongo que dibujar una línea en el mapa también está bien).

Esto es mucho más realista (y también consume una fracción de la potencia de procesamiento porque el gráfico es mucho más pequeño). Sí, puede significar que una unidad se mueve hacia un acantilado solo para descubrir que no puede cruzar. Eso está bien, también les sucede a los verdaderos adversarios. La próxima vez, no volverá a suceder (porque ahora se conoce el costo infinito).

Damon
fuente
1
Eso sí, ese "camino jerárquico simple" puede parecer bastante estúpido. Obtienes unidades que caminan directamente hacia una cresta de montaña, solo para descubrir que el camino está bloqueado. Luego se dirigen a través del paso de montaña al siguiente punto de referencia, y desde allí a su destino, incluso si ese último punto de referencia estaba muy desviado para ellos. El preprocesamiento habría identificado el paso de montaña en la delantera y señalado por allí. Pero incluso si no hace eso, una vez que esté demasiado alejado del curso planificado, debe volver a planificar el resto.
MSalters
@MSalters: Eso puede suceder con el método "dibujar una línea" incluso después del primer intento, sí. Es poco probable que ocurra más de una vez con el método jerárquico de cuadrícula gruesa (que, por ejemplo, toma el promedio, la mediana o incluso el valor de costo máximo de los nodos dentro). Es casi exactamente cómo jugaría un adversario humano: evita los grandes obstáculos que conoces o puedes ver desde lejos como una cadena de montañas, y de lo contrario planifica una ruta gruesa en su mayoría recta y muerde tu camino. A menos que sepa que hay una montaña, camina directamente hacia ella.
Damon