Effizientes Auffinden vieler Herden von Feinden um Hindernisse herum

20

Ich arbeite daran, die Wegfindung für die Feinde meines Spiels zu verbessern. Im Moment bewegen sie sich einfach ständig auf die exakte Position des Spielers zu, indem sie den Winkel zwischen sich und den Spielern berechnen und sich in diese Richtung bewegen. Ich habe auch einen Flock-Algorithmus, der verhindert, dass sich die Feinde übereinander stapeln, sodass sie sich zu Gruppen zusammenschließen, anstatt sich gegenseitig zu durchschneiden.

Jetzt, da ich eine kachelbasierte Karte hinzugefügt habe, müssen die Feinde jedoch auch in der Lage sein, beispielsweise um Hindernisse und Mauern herumzulaufen. Anfangs habe ich versucht, "nicht begehbaren" Kacheln einen Trennungswert hinzuzufügen, damit der Beflockungsalgorithmus die Wände und Hindernisse als Objekte ansieht, von denen man sich entfernen kann. Ich muss noch herausfinden, ob dies machbar ist oder nicht, da mein erster Test zeigte, dass die Feinde auf eine unsichtbare "Wand" treffen, an der es keine nicht begehbaren Kacheln gibt.

Ich habe mich gefragt, ob es möglicherweise zu leistungsintensiv ist, einen Pfad zum Spieler mit A * zu berechnen und dann den Flock-Algorithmus zu verwenden, um ein Verklumpen zu verhindern. Ursprünglich sollte mein Spiel ein wellenbasierter Shooter sein, aber ich habe mich dafür entschieden, es in Anlehnung an die Hotline Miami levelbasiert zu machen. Daher werde ich wahrscheinlich weniger Feinde haben, mit einer gelegentlichen Horde, und einfach machen sie stärker.

Ist das eine praktikable Lösung? Ich benutze Java mit Slick2D als meine Spiel-Engine. Oder gibt es eine bessere Lösung / einen besseren Algorithmus, der beide Probleme angeht?

Darin Beaudreau
quelle
7
Wie ich in der Bearbeitung beschrieben habe, ist "ist das zu schwer" eine Frage, die Sie Ihrem Profiler stellen sollten, da sie von Ihrer Implementierung, der Zielhardware, dem Leistungsbudget und dem Kontext Ihres Spiels abhängt - alles Dinge, die Sie und Ihr Profiler kennen Intim aber Internetfremde nicht. Wenn Sie die Pfadfindung für Herden effizienter gestalten möchten, können wir Strategien vorschlagen, die Ihnen dabei helfen. Nur Ihre eigene Profilerstellung kann jedoch die für Ihre Anforderungen effizient genug sind. Wenn Sie ein bestimmtes Leistungsproblem profilieren und identifizieren, können wir Ihnen auch bei der Suche nach einer Lösung für dieses Problem behilflich sein.
DMGregory
1
Wie Sie sie implementieren, wirkt sich auf die Leistung aus. Zum Beispiel, nur A * auf Anführer setzen und sich darauf verlassen, dass sich Anhänger versammeln.
Pikalek
Wenn Ihr Spiel in erster Linie auf dem Kampf gegen diese Feinde basiert, hat der Algorithmus, den Sie verwenden, einen enormen Einfluss darauf, wie sich das Spiel anfühlt. Sie sollten also verschiedene Ansätze ausprobieren. Haben Sie beispielsweise das Gefühl, dass die Feinde das Level und die Position des Spielers jederzeit perfekt kennen und ihn verfolgen, wie es eine allwissende KI vorgibt? - Andere Ansätze könnten sein, die Feinde in die allgemeine Richtung laufen zu lassen, in der der Spieler Lärm machte und nur auf direkte Sichtlinie auf ihn zu rennen, oder andere Feinde zu schreien und zu informieren, wo der Spieler ist ...
Falco
@Falco Da das Spiel nicht länger wellenbasiert ist und Level-basiert sein wird und die Feinde Zombies sind, habe ich überlegt, es so zu machen, dass du entweder gesehen werden musst oder Lärm machst, damit sie dich finden. Also, wenn Sie eine laute Waffe benutzen? Es gibt einen Ton in einer Reichweite und alle Gegner in Reichweite auf den Ort des Tons aus, der ausgesendet wird, und bewegt sich dann zufällig um diesen Bereich herum.
Darin Beaudreau

Antworten:

49

Dies klingt nach einem Anwendungsfall für Flow Fields.

Bei dieser Technik führen Sie eine einzelne Pfadfindungsabfrage von Ihren Player-Objekten nach außen durch und markieren dabei jede Zelle, auf die Sie stoßen, mit der Zelle, von der aus Sie sie erreicht haben.

