Un * algorithme pour les RPG tactiques?

15

Je déconne avec l'écriture d'un RPG tactique vraiment pauvre en C ++. Jusqu'à présent, j'ai une carte de tuiles 2D et je viens de faire fonctionner l'algorithme A * basé sur le pseudocode dans le wikipedia .

Mais les vrais RPG tactiques ne se contentent pas de trouver le meilleur chemin sur un plan plat et de s'y déplacer. Ils ont généralement des gammes de mouvement limitées et doivent monter ou descendre. Si vous avez déjà joué à Final Fantasy Tactics, ceux-ci seront affectés par les statistiques de déplacement et de saut. C'est là que je me perds. Comment puis-je modifier l'algorithme A * afin qu'il trouve le meilleur chemin vers une cible, mais que le chemin ne soit long que de nombreuses tuiles? Comment dois-je prendre en compte les différences de hauteur et les statistiques de saut? Comment mettre en œuvre le saut par-dessus un écart?

Si cela peut aider, ma carte est actuellement représentée par un vecteur d'objets Tile. Chaque tuile a des pointeurs vers la tuile Nord, Sud, Est et Ouest, qui sont définis sur un nullptr s'il n'y a pas de tuile là-bas, comme le long du bord de la carte ou si une tuile est définie sur non-passable.

user137
la source
5
Je ne sais pas pourquoi la portée de déplacement est un problème. Trouvez le chemin le plus court, puis après cela, déplacez les carrés de «vitesse» le long de ce chemin.
Mooing Duck du

Réponses:

33

L'escalade et les écarts ne sont que des fonctions de coût différentes. Pour une unité qui peut sauter l'écart a un coût normal (?), Tandis que pour une unité non sauteuse, elle a un coût arbitrairement élevé. L'escalade coûte plus cher, tout comme les terrains difficiles, etc. L'algorithme A * est bien capable de gérer les fonctions de coût, donc si votre implémentation ne le fait pas déjà, il suffit de google pour savoir comment implémenter A * avec une fonction de coût.

Cela dit, je ne pense pas que A * soit une approche particulièrement bonne pour un RPG tactique. Ou plus précisément, ce n'est pas une histoire complète. Vous ne voulez pas que vos unités beuglent aveuglément vers leur objectif, vous voulez qu'elles se positionnent pour exploiter la couverture, les hauteurs, peu importe, tout en progressant vers le but ultime et en cherchant à flanquer les adversaires et ainsi de suite. Ainsi, la valeur tactique du point final de chaque mouvement est d'une importance capitale, pas seulement la proximité de l'objectif. Cela nécessite une résolution des problèmes plus approfondie que la simple recherche de chemin.

Jack Aidley
la source
10
De bons points sur le «positionnement tactique», mais ces décisions pourraient être appliquées à un niveau supérieur à l'orientation de base. D'un autre côté, appliquer des coûts aux nœuds de l'algorithme de recherche de chemin qui sont générés par une sorte d'analyseur tactique pourrait être une bonne option. Par exemple, si l'ennemi a une ligne de vue sur le terrain, les nœuds sur ce terrain ont un coût très élevé.
DrMcCleod
1
@DrMcCleod: En effet, et c'est ce que je voulais dire par "Ou plus précisément, ce n'est pas une histoire complète". Vous pourriez certainement utiliser A * ou un autre algorithme pour effectuer une partie du traitement, mais je pense que j'éviterais des approches telles que tenter d'éviter les nœuds surveillés par le coût de mouvement, car se déplacer sur un terrain où une unité pourrait être touchée est mieux géré comme calcul du risque / rendement, OMI.
Jack Aidley
13

Lorsque vous voulez toutes les options de mouvement possibles d'une unité, utilisez l'algorithme de Dijkstra .

La différence entre A * et Dijkstra est que Dijkstra vous donne tous les itinéraires les plus courts possibles réalisables avec un coût donné, et si aucun d'entre eux n'atteint encore votre destination, il augmente le coût d'un et continue. A *, en revanche, préfère calculer d'abord les itinéraires qui ont de bonnes chances d'atteindre la destination.

