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.
quelle
Antworten:
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.
quelle
sqrt(x*x + y*y)
- aber nicht die billigere pro Knotenx*x + y*y
.