Implementieren Sie den Pfadfinder für KI-Agenten mit NavMesh

Bild

Dem Pfad folgen und den Verkehr kontrollieren


Manchmal brauchen wir KI-Charaktere, um die Spielwelt zu durchstreifen und einem grob definierten oder genau definierten Pfad zu folgen. In einem Rennspiel müssen KI-Gegner beispielsweise die Straße entlang fahren, und in RTS müssen sich die Einheiten zum gewünschten Punkt bewegen können, sich entlang des Geländes bewegen und die Position des anderen berücksichtigen.

Um intelligent zu wirken, müssen KI-Agenten bestimmen können, was sie tun. Wenn sie den gewünschten Punkt nicht erreichen können, müssen sie in der Lage sein, die effektivste Route zu zeichnen und ihren Weg zu ändern, wenn Hindernisse auf dem Weg erscheinen.

Das Vermeiden von Hindernissen ist ein einfaches Verhalten, mit dem KI-Einheiten Zielpunkte erreichen können. Es ist wichtig zu beachten, dass das in diesem Beitrag implementierte Verhalten für Verhaltensweisen wie die Crowd-Simulation gilt, bei denen das Hauptziel jedes Agenten darin besteht, andere Agenten zu meiden und das Ziel zu erreichen. Sie bestimmen nicht den effizientesten und kürzesten Weg.

Technische Anforderungen


Erfordert die Installation von Unity 2017 auf einem System mit Windows 7 SP1 +, 8, 10 oder Mac OS X 10.9+. Der Code in diesem Artikel funktioniert nicht unter Windows XP und Vista, und Serverversionen von Windows und OS X wurden nicht getestet.

Die Codedateien für diesen Beitrag finden Sie auf GitHub .

Sehen Sie sich dieses Video an, um den Code in Aktion zu lernen.

Navigationsnetz


Lassen Sie uns herausfinden, wie der integrierte Unity-Navigationsnetzgenerator verwendet wird, der die Suche nach Pfaden für KI-Agenten erheblich vereinfachen kann. In den frühen Phasen von Unity 5.x stand die NavMesh-Funktion allen Benutzern zur Verfügung, einschließlich Benutzern mit Personal Edition-Lizenzen, obwohl sie früher nur für Unity Pro verfügbar war. Vor der Veröffentlichung von 2017.1 wurde das System aktualisiert, um einen komponentenbasierten Workflow bereitzustellen. Da jedoch ein zusätzliches herunterladbares Paket erforderlich ist, das zum Zeitpunkt des Schreibens nur in der Vorschau-Version verfügbar ist, werden wir den standardmäßigen szenenbasierten Workflow einhalten. Keine Sorge, die Konzepte beider Ansätze sind ähnlich, und wenn die fertige Implementierung schließlich 2017.x erreicht, sollten keine wesentlichen Änderungen vorgenommen werden.

Weitere Informationen zum NavMesh-Komponentensystem in Unity auf GitHub .

Jetzt werden wir alle Möglichkeiten untersuchen, die dieses System uns bieten kann. Um nach KI-Pfaden zu suchen, muss die Szene in einem bestimmten Format dargestellt werden. Auf einer 2D-Karte wird ein zweidimensionales Gitter (Array) verwendet, um mithilfe des A * -Algorithmus nach Pfaden zu suchen. KI-Agenten müssen wissen, wo sich die Hindernisse befinden, insbesondere statische. Der Umgang mit Kollisionen zwischen sich dynamisch bewegenden Objekten ist ein weiteres Problem, das allgemein als Lenkverhalten bezeichnet wird. Unity verfügt über ein integriertes Tool zum Generieren von NavMesh, das die Szene in einem Kontext darstellt, in dem KI-Agenten den optimalen Pfad zum Ziel finden können. Öffnen Sie zunächst ein Demo-Projekt und rufen Sie die NavMesh-Szene auf.

Studienkarte


Nach dem Öffnen der NavMesh-Demoszene sollte es wie im Screenshot aussehen:


Hindernis- und Hang-Szene

Dies ist unsere Sandbox zum Erklären und Testen der Funktionalität des NavMesh-Systems. Das allgemeine Schema ähnelt einem Spiel im Genre RTS (Echtzeitstrategie). Wir fahren einen blauen Panzer. Klicken Sie auf verschiedene Punkte, damit sich der Tank auf sie zubewegt. Die gelbe Anzeige ist das aktuelle Ziel des Tanks.

