A * Navigation Mesh Path Finding

15

Also habe ich dieses Top-Down-2D-Java-Spiel in diesem Framework namens Greenfoot erstellt und an der KI für die Jungs gearbeitet, gegen die Sie kämpfen werden. Ich möchte, dass sie sich realistisch auf der ganzen Welt bewegen können, und erkannte bald, dass ich unter anderem eine Art Wegfindung benötigen würde.

Ich habe zwei A * -Prototypen hergestellt. Eine ist gitterbasiert, und dann habe ich eine erstellt, die mit Wegpunkten arbeitet. Daher muss ich jetzt einen Weg finden, um von einer 2D- "Karte" der Hindernisse / Gebäude zu einem Diagramm von Knoten zu gelangen, von denen ich einen Pfad erstellen kann. Die eigentliche Pfadfindung scheint in Ordnung zu sein, nur meine offenen und geschlossenen Listen könnten eine effizientere Datenstruktur verwenden, aber ich werde darauf zurückkommen, wenn und wann ich muss.

Ich beabsichtige, aus allen in diesem Beitrag auf ai-blog.net genannten Gründen ein Navigationsnetz zu verwenden . Das Problem, mit dem ich konfrontiert wurde, ist jedoch, dass A * den kürzesten Weg von den Polygonzentren / -kanten als nicht unbedingt den kürzesten Weg ansieht, wenn Sie durch einen Teil des Knotens fahren. Um eine bessere Vorstellung zu bekommen, können Sie die Frage sehen, die ich beim Stackoverflow gestellt habe .

Ich habe eine gute Antwort bezüglich eines Sichtbarkeitsgraphen erhalten. Ich habe das Buch ( Computational Geometry: Algorithms and Applications ) gekauft und lese weiter im Thema, bin jedoch immer noch für ein Navigationsnetz (siehe " Managing Complexity " in Amits Notes zum Path-Finding ). (Als Randnotiz: Vielleicht könnte ich Theta * verwenden, um mehrere Wegpunkte in eine gerade Linie umzuwandeln, wenn der erste und der letzte nicht verdeckt sind. Oder jedes Mal, wenn ich zum vorletzten Wegpunkt zurückkehre, überprüfe ich, ob ich direkt von dort ausgehen kann das dazu)

Ich möchte also im Grunde genommen ein Navigationsnetz, in dem ich, sobald ich es durch einen Trichteralgorithmus geführt habe (z. B. dieses von Digesting Duck ), den kürzesten Pfad erhalte, anstatt nur den kürzesten Pfad von Knoten zu Knoten. aber nicht die kürzeste, vorausgesetzt, Sie können einige Polygone durchlaufen und Knoten / Kanten überspringen.

Oh, und ich möchte auch wissen, wie Sie die Informationen zu den Polygonen speichern möchten. Für das von mir erstellte Wegpunkt-Prototyp-Beispiel hatte ich gerade jeden Knoten als Objekt und speicherte eine Liste aller anderen Knoten, zu denen Sie von diesem Knoten aus reisen konnten. und wie kann ich feststellen, ob ein Polygon offen / durchlässig ist oder ob es sich um ein festes Objekt handelt? Wie speichere ich, welche Knoten das Polygon bilden?

Zum Schluss noch ein Hinweis: Ich möchte dies von Grund auf selbst programmieren, obwohl bereits andere Lösungen zur Verfügung stehen, und ich beabsichtige nicht, diesen Code in etwas anderem als diesem Spiel (wieder) zu verwenden, es spielt also keine Rolle es wird unvermeidlich schlechte Qualität sein.