Wenn alle Ihre Kacheln / Kanten die gleichen Traversalkosten haben, können Sie eine einfache Breitensuche durchführen. Ansonsten funktioniert der Dijkstra-Algorithmus (wie A * ohne Ziel / Heuristik).

Dadurch wird ein Flussfeld erstellt: Eine Nachschlagetabelle, die jede Zelle dem nächsten Schritt zum nächstgelegenen Spielerobjekt von dieser Position aus zuordnet.

Jetzt können Ihre Feinde ihre aktuelle Position im Flussfeld abrufen, um den nächsten Schritt auf ihrem kürzesten Weg zum nächstgelegenen Spielerobjekt zu finden, ohne dass sie jeweils eine eigene Pfadfindungsabfrage durchführen müssen.

Dies skaliert besser und besser, je mehr Feinde sich in Ihrer Herde befinden. Für einen einzelnen Gegner ist es teurer als A *, weil es die gesamte Karte durchsucht (obwohl Sie frühzeitig aussteigen können, sobald Sie alle Wegbereiter erreicht haben). Wenn Sie jedoch weitere Gegner hinzufügen, können diese mehr und mehr an den Kosten für die Pfadfindung beteiligt werden, indem sie gemeinsam genutzte Pfadsegmente einmal und nicht immer wieder berechnen. Sie profitieren auch von der Tatsache, dass BFS / Dijkdtra einfacher als A * und in der Regel billiger pro untersuchter Zelle zu bewerten sind.

Genau dort, wo die Gewinnschwelle erreicht wird, von einzelnen A *, die billiger sind, bis zu A *, wobei die Speicherung billiger ist (wobei Sie einige der Ergebnisse für eine frühere Wegfindungsabfrage wiederverwenden, um die nächste zu beschleunigen), um Felder zu fließen Dies hängt von Ihrer Implementierung, der Anzahl der Agenten und der Größe Ihrer Karte ab. Wenn Sie jedoch jemals einen großen Schwarm von Feinden planen, der sich aus mehreren Richtungen auf engstem Raum nähert, ist ein Strömungsfeld mit ziemlicher Sicherheit billiger als das iterierte A *.

Als extremes Beispiel sehen Sie hier ein Video mit 20 000 Agenten, die alle gleichzeitig auf einem relativ kleinen Raster nach Wegen suchen .

DMGregory
quelle
Diese Technik klingt wirklich ordentlich. Ich werde das überprüfen.
Darin Beaudreau
15
Es ist möglich, einen hybriden Algorithmus zu verwenden, der ein Teilflussfeld erstellt, ohne mehr von der Karte zu durchsuchen als wiederholte Aufrufe von A * und niemals dieselbe Position zweimal zu durchsuchen. Die Grundidee ist, einen beliebigen Feind auszuwählen und eine A * -Suche vom Spieler auf diesen Feind zu starten, wobei die Zellen markiert werden, wenn Sie ihnen begegnen, genau wie bei der normalen Erzeugung von Strömungsfeldern. Wenn die Suche diesen Feind gefunden hat, wählen Sie einen anderen Feind (den Sie noch nicht gefunden haben) als Ziel aus, sortieren Sie den offenen Satz entsprechend der neuen Heuristik neu und setzen Sie die Suche fort. Hören Sie auf, wenn Sie alle Feinde gefunden haben.
Ilmari Karonen
1
Was ist mit der Vermeidung von Kollisionen? Das ist (etwas) in der OP erwähnt (Vermeidung von Beschneidungen, wenn sie den Spieler erreichen). Mir scheint, Sie müssten jedes Mal, wenn sich etwas bewegt (oder zusätzliche Logik hinzufügt), die vollständigen Djikstras erneut ausführen
Mars,
2
@ Mars Das OP spricht von Flocking, daher würde ich davon ausgehen, dass sich alle Individuen mit der gleichen Geschwindigkeit bewegen können. Die einzigen Orte, an denen Kollisionen zum Problem werden, sind Engpässe, bei denen ein Teil der Herde anhalten und warten muss. Die Pfadfindung muss jedoch nicht wirklich geändert werden - eine einfache Warteschlange funktioniert wahrscheinlich in den meisten Fällen gut genug, und eine gewisse Pfadgewichtung (eine pseudozufällige Auswahl alternativer Pfade mit ähnlichen Kosten) führt zu einer natürlicheren Herde Ströme, die es auch vermeiden, dass die ganze Herde versucht, eine bestimmte Lücke zu
überwinden
3
@Luaan In einem auf Kacheln basierenden Spiel wären Sie überrascht, wie oft Kollisionen auftreten. Persönlich finde ich die "Warteschlangen" -Option weniger als optimal. Wenn Einheiten nicht durcheinander kommen können, müssen Sie neu berechnen, wann die Einheiten ihre endgültige Position einnehmen, und eine Reihe weiterer Randfälle. Beflockung ist schwer;)
Mars
8