Navigation statisch


Zunächst müssen Sie sagen, dass Sie die gesamte in NavMesh gebackene Geometrie in der Szene als Navigationsstatik markieren sollten. Möglicherweise haben Sie dies bereits im Unity-Beleuchtungskartensystem gesehen. Um Spielobjekte statisch zu machen, ist es sehr einfach, das Kontrollkästchen Statisch für alle ihre Eigenschaften (Navigation, Beleuchtung, Keulen, Stapeln usw.) zu aktivieren oder die Eigenschaften in der Dropdown-Liste anzugeben. Das Kontrollkästchen befindet sich in der oberen rechten Ecke des Inspektors ausgewählter Objekte.


Eigenschaftsnavigation statisch

Dies kann einzeln für verschiedene Objekte erfolgen. Wenn Sie über eine integrierte Hierarchie von Spielobjekten verfügen, wenden Sie den Parameter auf das übergeordnete Objekt an. Anschließend bietet Unity an, ihn auf alle untergeordneten Objekte anzuwenden.

Braten eines Navigationsnetzes


Für die gesamte Szene werden die Navigationsoptionen von navmesh über das Navigationsfenster angewendet. Dieses Fenster kann geöffnet werden, indem Sie zu Fenster | gehen Navigation Wie jedes andere Fenster kann es für freie Bewegung getrennt oder fixiert werden. In unseren Screenshots wird es als angedockte Registerkarte neben der Hierarchie angezeigt. Sie können dieses Fenster jedoch an einer beliebigen Stelle platzieren.

Wenn Sie das Fenster öffnen, sehen Sie einzelne Registerkarten. Es wird ungefähr so ​​aussehen:


Navigationsfenster

In unserem Fall zeigt der vorherige Screenshot die Registerkarte Backen. In Ihrem Editor kann jedoch standardmäßig jede Registerkarte ausgewählt werden.

Schauen wir uns die einzelnen Registerkarten an, beginnend von links und nach rechts. Beginnen wir mit der Registerkarte " Agenten" , die wie im Screenshot dargestellt aussieht:


Registerkarte "Agenten"

Wenn Sie an einem anderen Projekt arbeiten, stellen Sie möglicherweise fest, dass einige der Einstellungen von denen abweichen, die wir für das im Screenshot gezeigte Beispielprojekt festgelegt haben. Oben auf der Registerkarte befindet sich eine Liste, in der Sie neue Agententypen hinzufügen können, indem Sie auf die Schaltfläche + klicken. Sie können zusätzliche Agenten entfernen, indem Sie sie auswählen und auf die Schaltfläche - klicken. Das Fenster zeigt deutlich, was verschiedene Einstellungen beim Ändern tun. Mal sehen, was jede der Einstellungen bewirkt:

  • Name: Name des Agententyps, der in der Dropdown-Liste Agententypen angezeigt wird.
  • Radius: Sie können sich das als den „persönlichen Raum“ eines Agenten vorstellen. Agenten werden versuchen, einen zu engen Kontakt mit anderen Agenten basierend auf diesem Wert zu vermeiden, da er zur Vermeidung verwendet wird.
  • Höhe: Wie Sie vielleicht erraten haben, legt diese Einstellung die Höhe des Agenten fest, den er zur vertikalen Vermeidung verwendet (z. B. beim Passieren unter Objekten).
  • Schritthöhe: Dieser Wert bestimmt, auf welche Höhe der Agent klettern kann.
  • Max. Steigung: Wie wir im nächsten Abschnitt sehen werden, bestimmt dieser Wert den maximalen Winkel, unter dem der Agent klettern kann. Mit diesem Parameter können Sie die steilen Hänge der Karte für den Agenten unzugänglich machen.

Als nächstes haben wir die Registerkarte Bereiche , die wie in diesem Screenshot gezeigt aussieht:


Wie Sie im Screenshot sehen können, bietet Unity verschiedene Arten von Bereichen, die nicht geändert werden können: Begehbar , Nicht begehbar und Springen . Zusätzlich zum Benennen und Erstellen neuer Bereiche können Sie diesen Bereichen die Kosten für das Verschieben zuweisen.