Angrydust
quelle
Ich bin nicht sicher, aber diese Links können helfen: gamedev.stackexchange.com/questions/1327/… gamedev.stackexchange.com/questions/8087/… es gab auch eine andere Frage zur Wegfindung, die ich momentan nicht finden kann , der ein Kopfgeld bekam und eine sehr gute Antwort hatte.
Ali1S232
Ja, im zweiten Link können Sie sehen, dass mein Hauptanliegen darin besteht, dass der A * -Algorithmus den Pfad um den unteren Rand als den kürzesten unter Verwendung von Kantenmittelpunkten angibt, aber der Pfad um den oberen Rand des Hindernisses ist tatsächlich der kürzeste. Ich möchte wissen, wie ich A * bekommen kann, um mir den Pfad um die Spitze zu geben, den ich dann begradigen werde (zum Beispiel durch einen Trichteralgorithmus), um den wirklich kürzesten Pfad zu erhalten, wo, als ob es mir den Pfad um die Unterseite gibt Auch wenn ich es gerade mache, macht es immer noch einen Umweg.
Angrydust
Eigentlich habe ich gerade den Artikel in gamedev.stackexchange.com/questions/8087/… gelesen und es scheint zu funktionieren, indem ich eine Route mit A * finde , dann die wahren Kosten mit einem modifizierten Trichteralgorithmus berechne und dann eine andere Route finde und berechne wieder wahre Kosten und zu sehen, ob es kürzer als die andere ist. Es wiederholt sich, bis es weiß, dass es den kürzesten Weg gefunden hat. Dies löst zwar mein Problem, scheint jedoch ziemlich langsam zu sein, da Sie sowohl das Richten als auch das Auffinden des Pfades wiederholen, was ziemlich kostspielig wäre.
Angrydust
Polygonspeicher: Speichern Sie nur die sichtbaren Polygone - oder ordnen Sie jedem Polygon ein Tag zu (denken Sie daran, dass jedes Polygon eine kreisförmige Liste von Eckpunkten sein muss). Ähnliches gilt für Knoten, bei denen Sie die ID des Polygons speichern können, von dem sie stammen. Ich sollte Ihnen jedoch nicht sagen müssen, dass es sich um eine elementare Datenspeicherung handelt. Warum interessiert Sie der kürzeste Weg? Dein Spiel kann sehr langsam laufen oder du hast leicht falsche Wege: wähle einen. Um den wirklich kürzesten Pfad zu erhalten, ist eine Suche in voller Breite (zumindest über einem Knotendiagramm) erforderlich .
Jonathan Dickinson
@JonathanDickinson Ich weiß , dass ich nicht einen Weg brauchen , die zu 100% genau auf das letzte Pixel sein wird, und ich weiß , dass ich A verwenden können * Pfade erzeugen , die höchstens sein p zulässig. Das Ding geht den weiten Weg um ein Hindernis so deutlich, wie es in meiner vorherigen Frage zum Stack Overflow oder in gamedev.stackexchange.com/questions/8087/… einfach zu viel ist. Ich kann meine KI nicht so um ein Hindernis herumlaufen lassen!
Angrydust

Antworten:

14

Ich würde empfehlen, dass Sie, selbst wenn Sie Ihren gesamten Code selbst schreiben, Recast herunterladen und die Beispielanwendung erstellen, da sie Visualisierungen enthält, die das generierte Navmesh anzeigen, und Sie die Pfadfindung mit einem einfachen Mausklick testen können Schnittstelle. Sie können viel lernen, indem Sie einfach damit spielen.

Wie Sie bereits erkannt haben, sind zwei Schritte erforderlich, um einen gut aussehenden Pfad zu erstellen - der A * -Pfadfinder und die anschließende Nachbearbeitung, die das Begradigen des Pfads (das Eliminieren von Abbiegungen, die nicht zur Vermeidung von Hindernissen erforderlich sind) und möglicherweise auch die Hinzufügen von Kurven an den Wendepunkten.

Wegfindung

Du hast den A * Part gemeistert, was großartig ist. Sie haben auch festgestellt, dass A * nicht immer den geradesten Weg findet. Es ist wichtig zu verstehen, dass dies darauf zurückzuführen ist, dass A * ein Algorithmus ist, um den kürzesten Weg durch ein Diagramm zu finden (ein mathematisches Konzept, bei dem Knoten dimensionslos sind), wenn Sie es auf ein Netz anwenden, müssen Sie Knoten irgendwie auf Netzelemente abbilden.

Am naheliegendsten ist es, vom Mittelpunkt des Polygons zum Mittelpunkt zu navigieren und die Kosten anhand dieser Entfernung zu berechnen. Dies hat jedoch einige Probleme. Erstens wird nicht immer der geometrisch kürzeste Pfad gefunden, und zweitens wird beim Versuch, dem dort berechneten Pfad zu folgen, festgestellt, dass eine gerade Linie von einem Zentrum zum nächsten ein Polygon kreuzen kann, das nicht vorhanden ist Teil des Pfades (und möglicherweise überhaupt nicht navigierbar). Es ist keine schreckliche Methode, den Diagrammdurchlauf zu kosten, während A * ausgeführt wird, aber es ist eindeutig nicht für einen Durchlaufzweck geeignet.

Die nächst einfachere Lösung besteht darin, A * von Kante zu Kante durch das Netz zu führen. Dies ist einfacher zu überlegen, wenn Sie sich vorstellen, dass anstelle eines Knotens pro Polygon ein Rand pro Polygon vorhanden ist. Der Pfad verläuft also vom Startpunkt bis zur nächsten Kante, überquert die Kante des angrenzenden Polygons und verläuft dann bis zur nächsten Kante desselben Polygons und so weiter. Dies erzeugt häufiger den kürzesten Weg und hat auch den Vorteil, dass Sie ihn überqueren können, wenn Sie keinen Schritt zum Begradigen des Weges ausführen möchten.

Weg begradigen

