Eine * gitterbasierte Pfadfindung funktioniert gut für Spiele, bei denen sich die Charaktere entlang der X- und Y-Achse frei bewegen können. Das Problem bei der Anwendung auf Plattformfahrer ist, dass die Bewegung auf der y-Achse aufgrund simulierter Gravitationskräfte stark eingeschränkt ist.
In diesem Lernprogramm werde ich einen umfassenden Überblick darüber geben, wie ein Standard-A * -Algorithmus für Plattformspieler modifiziert werden kann, indem diese Schwerkraftbeschränkungen simuliert werden. (Im nächsten Teil gehe ich durch die eigentliche Kodierung des Algorithmus.) Der angepasste Algorithmus könnte verwendet werden, um einen KI-Charakter zu erstellen, der dem Spieler folgt, oder um dem Spieler beispielsweise eine Route zu seinem Ziel zu zeigen.
Ich gehe davon aus, dass Sie mit A * pathfinding bereits vertraut sind, aber wenn Sie eine Einführung benötigen, ist Patrick Lesters A * pathfinding for Beginners ausgezeichnet.
Sie können die Unity-Demo oder die WebGL-Version (64 MB) abspielen, um das Endergebnis in Aktion zu sehen. Benutzen WASD um den Charakter zu bewegen, links Klick an einer Stelle, um einen Pfad zu finden, dem Sie folgen können, und Rechtsklick eine Zelle, die den Boden an diesem Punkt umschaltet.
Am Ende dieser Serie haben wir auch One-Way-Plattformen hinzugefügt, den Code für unterschiedliche Zeichengrößen erweitert und einen Bot codiert, der automatisch dem Pfad folgen kann! Schauen Sie sich die Unity-Demo hier (oder die 100 MB + WebGL-Version) an..
Bevor wir den Pfadfindungsalgorithmus anpassen können, müssen wir klar definieren, welche Formen die Pfade selbst annehmen können.
Nehmen wir an, unser Charakter nimmt auf eine Zelle, und kann springen drei Zellen hoch.
Wir erlauben unserem Charakter nicht, sich diagonal durch Zellen zu bewegen, weil wir nicht wollen, dass er durch festes Gelände geht. (Das heißt, wir lassen es nicht zu, dass es sich durch die Ecke einer Zelle bewegt, nur durch die obere, untere, linke oder rechte Seite.) Um zu einer diagonal benachbarten Zelle zu gelangen, muss sich der Charakter um eine Zelle nach oben oder unten bewegen und eine Zelle links oder rechts.
Da die Sprunghöhe des Charakters drei Zellen beträgt, sollte es bei jedem Sprung, nachdem er dreimal nach oben bewegt wurde, nicht in der Lage sein, sich zu bewegen oben mehr Zellen, aber es sollte immer noch in der Lage sein Seite.
Basierend auf diesen Regeln folgt ein Beispiel für den Pfad, den der Charakter während seines maximalen Entfernungssprungs nehmen würde:
Natürlich kann der Charakter direkt nach oben springen oder mit einer beliebigen Kombination aus linker und rechter Bewegung, aber dieses Beispiel zeigt die Art der Annäherung, die wir beim Berechnen des Pfads anhand des Gitters berücksichtigen müssen.
In jeder der Zellen im Pfad müssen Daten über die Sprunghöhe gespeichert werden, damit unser Algorithmus erkennen kann, dass der Spieler nicht höher springen kann und herunterfallen muss.
Beginnen wir damit, den Sprungwert jeder Zelle zuzuweisen, indem sie für jede Zelle um eins erhöht wird, solange der Sprung andauert. Wenn sich der Charakter am Boden befindet, sollte der Sprungwert natürlich sein 0.
Hier ist diese Regel, die zuvor auf denselben Sprungweg mit maximaler Entfernung angewendet wurde:
Die Zelle, die a enthält 6 markiert den höchsten Punkt des Pfades. Dies ist sinnvoll, da dies das Doppelte der maximalen Sprunghöhe des Charakters ist und der Charakter abwechselnd eine Zelle nach oben und eine Zelle zur Seite bewegt, in diesem Beispiel.
Wenn der Charakter direkt nach oben springt und wir den Sprungwert um jeweils eine Zelle erhöhen, funktioniert dies nicht mehr, da in diesem Fall die höchste Zelle einen Sprungwert von hat 3.
Ändern wir unsere Regel, um den Sprungwert auf den Wert zu erhöhen nächste gerade Nummer wann immer sich der Charakter nach oben bewegt. Wenn der Sprungwert gerade ist, kann sich der Charakter entweder nach links, rechts oder nach unten bewegen (oder nach oben, falls der Sprungwert noch nicht erreicht wurde) 6 noch), und wenn der Sprungwert ungerade ist, bewegt sich der Charakter nur auf oder ab (je nachdem, ob er den Scheitelpunkt des Sprungs noch erreicht hat).
So sieht das für einen Sprung nach oben aus:
Und hier ist ein komplizierterer Fall:
So werden die Sprungwerte berechnet:
Wie Sie sehen, wissen wir bei dieser Art der Nummerierung genau, wann der Charakter seine maximale Sprunghöhe erreicht hat: Es ist die Zelle, deren Sprungwert gleich ist zweimal die maximale Charaktersprunghöhe. Wenn der Sprungwert niedriger ist, kann sich der Charakter noch nach oben bewegen. Andernfalls müssen wir den Knoten direkt darüber ignorieren.
Nun, da wir uns der Bewegungen bewusst sind, die der Charakter im Gitter ausführen kann, betrachten wir die folgenden Einstellungen:
Die grüne Zelle an Position (3, 1) ist der Charakter; die blaue Zelle in Position (2, 5) ist das ziel. Lassen Sie uns eine Route zeichnen, die der A * -Algorithmus zuerst wählen kann, um zu versuchen, das Ziel zu erreichen @
Die gelbe Zahl in der oberen rechten Ecke einer Zelle ist der Sprungwert der Zelle. Wie Sie sehen, kann der Charakter mit einem geraden Sprung nach oben drei Plättchen hoch springen, aber nicht weiter. Das ist nicht gut.
Wir können mit einer anderen Route mehr Glück finden. Lassen Sie uns also unsere Suche zurückspulen und von Knoten (3, 2) erneut beginnen..
Wie Sie sehen, wurde es durch den Sprung auf den Block rechts neben dem Charakter ermöglicht, dass er hoch genug sprang, um zum Ziel zu gelangen! Hier gibt es jedoch ein großes Problem…
Aller Wahrscheinlichkeit nach ist der erste Weg, den der Algorithmus einschlagen wird, der erste, den wir untersucht haben. Nach der Übernahme wird der Algorithmus nicht sehr weit kommen und landet wieder im Knoten (3, 2). Es kann dann Knoten durchsuchen (4, 2), (4, 3), (3, 3) (nochmal), (3, 4) (nochmal), (3, 5), und schließlich die Zielzelle, (2, 5).
Wenn in einer Basisversion des A * -Algorithmus ein Knoten bereits einmal besucht wurde, müssen wir ihn nie wieder verarbeiten. In dieser Version machen wir es jedoch. Dies liegt daran, dass die Knoten nicht nur durch X- und Y-Koordinaten, sondern auch durch Sprungwerte unterschieden werden.
Beim ersten Versuch, einen Pfad zu finden, den Sprungwert am Knoten (3, 3) war 4; In unserem zweiten Versuch war es so 3. Da im zweiten Versuch der Sprungwert bei dieser Zelle geringer war, bedeutete das, dass wir von dort aus möglicherweise höher werden konnten als beim ersten Versuch.
Dies bedeutet im Grunde diesen Knoten (3, 3) mit einem Sprungwert von 4 ist ein anderer Knoten als der Knoten bei (3, 3) mit einem Sprungwert von 3. Das Gitter muss an einigen Koordinaten im Wesentlichen dreidimensional werden, um diese Unterschiede zu berücksichtigen, wie z.
Wir können den Sprungwert des Knotens bei nicht einfach ändern (3, 3) von 4 zu 3, weil einige Pfade denselben Knoten mehrmals verwenden; Wenn wir das tun, würden wir den vorherigen Knoten grundsätzlich überschreiben, und das Endergebnis würde dadurch natürlich beschädigt.
Wir hätten das gleiche Problem, wenn der erste Weg das Ziel erreicht hätte Trotz die höheren Sprungwerte; Wenn wir einen der Knoten mit einem vielversprechenderen Knoten überschrieben hätten, hätten wir keine Möglichkeit, den ursprünglichen Pfad wiederherzustellen.
Denken Sie daran, es ist das Algorithmus das einen netzbasierten Ansatz verwendet; Theoretisch müssen Ihr Spiel und sein Level nicht.
Es ist wichtig, dass der Charakter dabei ist wenigstens so viel Bewegungsfreiheit, wie der Algorithmus erwartet - und vorzugsweise etwas mehr als das.
Es ist aufgrund der diskreten Beschaffenheit des Gitters und der Zellengröße des Gitters nicht möglich, die Zeichenbewegung genau mit den Einschränkungen des Algorithmus abzustimmen. Es ist möglich, die Physik so zu codieren, dass der Algorithmus dies tut immer finde einen Weg, wenn es einen gibt, aber dafür musst du die Physik für diesen Zweck bauen.
Ich gehe in diesem Tutorial vor, den Algorithmus an die Physik des Spiels anzupassen, nicht umgekehrt.
Die Hauptprobleme treten in Randfällen auf, wenn die erwartete Bewegungsfreiheit des Algorithmus nicht mit der Bewegungsfreiheit des wahren Charakters im Spiel übereinstimmt.
Nehmen wir an, die KI erlaubt Sprünge von sechs Zellen, aber die Physik des Spiels erlaubt einen Sprung von sieben Zellen. Wenn es einen Pfad gibt, der einen längeren Sprung erfordert, um das Ziel in kürzester Zeit zu erreichen, ignoriert der Bot diesen Pfad und wählt den konservativeren Pfad. Er denkt, dass der längere Sprung unmöglich ist.
Wenn es einen Pfad gibt, der den längeren Sprung erfordert und es keinen anderen Weg gibt, um das Ziel zu erreichen, wird der Pfadfinder zu dem Schluss kommen, dass das Ziel nicht erreichbar ist.
Wenn umgekehrt der Algorithmus der Meinung ist, dass es möglich ist, sieben Zellen wegzuspringen, die Physik des Spiels jedoch nur einen Sechszellensprung zulässt, kann der Bot entweder dem falschen Pfad folgen und an einen Ort fallen, an den er nicht gelangen kann oder versuchen Sie, einen Pfad erneut zu finden und dasselbe falsche Ergebnis zu erhalten, das eine Schleife verursacht.
(Bei diesen beiden Problemen sollte die Physik des Spiels mehr Bewegungsfreiheit erlauben, als der Algorithmus erwartet.)
Der erste Weg, um sicherzustellen, dass der Algorithmus immer korrekt ist, besteht in Stufen, die die Spieler nicht ändern können. In diesem Fall müssen Sie nur sicherstellen, dass das von Ihnen entworfene oder generierte Terrain mit der Pfadfinding-KI gut funktioniert.
Die zweite Lösung für diese Probleme besteht darin, den Algorithmus, die Physik oder beide zu optimieren, um sicherzustellen, dass sie übereinstimmen. Wie ich bereits erwähnt habe, heißt das nicht, dass sie übereinstimmen müssen genau; Wenn der Algorithmus beispielsweise glaubt, dass der Charakter fünf Zellen nach oben springen kann, ist es in Ordnung, den tatsächlichen maximalen Sprung auf zu setzen 5.5 Zellen hoch. (Im Gegensatz zum Algorithmus kann die Physik des Spiels gebrochene Werte verwenden.)
Je nach Spiel kann es auch richtig sein, dass der KI-Bot keinen vorhandenen Pfad findet, keine große Sache ist; es wird einfach aufgeben und zu seinem Posten zurückkehren oder einfach nur sitzen und auf den Spieler warten.
An diesem Punkt sollten Sie ein fundiertes konzeptionelles Verständnis dafür haben, wie A * -Pfadfindung an ein Plattformprogramm angepasst werden kann. In meinem nächsten Tutorial machen wir dies konkret, indem wir einen vorhandenen A * -Pathfinding-Algorithmus anpassen, um dies in einem Spiel zu implementieren!