Bereiche dienen zwei Zwecken: Bereiche für den Agenten zugänglich oder unzugänglich machen und Bereiche im Hinblick auf die Reisekosten als weniger wünschenswert markieren. Sie können beispielsweise ein Rollenspiel entwickeln, in dem Dämonenfeinde keine Gebiete betreten können, die als „geweihter Boden“ gekennzeichnet sind. Sie können einige Bereiche der Karte auch als „Sumpf“ oder „Sumpf“ markieren, was der Agent aufgrund der hohen Umzugskosten vermeiden wird.

Die dritte Registerkarte Backen ist wahrscheinlich die wichtigste. Sie können NavMesh selbst für die Szene erstellen. Sie sollten bereits mit einigen Optionen vertraut sein. Die Registerkarte Backen sieht folgendermaßen aus:


Tab backen

Die Optionen für die Agentengröße auf dieser Registerkarte bestimmen, wie Agenten mit der Umgebung interagieren, während die Optionen auf der Registerkarte Agenten die Interaktionen mit anderen Agenten und sich bewegenden Objekten steuern. Sie steuern jedoch dieselben Parameter, sodass wir sie überspringen. Drop Height und Jump Distance steuern, wie weit der Agent „springen“ kann, um den Teil von NavMesh zu erreichen, der nicht direkt mit dem Teil zusammenhängt, in dem sich der Agent gerade befindet. Wir werden dies im Folgenden genauer betrachten. Wenn Sie sich also nicht sicher sind, können Sie diese Parameter immer noch nicht untersuchen.

Darüber hinaus gibt es erweiterte Optionen, die normalerweise standardmäßig ausgeblendet sind. Um diese Optionen zu erweitern, klicken Sie einfach auf das Dropdown-Dreieck neben der Überschrift Erweitert . Die manuelle Voxelgröße kann als „Qualitätseinstellung“ angesehen werden. Je kleiner die Größe, desto mehr Details werden im Netz gespeichert. Min Region Area wird verwendet, um Backplattformen oder Oberflächen unterhalb des ausgewählten Schwellenwerts zu überspringen. Höhe Netz gibt uns detailliertere vertikale Daten beim Backen eines Netzes. Mit dieser Option können Sie beispielsweise beim Treppensteigen die korrekte Position des Agenten beibehalten.

Mit der Schaltfläche Löschen werden alle NavMesh-Daten der Szene gelöscht, und mit der Schaltfläche Backen wird ein Netz für die Szene erstellt. Der Backvorgang ist ziemlich schnell. Solange Sie ein Fenster ausgewählt haben, können Sie die Erzeugung von NavMesh mit der Schaltfläche Backen im Szenenfenster beobachten. Klicken wir auf die Schaltfläche Backen , um die Ergebnisse anzuzeigen. In unserer Beispielszene erhalten wir etwas Ähnliches wie diesen Screenshot:


Die blauen Bereiche zeigen NavMesh. Im Folgenden werden wir darauf zurückkommen. Fahren wir in der Zwischenzeit mit der letzten Registerkarte fort - Objekt , das folgendermaßen aussieht:


Die drei im vorherigen Screenshot gezeigten Schaltflächen - Alle , Netz-Renderer und Terrains - werden als Szenenfilter verwendet. Sie sind nützlich, wenn Sie in komplexen Szenen mit vielen Objekten in der Hierarchie arbeiten. Durch Auswahl einer Option wird der entsprechende Typ aus der Hierarchie herausgefiltert, sodass die Auswahl vereinfacht wird. Mit den Schaltflächen können Sie Ihre Szene auf der Suche nach Objekten durchsuchen, die Sie als statische Navigation markieren möchten.

Verwenden des Nav Mesh Agent


Nachdem wir die Szene mit NavMesh eingerichtet haben, muss der Agent diese Informationen verwenden können. Zum Glück hat Unity eine Nav Mesh Agent- Komponente, die Sie auf einen Charakter ziehen können. In unserer Beispielszene gibt es ein Spielobjekt namens Tank , an das bereits eine Komponente angehängt ist. Schauen Sie sich die Hierarchie an und Sie werden ungefähr Folgendes sehen:


Hier gibt es einige Parameter, und wir werden nicht alles berücksichtigen, da sie ziemlich klar sind und die Beschreibung in der offiziellen Unity-Dokumentation zu finden ist. Aber wir werden die wichtigsten Dinge erwähnen:

  • Agententyp : Erinnern Sie sich an die Registerkarte Agenten im Navigationsfenster ? Hier können zuweisbare Agententypen ausgewählt werden.
  • Auto-Traverse-Off-Mesh-Link : Mit dieser Option können Agenten automatisch die Off-Mesh-Link- Funktion verwenden, die im Folgenden erläutert wird.
  • Bereichsmaske : Hier können Sie die Bereiche auswählen, die auf der Registerkarte Bereiche des Navigationsfensters konfiguriert wurden.

