Advertisement
  1. Game Development
  2. Artificial Intelligence
Gamedevelopment

Erstellen Sie eine Hockey-Spiel-KI mithilfe von Lenkverhalten: Grundlage

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Create AI for a Hockey Game Using Steering Behaviors.
Create a Hockey Game AI Using Steering Behaviors: Attack

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

Es gibt verschiedene Möglichkeiten, ein bestimmtes Spiel zu erstellen.  Normalerweise wählt ein Entwickler etwas, das zu seinen Fähigkeiten passt, und verwendet dabei die Techniken, die er bereits kennt, um das bestmögliche Ergebnis zu erzielen.  Manchmal wissen die Leute noch nicht, dass sie eine bestimmte Technik brauchen - vielleicht sogar eine einfachere und bessere -, einfach weil sie bereits wissen, wie man dieses Spiel erstellt.

In dieser Serie von Tutorials erfahren Sie, wie Sie künstliche Intelligenz für ein Eishockeyspiel mithilfe einer Kombination von Techniken erstellen, z. B. Lenkungsverhalten, die ich zuvor als Konzepte erklärt habe.

Hinweis: Obwohl dieses Tutorial mit AS3 und Flash geschrieben wurde, sollten Sie in der Lage sein, in fast jeder Spieleentwicklungsumgebung dieselben Techniken und Konzepte zu verwenden.


Einführung

Hockey ist ein unterhaltsamer und beliebter Sport, und als Videospiel enthält es viele gamedev-Themen wie Bewegungsmuster, Teamwork (Angriff, Verteidigung), künstliche Intelligenz und Taktiken.  Ein spielbares Hockeyspiel eignet sich hervorragend, um die Kombination einiger nützlicher Techniken zu demonstrieren.

Den Hockeymechaniker zu simulieren, bei dem Sportler laufen und sich bewegen, ist eine Herausforderung.  Wenn die Bewegungsmuster selbst bei unterschiedlichen Pfaden vordefiniert sind, wird das Spiel vorhersehbar (und langweilig).  Wie können wir eine solche dynamische Umgebung implementieren und gleichzeitig die Kontrolle über die Vorgänge behalten?  Die Antwort lautet: Lenkverhalten verwenden.

Lenkverhalten soll mit improvisatorischer Navigation realistische Bewegungsmuster erzeugen.  Sie basieren auf einfachen Kräften, die bei jedem Spielupdate kombiniert werden, und sind daher von Natur aus extrem dynamisch.  Dies macht sie zur perfekten Wahl, um etwas so komplexes und dynamisches wie ein Hockey- oder Fußballspiel umzusetzen.

Umfang der Arbeit

Um Zeit und Unterricht zu sparen, reduzieren wir den Spielumfang etwas.  Unser Hockeyspiel folgt nur einem kleinen Satz der ursprünglichen Regeln des Sports: In unserem Spiel gibt es keine Strafen und keine Torhüter, sodass sich jeder Athlet auf der Eisbahn bewegen kann:

Hockeyspiel mit vereinfachten Regeln.

Jedes Tor wird durch eine kleine "Mauer" ohne Netz ersetzt.  Um zu punkten, muss ein Team den Puck (die Scheibe) bewegen, damit er eine Seite des gegnerischen Tores berührt.  Wenn jemand punktet, organisieren sich beide Teams neu und der Puck wird in der Mitte platziert. Das Spiel wird einige Sekunden danach erneut gestartet.

Zum Puck-Handling: Wenn ein Athlet, sagen wir A, den Puck hat und von einem Gegner berührt wird, sagen Sie B, dann gewinnt B den Puck und A wird für einige Sekunden unbeweglich.  Wenn der Puck die Eisbahn verlässt, wird er sofort in der Mitte der Eisbahn platziert.

Ich werde die Flixel-Spiel-Engine verwenden, um mich um den grafischen Teil des Codes zu kümmern.  Der Engine-Code wird in den Beispielen jedoch vereinfacht oder weggelassen, um den Fokus auf das Spiel selbst zu behalten.

Die Umwelt gestalten