Der in Detour (der mit Recast gelieferten Navigationsbibliothek) verwendete Algorithmus ist ziemlich einfach. Sie sollten beachten, dass der Pfad nur innerhalb der Grenzen der bei der A * -Suche gefundenen Polygone gerade wird. Wenn das nicht den engsten Pfad um ein Hindernis herum findet, erhalten Sie auch nach dem Ausführen dieses Algorithmus keinen engsten Pfad. In der Praxis haben die durch die Neufassung erstellten Navigationsgitter in der Regel ein einziges Polygon, durch das Sie beim Navigieren eines Drosselpunkts (dem nächstgelegenen Punkt zwischen zwei Hindernissen) navigieren können. Daher wird mit A * immer eine Liste von Knoten erstellt, die sich in der Nähe des Hindernisses befinden möglich. Wenn Sie Kacheln als Navmesh verwenden, ist dies nicht der Fall und dieser sehr einfache Algorithmus fügt falsche Kurven ein.

Der Algorithmus zum Begradigen des Umweges ist nicht sehr komplex, da er, wenn er feststellt, dass eine Kurve eingefügt werden muss, diese an der Stelle einfügt, an der er den Trichter zuletzt nach links festgezogen hat (beim Drehen nach links und umgekehrt) und Anschließend werden die Knoten ab diesem Punkt erneut durchlaufen.

Wenn Sie den Pfad außerhalb der Polygone begradigen möchten, die Teil der A * -Route sind, werden die Dinge viel komplexer. Sie müssen eine Raycast-Routine implementieren, mit der getestet werden kann, ob zwei Punkte in Ihrem Navmesh einander sehen können (Sie sollten dies trotzdem haben, damit Sie sehen können, ob Sie überhaupt A * verwenden müssen). Sie tun dies, indem Sie das durch origin-> target gebildete Liniensegment mit den Verbindungskanten des Polygons schneiden, das den Ursprung enthält, und dann die Verbindungskanten des Polygons testen, in das Sie sich bewegen, und so weiter. Wenn Sie eine nicht verbindende Kante schneiden (ich nenne sie Randkanten), haben Sie ein Hindernis getroffen.

Sie können diesen Raycast - Test dann immer dann durchführen, wenn der Trichteralgorithmus feststellt, dass eine Kurve eingefügt werden muss, um zu überprüfen, ob dies tatsächlich der Fall ist welchen Punkt Sie auf den einfachen Trichteralgorithmus zurückgreifen können). Das wird teuer, da sich der Pfad ungefähr auf 0 (n ^ 2) begradigt.

Stellvertretend für das Navmesh

Sie können Ihr Netz als Array von Polygonklassen darstellen. Die Polygonklasse kann so einfach sein wie ein Array von Eckpunkten und ein Array von Verweisen auf das benachbarte Polygon für jede Kante, falls es eine gibt. Natürlich können Sie sich wahrscheinlich Möglichkeiten überlegen, wie Sie das kompakter speichern können. Da ein Scheitelpunkt normalerweise von mehreren Polygonen gemeinsam genutzt wird, ist es normal, dass ein großes Array von Scheitelpunkten vorhanden ist und dann jedes Polygon Indizes in diesem Array speichert. Abhängig von den Eigenschaften Ihres Navigationsgeräts kann die durchschnittliche Anzahl der Verbindungskanten nur 50% oder weniger der Anzahl der Kanten betragen. In diesem Fall möchten Sie möglicherweise einen Link zu einem anderen Polygon und den Index der Kante speichern, anstatt für jede Kante einen Link zu speichern. Außerdem empfehle ich, den Index des Polygons im Polygon-Array von navmesh zu speichern, anstatt eine Klassenreferenz zu verwenden.

U62
quelle
Ich hatte nur ein kurzes dies lesen, aber ich verstehe Sie nie das Quadrat der Entfernung (oder nicht Quadratwurzel es) für A * verwenden sollte: theory.stanford.edu/~amitp/GameProgramming/...
angrydust
Ich bin nicht wirklich besorgt darüber, wie man tatsächlich den Weg begradigt, was mich betrifft, ist das, was Sie sagen: "Sie sollten beachten, dass es den Weg nur innerhalb der Grenzen der Polygone begradigt, die während der A * Suche gefunden wurden Englisch: emagazine.credit-suisse.com/app/art...1007 & lang = en Findet dieser nicht den engsten Pfad um ein Hindernis herum, erhalten Sie auch nach dem Ausführen dieses Algorithmus keinen engsten Pfad.
Angrydust
Ich möchte ein Navigationsnetz haben, in dem A * immer den Weg findet, der nach dem Geraderichten am kürzesten ist, unabhängig von den Fahrtkosten über Eckpunkte / Mittelpunkt. Ich weiß, dass dies mit Sichtbarkeitsgraphen möglich ist, möchte aber ein Navigationsnetz verwenden, da es viele andere Vorteile bietet und die Komplexität eines Sichtbarkeitsgraphen sehr schnell zunehmen kann: theory.stanford.edu/~amitp/GameProgramming/…
Angrydust
@theguywholikeslinux können Sie die euklidische Entfernung verwenden sqrt(x*x + y*y)- aber nicht die billigere pro Knoten x*x + y*y.
Jonathan Dickinson
@ Jonathan Dickinson Ich weiß, daher mein Punkt.
Angrydust