Das ist alles. Diese Komponente erledigt 90% der harten Arbeit für uns: Wegbereiter, Vermeidung von Hindernissen und so weiter. Sie müssen lediglich den Zielpunkt an den Agenten übertragen. Schauen wir uns dieses Problem an.

Zielpunkteinstellung


Nachdem wir den KI-Agenten eingerichtet haben, müssen wir ihm sagen, wohin er gehen soll. In unserem Beispielprojekt gibt es ein Skript namens Target.cs , das genau diese Aufgabe ausführt.

Dies ist eine einfache Klasse, die drei Dinge tut:

  • "Schießt" den Strahl von der Kamera zur Mausposition in der Welt
  • Aktualisiert die Markerposition
  • Aktualisiert die Zieleigenschaft für alle NavMesh-Agenten.

Der Code ist recht einfach. Die ganze Klasse ist wie folgt:

using UnityEngine; using UnityEngine.AI; public class Target : MonoBehaviour { private NavMeshAgent[] navAgents; public Transform targetMarker; private void Start () { navAgents = FindObjectsOfType(typeof(NavMeshAgent)) as NavMeshAgent[]; } private void UpdateTargets ( Vector3 targetPosition ) { foreach(NavMeshAgent agent in navAgents) { agent.destination = targetPosition; } } private void Update () { if(GetInput()) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; if (Physics.Raycast(ray.origin, ray.direction, out hitInfo)) { Vector3 targetPosition = hitInfo.point; UpdateTargets(targetPosition); targetMarker.position = targetPosition; } } } private bool GetInput() { if (Input.GetMouseButtonDown(0)) { return true; } return false; } private void OnDrawGizmos() { Debug.DrawLine(targetMarker.position, targetMarker.position + Vector3.up * 5, Color.red); } } 

Die folgenden Aktionen werden hier ausgeführt: In der Start- Methode initialisieren wir das navAgents- Array mit der FindObjectsOfType () -Methode.

Die UpdateTargets () -Methode durchläuft unser navAgents- Array und legt den Zielpunkt für sie im angegebenen Vector3 fest . Dies ist der Schlüssel zum Code. Sie können einen beliebigen Mechanismus verwenden, um den Zielpunkt abzurufen. Damit sich der Agent dorthin bewegen kann, legen Sie einfach das Feld NavMeshAgent.destination fest . Der Agent erledigt den Rest.

In unserem Beispiel werden Klicks zum Bewegen verwendet. Wenn also ein Spieler auf die Maus klickt, geben wir den Strahl von der Kamera in Richtung des Mauszeigers in die Welt frei. Wenn er sich mit etwas schneidet, weisen wir dem neuen targetPosition- Agenten einen Kollisionspunkt zu. Wir passen auch die Zielmarkierung entsprechend an, um das Ziel im Spiel einfach zu visualisieren.

Um den Vorgang zu testen, müssen Sie NavMesh gemäß der Beschreibung im vorherigen Abschnitt backen, dann den Wiedergabemodus starten und einen beliebigen Bereich auf der Karte auswählen. Wenn Sie mehrmals klicken, können Sie feststellen, dass der Agent einige Bereiche nicht erreichen kann - die oberen Bereiche der roten Würfel, die obere Plattform und die Plattform am unteren Bildschirmrand.

Rote Würfel sind zu hoch. Der Hang, der zur höchsten Plattform führt, ist für unsere Max Slope- Einstellungen zu scharf, und der Agent kann ihn nicht erklimmen. Die folgenden Screenshots zeigen, wie sich die Einstellungen für die maximale Neigung auf NavMesh auswirken:


NavMesh mit maximaler Steigung = 45

Wenn Sie den Wert für die maximale Steigung in etwa 51 ändern und dann erneut auf die Schaltfläche Backen klicken, um NavMesh erneut zu backen, lauten die Ergebnisse wie folgt:


NavMesh mit maximaler Steigung = 51

Wie Sie sehen, können wir das Level-Design anpassen und so ganze Bereiche unzugänglich machen, indem wir einen einzelnen Parameter ändern. Dies kann beispielsweise nützlich sein, wenn Sie eine Plattform oder einen Sims haben, für deren Klettern ein Seil, eine Leiter oder ein Aufzug erforderlich ist. Oder vielleicht eine besondere Fähigkeit, zum Beispiel die Fähigkeit zu klettern?