Beginnen wir mit der Spielumgebung, die aus einer Eisbahn, einer Anzahl von Athleten und zwei Toren besteht.  Die Eisbahn besteht aus vier Rechtecken, die um die Eisfläche angeordnet sind. Diese Rechtecke kollidieren mit allem, was sie berührt, sodass nichts die Eisfläche verlassen wird.

Ein Athlet wird von der Athletenklasse beschrieben:

Die Eigenschaft mBoid ist eine Instanz der Boid-Klasse, eine Kapselung der mathematischen Logik, die in der Steuerungsverhaltensreihe verwendet wird.  Die mBoid-Instanz enthält unter anderem mathematische Vektoren, die die aktuelle Richtung, die Lenkkraft und die Position der Entität beschreiben.

Die update() -Methode in der Athlete-Klasse wird bei jeder Aktualisierung des Spiels aufgerufen.  Für den Moment löscht es nur eine aktive Lenkkraft, fügt eine Wanderkraft hinzu und ruft schließlich mBoid.update() auf.  Mit dem vorherigen Befehl wird die gesamte in mBoid eingeschlossene Steuerungsverhaltenslogik aktualisiert, sodass sich der Athlet bewegt (mithilfe der Euler-Integration).

Die Spielklasse, die für die Spielschleife verantwortlich ist, wird PlayState genannt.  Es hat die Eisbahn, zwei Gruppen von Athleten (eine Gruppe pro Team) und zwei Ziele:

Angenommen, ein einzelner Athlet wurde zum Match hinzugefügt, ist unten das Ergebnis von allem bisher:

Dem Mauszeiger folgen

Der Athlet muss dem Mauszeiger folgen, damit der Spieler tatsächlich etwas steuern kann.  Da der Mauszeiger eine Position auf dem Bildschirm hat, kann er als Ziel für das Ankunftsverhalten verwendet werden.

Das Ankunftsverhalten bewirkt, dass ein Athlet die Cursorposition sucht, die Geschwindigkeit langsam verlangsamt, wenn er sich dem Cursor nähert, und stoppt schließlich dort.

In der Athlete-Klasse ersetzen wir die Wandermethode durch das Ankunftsverhalten:

Das Ergebnis ist ein Athlet, der den Mauszeiger bewegen kann.  Da die Bewegungslogik auf Lenkverhalten basiert, navigieren die Athleten auf der Bahn überzeugend und geschmeidig.

Verwenden Sie den Mauszeiger, um den Athleten in der folgenden Demo zu führen:

Hinzufügen und Steuern des Pucks

Der Puck wird durch die Klasse Puck dargestellt.  Die wichtigsten Teile sind die update() - Methode und die mOwner-Eigenschaft:

Nach derselben Logik des Athleten wird die update() -Methode des Pucks bei jeder Aktualisierung des Spiels aufgerufen.  Die Eigenschaft mOwner bestimmt, ob der Puck im Besitz eines Athleten ist.  Wenn mOwner den Wert null hat, bedeutet dies, dass der Puck "frei" ist, und er wird sich bewegen und schließlich von den Spaziergängen der Eisbahn abprallen.

Wenn mOwner nicht null ist, bedeutet dies, dass der Puck von einem Athleten getragen wird.  In diesem Fall werden Kollisionsprüfungen ignoriert und zwangsweise vor dem Athleten platziert.  Dies kann mit dem velocity vektor des Athleten erreicht werden, der auch der Richtung des Athleten entspricht:

Erklärung, wie der Puck vor dem Athleten platziert wird.

Der Ahead-Vektor ist eine Kopie des velocity vektors des Athleten und zeigt in dieselbe Richtung.  Nachdem sich der ahead normalisiert hat, kann er um einen beliebigen Wert (z. B. 30) skaliert werden, um zu steuern, wie weit der Puck vor dem Sportler platziert wird.

Schließlich erhält die Position des Pucks die Position des Athleten, die nach ahead hinzugefügt wird, und platziert den Puck an der gewünschten Position.

Unten ist der Code für all das:

In der PlayState-Klasse gibt es einen Kollisionstest, um zu prüfen, ob der Puck einen Athleten überlappt.  Wenn dies der Fall ist, wird der Athlet, der gerade den Puck berührt hat, sein neuer Besitzer.  Das Ergebnis ist ein Puck, der beim Sportler "haftet".  Führen Sie den Athleten in der folgenden Demo dazu, den Puck in der Mitte der Eisbahn zu berühren, um dies in Aktion zu sehen:


Den Puck schlagen

Es ist Zeit, den Puck zu bewegen, wenn er vom Stock getroffen wird.  Unabhängig vom Athleten, der den Puck trägt, ist zur Simulation eines Schlags mit dem Stock nur die Berechnung eines neuen Geschwindigkeitsvektors erforderlich.  Diese neue Geschwindigkeit bewegt den Puck in Richtung des gewünschten Ziels.

Ein Geschwindigkeitsvektor kann durch einen Positionsvektor von einem anderen erzeugt werden; Der neu erzeugte Vektor bewegt sich dann von einer Position zur anderen.  Genau das ist nötig, um den neuen Geschwindigkeitsvektor des Pucks nach einem Treffer zu berechnen:

Berechnung der neuen Geschwindigkeit des Pucks nach einem Treffer mit dem Stock.

In der Abbildung oben ist der Zielpunkt der Mauszeiger.  Die aktuelle Position des Pucks kann als Ausgangspunkt verwendet werden, während der Punkt, an dem sich der Puck befinden soll, nachdem er vom Stock getroffen wurde, als Endpunkt verwendet werden kann.

Der folgende Pseudocode zeigt die Implementierung von goFromStickHit(), einer Methode in der Puck-Klasse, die die im obigen Bild dargestellte Logik implementiert:

Der new_velocity-Vektor bewegt sich von der aktuellen Position des Pucks zum Ziel (theDestination).  Danach wird es von theSpeed normalisiert und skaliert, wodurch die Größe (Länge) von new_velocity definiert wird.  Diese Operation definiert mit anderen Worten, wie schnell sich der Puck von seiner aktuellen Position zum Ziel bewegt.  Schließlich wird der velocity vektor des Pucks durch new_velocity ersetzt.

In der PlayState-Klasse wird die goFromStichHit() - Methode jedes Mal aufgerufen, wenn der Player auf den Bildschirm klickt.  In diesem Fall wird der Mauszeiger als Ziel für den Treffer verwendet.  Das Ergebnis ist in dieser Demo zu sehen:

Hinzufügen des A.I.

Bis jetzt hatten wir nur einen einzigen Sportler, der sich auf der Eisbahn bewegte.  Wenn mehr Athleten hinzugefügt werden, muss die KI implementiert werden, damit alle diese Athleten so aussehen, als würden sie lebendig und denkend.

Um dies zu erreichen, verwenden wir eine stapelbasierte Zustandsmaschine (Stack-based FSM, kurz: FSM).  Wie zuvor beschrieben, sind FSMs vielseitig und nützlich für die Implementierung von KI in Spielen.

Für unser Hockeyspiel wird der Athlete-Klasse eine Eigenschaft namens mBrain hinzugefügt:

Diese Eigenschaft ist eine Instanz von StackFSM, einer zuvor im FSM-Lernprogramm verwendeten Klasse.  Es verwendet einen Stack, um die AI-Zustände einer Entität zu steuern.  Jeder Staat wird als Methode beschrieben; Wenn ein Status in den Stack verschoben wird, wird er zur aktiven Methode und wird bei jeder Aktualisierung des Spiels aufgerufen.

Jeder Staat führt eine bestimmte Aufgabe aus, beispielsweise den Athleten in Richtung Puck zu bewegen.  Jeder Staat ist dafür verantwortlich, sich selbst zu beenden, das heißt, er ist dafür verantwortlich, sich vom Stapel zu befreien.

Der Athlet kann jetzt vom Spieler oder von der KI gesteuert werden. Daher muss die update() - Methode in der Athlet-Klasse geändert werden, um diese Situation zu überprüfen:

Wenn die KI aktiv ist, wird mBrain aktualisiert, wodurch die derzeit aktive Statusmethode aufgerufen wird, wodurch sich der Athlet entsprechend verhält.  Wenn der Spieler die Kontrolle hat, wird mBrain alle ignoriert und der Athlet bewegt sich gemäß den Anweisungen des Spielers.

Was die Zustände betrifft, die in das Gehirn vordringen sollen: Lassen Sie uns jetzt nur zwei davon implementieren.  Ein Staat lässt sich von einem Athleten auf ein Match vorbereiten; Bei der Vorbereitung auf das Spiel bewegt sich ein Athlet zu seiner Position in der Halle und bleibt stehen und starrt den Puck an.  Der andere Staat wird den Athleten dazu bringen, einfach stillzustehen und den Puck anzustarren.

In den nächsten Abschnitten implementieren wir diese Zustände.

Der untätige Zustand

Wenn sich der Athlet im idle stand befindet, wird er aufhören sich zu bewegen und den Puck anstarren.  Dieser Status wird verwendet, wenn der Athlet sich bereits in der Eisbahn befindet und darauf wartet, dass etwas passiert, beispielsweise der Beginn des Spiels.

Der Zustand wird in der Athlete-Klasse unter der Methode idle() codiert:

Da sich diese Methode nicht vom Stapel löst, bleibt sie für immer aktiv.  In der Zukunft wird dieser Staat sich selbst platzieren, um Platz für andere Staaten wie Angriff zu schaffen, aber im Moment macht es den Trick.

Die Methode stopAndStareAt() folgt demselben Prinzip, das zur Berechnung der Puck-Geschwindigkeit nach einem Treffer verwendet wird.  Ein Vektor von der Position des Athleten zur Position des Pucks wird von der Point - mBoid.position berechnet und als neuer Geschwindigkeitsvektor des Athleten verwendet.

Dieser neue Geschwindigkeitsvektor bewegt den Athleten in Richtung Puck.  Um sicherzustellen, dass sich der Athlet nicht bewegt, wird der Vektor um 0,01 skaliert, wodurch seine Länge auf nahezu Null "schrumpft".  Der Sportler hört auf, sich zu bewegen, hält den Puck aber weiter an.

Für ein Match vorbereiten

Befindet sich der Athlet im Status prepareForMatch, bewegt er sich in Richtung seiner Ausgangsposition und bleibt dort glatt stehen.  In der Ausgangsposition sollte sich der Athlet kurz vor Beginn des Spiels befinden.  Da der Athlet am Ziel anhalten sollte, kann das Ankunftsverhalten wieder verwendet werden:

Der Staat verwendet das Ankunftsverhalten, um den Athleten zur Ausgangsposition zu bewegen.  Wenn der Abstand zwischen dem Athleten und seiner Ausgangsposition weniger als 5 beträgt, bedeutet dies, dass der Athlet am gewünschten Ort angekommen ist.  Wenn dies der Fall ist, wird sich prepareForMatch vom Stapel entfernt und in den idle stand versetzt, wodurch es zum neuen aktiven Status wird.

Im Folgenden wird das Ergebnis der Verwendung eines stapelbasierten FSM zur Steuerung mehrerer Athleten beschrieben.  Drücken Sie G, um sie an zufälligen Positionen in der Eisbahn zu prepareForMatch platzieren.


Fazit

In diesem Lernprogramm wurden die Grundlagen für die Implementierung eines Hockeyspiels mit Steuerungsverhalten und stapelbasierten Zustandsautomaten vorgestellt.  Mit einer Kombination dieser Konzepte kann sich ein Athlet auf dem Platz bewegen, indem er dem Mauszeiger folgt.  Der Athlet kann den Puck auch auf ein Ziel treffen.

Mit zwei Zuständen und einem stapelbasierten FSM können sich die Athleten neu organisieren und ihre Position in der Eisbahn vorbereiten, um sich auf das Spiel vorzubereiten.

Im nächsten Tutorial lernen Sie, wie man die Athleten angreift, indem sie den Puck zum Ziel tragen und dabei den Gegner meiden.

Verweise

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.