Donc, lorsque vous voulez simplement le chemin le plus court du point A au point B, A * est un bon choix. Mais si vous voulez toutes les options de mouvement possibles et le chemin le plus court vers chacune d'elles, alors Dijkstra est exactement ce que vous voulez.

Tout ce que vous devez faire est d'exécuter l'algorithme de Dijksta sans nœud de destination spécifique, mais avec un coût maximum qui ne doit pas être dépassé (la plage de mouvement de l'unité). Lorsque le déplacement vers un nœud dépasse le coût maximum, ne le visitez pas. Lorsque l'algorithme se termine en raison du manque de bords non visités, chaque nœud de l'ensemble visité est une destination possible, et les marqueurs de nœud précédents des nœuds forment une liste liée représentant le chemin de retour au nœud initial.

En ce qui concerne les sauts: ceux-ci peuvent être représentés comme un autre avantage à la fois dans A * et Dijkstra. Ils peuvent avoir le même coût que la traversée d'un bord régulier ou différent. Vous pouvez également passer un paramètre "jump_height" à l'algorithme qui lui dit d'ignorer les sauts qui dépassent une hauteur donnée.

Philipp
la source
9
Il vaut la peine de mentionner ici que ce A*n'est vraiment qu'une généralisation de Dijkstra, donc si vous en comprenez un, il ne devrait pas être trop difficile de comprendre l'autre.
Cubic
8
En effet, si vous avez une heuristique qui ne renvoie que 0 dans votre algorithme A *, félicitations! Vous venez d'écrire l'algorithme de Dijkstra.
Yann
9
"Dijkstra vous donne tous les itinéraires les plus courts possibles réalisables avec un coût donné, et si aucun d'entre eux n'atteint encore votre destination, il augmente le coût d'un et continue" - Ce n'est ni comment cela fonctionne ni ce qu'il génère. Il s'agit en fait d'une simple généralisation de la recherche en largeur aux graphiques pondérés. Il trouve un seul chemin le plus court. Un * n'est qu'une généralisation de cela, quand vous avez une heuristique de distance disponible.
BlueRaja - Danny Pflughoeft
1
Je ne sais pas pourquoi cela est si voté. D'un point de vue pragmatique, Dijkstra est obsolète. Il est enseigné en CS pour des raisons éducatives et historiques. Même A * est obsolète pour un travail sérieux; Google Maps ne l'utilise certainement pas. Vous examineriez les variantes d'ArcGraph de nos jours.
MSalters
2
@MSalters Dijkstra et A * sont des algorithmes parfaitement fins pour des problèmes simples tels que les RPG tactiques. Il existe une gamme très étroite de mouvements valides (tuiles) et un nombre très limité de façons de se déplacer à travers ces tuiles (orthagonales, parfois diagonales) et généralement un chemin maximum assez court: SQRT (ArenaWidth² * ArenaHeight²). Sur le plan informatique, la différence est négligeable sur une machine moderne pour ce qui est probablement des valeurs assez petites, alors pourquoi s'embêter avec une implémentation plus complexe alors qu'une simple est suffisante pour les objectifs décrits ici?
Valthek
2

Les autres réponses contiennent de bonnes informations, alors assurez-vous de les lire.

Cependant, pour répondre à votre question: en fonction du pseudocode auquel vous avez lié, vous avez une sorte de fonction heuristic_cost_estimateoù vous calculez le coût de tileA à tileB (en supposant qu'ils sont adjacents). Au lieu d'utiliser un flat (1) pour ce coût, vous devrez l'ajuster pour inclure les statistiques des tuiles et des unités, et éventuellement des statistiques de bord.

Par exemple:

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

Cela vous donnera votre chemin. Ensuite, vous déplaceriez simplement l'unité le long de son chemin tout en consommant des points de mouvement et les arrêteriez lorsque les points restants <edgeCost. Notez que cela peut ne pas être complètement optimal si vous vous retrouvez avec des points restants = 1, mais cela devrait être assez bon pour un RPG d'entraînement. En réalité, vous voudriez plus tactique, comme l'a souligné Jack Aidley!

Défi:
Si vous voulez devenir plus avancé, vous voulez probablement utiliser Djikstras comme suggéré pour trouver tous les espaces à une distance X, alors vous voulez évaluer chaque espace de cette liste pour un "meilleur" endroit, basé sur la proximité de la cible, la défense pouvoir, que vous puissiez ou non être attaqué à partir de cette position, etc. Sur la base de ces informations, vous choisiriez une tuile, puis vous y déplacer en suivant le chemin que vous venez de calculer en utilisant Djikstras.

Mars
la source
1

L'escalade et les lacunes sont assez triviales car elles ne modifient que le coût. La recherche de chemin (et la plupart de l'IA tactique) consiste à résumer le coût sur tous les nœuds à visiter et à le minimiser. Une falaise infranchissable aura un coût infini (très, très élevé), les pentes auront un coût plus élevé que la normale, etc.

Ceci, cependant, trouve le chemin globalement optimal qui n'est pas la meilleure solution car les vrais adversaires ne trouvent généralement pas le chemin optimal. C'est très irréaliste, parfois à un point qui est évident pour le joueur, et ennuyeux (surtout lorsque l'IA en tant que telle est fondamentalement invincible car elle aussi choisit l'optimum).

Les bonnes simulations ne trouvent délibérément pas le meilleur chemin. Un meilleur algorithme pourrait être de faire une recherche de chemin hiérarchique - si rien d'autre, en traçant une ligne droite sur la carte, et en prenant 4-5 points de cheminement, puis la recherche de chemin d'un point de cheminement au suivant, en ne considérant que les poids des nœuds qui sont jusqu'à présent connus et en définissant tous les autres poids de noeud sur "indifférents". Alternativement, vous pouvez d'abord exécuter A * sur une grille plus grossière, puis rechercher un chemin d'un grand nœud au suivant (mais je suppose que tracer une ligne sur la carte est très bien aussi).

Ceci est beaucoup plus réaliste (et consomme également une fraction de la puissance de traitement car le graphique est beaucoup plus petit). Oui, cela peut signifier qu'une unité se déplace vers une falaise uniquement pour découvrir qu'elle ne peut pas traverser. Ça va, ça arrive aussi à de vrais adversaires. La prochaine fois, cela ne se reproduira plus (car maintenant le coût infini est connu).

Damon
la source
1
Rappelez-vous, que «la recherche de chemin hiérarchique simple» peut sembler assez stupide. Vous obtenez des unités marchant directement vers une crête de montagne, seulement pour y découvrir que le chemin est bloqué. Ils traversent ensuite le col vers le prochain point de cheminement, et de là jusqu'à leur destination - même si ce dernier point de cheminement était bien loin pour eux. Un prétraitement aurait permis d'identifier le col de montagne à l'avant et de s'y diriger. Mais même si vous ne le faites pas, une fois que vous êtes trop loin du parcours prévu, vous devez replanifier le reste.
MSalters
@MSalters: Cela peut arriver avec la méthode "tracer une ligne" même après la première tentative, oui. Il est peu probable que cela se produise plus d'une fois avec la méthode hiérarchique à grille grossière (qui, par exemple, prend la moyenne, ou la médiane, ou même la valeur de coût maximale des nœuds à l'intérieur). C'est à peu près exactement comment un adversaire humain jouerait - évitez les gros obstacles que vous connaissez ou que vous pouvez voir de loin comme une chaîne de montagnes, et sinon planifiez un itinéraire grossier principalement droit et mordez votre chemin. À moins que vous ne sachiez qu'il y a une montagne, vous marchez directement vers elle.
Damon