Anwendung Off Mesh Links


Sie werden vielleicht feststellen, dass es in unserer Szene zwei Pausen gibt. Unser Agent kann in den ersten gelangen, aber der am unteren Bildschirmrand ist zu weit entfernt. Diese Berechnungen sind nicht völlig willkürlich. Off-Mesh-Links bilden im Wesentlichen eine Brücke zwischen den Zwischenräumen zwischen nicht verwandten NavMesh-Segmenten. Diese Links sind im Editor zu sehen:


Blaue Kreise mit Verbindungslinien sind Verbindungen.

Unity kann diese Links auf zwei Arten erzeugen. Das erste haben wir schon überlegt. Erinnern Sie sich an den Sprungdistanzwert auf der Registerkarte Backen des Navigationsfensters ? Unity verwendet diesen Wert automatisch, um diese Links beim Backen von NavMesh zu generieren. Versuchen Sie, den Wert in unserer Testszene auf 5 zu ändern und erneut zu backen. Sehen Sie - die Plattformen sind jetzt verbunden? Dies liegt daran, dass die Netze jetzt innerhalb des neu angegebenen Schwellenwerts liegen.

Ändern Sie den Wert erneut auf 2 und backen Sie. Schauen wir uns nun den zweiten Weg an. Erstellen Sie die Kugeln, mit denen die beiden Plattformen verbunden werden. Platzieren Sie sie ungefähr wie im Screenshot gezeigt:


Sie können bereits sehen, was passiert, aber lassen Sie uns den Prozess analysieren, der es ihnen ermöglicht, eine Verbindung herzustellen. In unserem Fall habe ich die Kugel am rechten Anfang und die Kugel am linken Ende genannt . Sie werden bald verstehen warum. Als Nächstes habe ich der Plattform auf der rechten Seite die Off Mesh Link- Komponente hinzugefügt (relativ zum vorherigen Screenshot). Sie werden feststellen, dass die Komponente Start- und Endfelder hat. Wie Sie vielleicht erraten haben, ziehen wir die zuvor erstellten Kugeln in die entsprechenden Slots - die Startkugel im Startfeld und die Endkugel im Endfeld. Der Inspektor sieht folgendermaßen aus:


Der Wert von Cost Override wird berücksichtigt, wenn er einen positiven Wert erhält. Bei Verwendung dieser Beziehung wird ein Kostenfaktor angewendet, im Gegensatz zu einem kostengünstigeren Weg zum Ziel.

Bi Directional, wenn true, ermöglicht es dem Agenten, sich in beide Richtungen zu bewegen. Um Links mit Einwegverkehr zu erstellen, können Sie diesen Wert deaktivieren. Der Wert Aktiviert wird entsprechend seinem Namen verwendet. Bei false ignoriert der Agent diese Zuordnung. Sie können es ein- und ausschalten, um Spielszenarien zu erstellen, in denen ein Spieler beispielsweise einen Schalter drücken muss, um eine Verbindung zu aktivieren.

Um diese Beziehung zu ermöglichen, ist kein erneutes Backen erforderlich. Schauen Sie sich Ihr NavMesh an und Sie werden sehen, dass es genau so aussieht wie im Screenshot:


Wie Sie sehen können, wird die kleinere Lücke immer noch automatisch verbunden, und jetzt haben wir eine neue Verbindung, die von der Off Mesh Link- Komponente zwischen den beiden Kugeln generiert wird. Starten Sie den Wiedergabemodus und klicken Sie auf die entfernte Plattform. Wie erwartet kann der Agent jetzt zur nicht verbundenen Plattform navigieren:


Auf den Ebenen Ihres Spiels müssen Sie möglicherweise diese Parameter ändern, um die gewünschten Ergebnisse zu erzielen. Eine Kombination dieser Funktionen bietet Ihnen jedoch ein praktisches, vorgefertigtes Werkzeug. Mit der NavMesh-Funktion können Sie schnell ein einfaches Spiel erstellen.

Dieses Tutorial ist Teil von Unity 2017 Game AI Programming - Dritte Ausgabe von Ray Barrera, Aung Sithu Kyaw und Thet Naing Swe.

Source: https://habr.com/ru/post/de414453/


All Articles