Ein * ist nicht leistungslastig. Ich würde mich dieser Situation durch Variation der Algorithmen nähern. Machen Sie von Zeit zu Zeit ein A * und prüfen Sie zwischendurch, ob der nächste Schritt frei ist oder Sie Ausweichen müssen.

Verfolgen Sie beispielsweise die Entfernung des Spielers vom A * -Zielort, wenn dieser über einem Schwellenwert liegt. Berechnen Sie ein * neu und aktualisieren Sie dann einfach die Bewegungen. Die meisten Spiele verwenden eine Kombination von Wegpunkten, z. B. ein vereinfachtes Raster für die Wegfindung und eine Logik, die die Bewegung zwischen Wegpunkten mit Ausweichlenkungsalgorithmen unter Verwendung von Raycasts handhabt. Die Agenten versuchen, durch Manövrieren um Hindernisse in ihrer Nähe zu einem entfernten Wegpunkt zu rennen, was meiner Meinung nach der beste Ansatz ist.

Am besten arbeitet man hier mit endlichen Automaten und liest das Buch "Programming Game AI By Example" von Mat Buckland. Das Buch bietet bewährte Techniken für Ihr Problem und beschreibt die erforderlichen Berechnungen. Der Quellcode aus dem Buch ist im Internet verfügbar. Das Buch ist in C ++, aber einige Übersetzungen (einschließlich Java) sind verfügbar.

D3d_dev
quelle
2
Bei einem selten aktualisierten A * -Ansatz kann es hilfreich sein, die Aktualisierungen zu staffeln und dabei ein Budget für die Anzahl der Feinde aufrechtzuerhalten, die auf einem einzelnen Frame erneut ausgeführt werden dürfen. Auf diese Weise können Sie Ihre maximalen Pfadfindungskosten pro Frame begrenzen und viele AI-Pfade robuster handhaben, indem Sie ihre Gesamtkosten über mehrere Frames amortisieren. Eine KI, die einen veralteten Pfad für einen oder zwei Frames verwendet, wenn das Budget für den Frame überschritten wurde, oder wenn sie in der Nähe ist, auf Dead Reckoning zurückgreift, ist normalerweise nicht störend.
DMGregory
2
Wahrscheinlich wird hier das Offensichtliche angegeben, aber wenn Sie nur einige Ihrer Pfade in einem bestimmten Frame aktualisieren möchten, möchten Sie möglicherweise ein Prioritätssystem, das auf der Entfernung zum Player basiert. Es ist wahrscheinlich wichtiger für Feinde in der Nähe des Spielers, ihre Pfade zu aktualisieren, während es für Feinde in der Ferne wahrscheinlich in Ordnung ist, einen veralteten Pfad zu verwenden.
AC
4

Es ist nicht nur machbar, ich glaube es wurde in einem kommerziellen Spiel in den 90ern gemacht - BattleZone (1998).

Das Spiel hatte 3D-Einheiten mit freien Bewegungen, die nicht auf Kacheln basierten, und eine auf Kacheln basierende Basiskonstruktion.

So schien es zu funktionieren:

Erstens A * oder etwas Ähnliches (wahrscheinlich eine Variation von A * mit strengen Einschränkungen für die Länge eines Pfads, sodass die Ausführung niemals zu viele Ressourcen erfordert, aber nicht immer einen Pfad bis zum Ziel findet) würde verwendet werden, um einen Weg zu finden, über den ein Schwebetank an sein Ziel gelangen kann, ohne in kachelförmigen Hindernissen stecken zu bleiben.

Dann flog der Panzer durch den gesamten Raum, als würde er auf seinem Weg von der Mitte eines nahegelegenen Feldes angezogen und von Hindernissen, anderen nahegelegenen Panzern usw. zurückgestoßen.

Robyn
quelle
1
Was ist also ein guter Weg, um dem Pfad zu folgen, aber nicht genau? Wenn ich Kurvenfahrten für Kinder zulasse, muss ich in der Lage sein, die Feinde daran zu hindern, mit der Ecke eines Hindernisses zusammenzustoßen. Sollte ich das Flockverhalten für Feinde und Hindernisse beibehalten und A * hinzufügen, um mit diesen Situationen umzugehen?
Darin Beaudreau