Machen Sie ein Tower Defense-Spiel in AS3 Feinde und grundlegende KI

Hallo Flash-Entwickler, willkommen zum zweiten Teil meines Tower Defense Game-Tutorials. Im ersten Teil haben wir den grundlegenden Mechanismus entwickelt, mit dem Sie Türme erstellen und in Richtung des Mausklicks schießen können. Aber dafür sind Türmchen nicht geeignet! In diesem Teil erweitern wir das Spiel um Feinde, grundlegende künstliche Intelligenz (KI) in Türmchen und einige weitere Spielelemente. Sind Sie bereit?


Endergebnisvorschau

Dies ist das Spiel, das wir in diesem Tutorial erstellen werden:

Klicken Sie auf die orangefarbenen Kreise, um Türmchen zu platzieren. Die roten Kreise sind Feinde, und die Zahl auf jedem steht für ihre Trefferpunkte.


Schritt 1: Zusammenfassung

Im vorherigen Tutorial haben wir ein Spiel entwickelt, das Platzhalter für die Türmchen hatte. Wir könnten Revolver einsetzen, indem Sie auf diese Platzhalter klicken, und die Revolver auf den Mauszeiger richten und Kugeln auf den Punkt ausrichten, auf den der Benutzer geklickt hat.

Wir haben mit a beendet Main Klasse, die die Spielschleife und Spiellogik hatte. Ansonsten hatten wir die Turm Klasse, die außer der aktualisieren Funktion, die den Turm drehen ließ.


Schritt 2: Eine separate Aufzählungsklasse

Wir haben zuvor die Kugeln in erstellt Main Klasse und angehängt ein ENTER_FRAME Hörer, um es zu verschieben. Das Aufzählungszeichen hatte zuvor nicht genügend Eigenschaften, um es als separate Klasse zu betrachten. In einem solchen Spiel können Aufzählungszeichen jedoch viele Variationen wie Geschwindigkeit, Schaden usw. haben. Es ist daher ratsam, den Aufzählungscode herauszuziehen und in einem separaten Code zu kapseln Kugel Klasse. Machen wir das.

Erstellen Sie eine neue Klasse namens Kugel, Erweiterung der Sprite Klasse. Der grundlegende Code für diese Klasse sollte lauten:

 package import flash.display.Sprite; public class Bullet erweitert Sprite public function Bullet () 

Als Nächstes setzen wir den Code zum Zeichnen der Aufzählungsgrafik aus Main, im Kugel. Wie wir es mit dem gemacht haben Turm Klasse erstellen wir eine Funktion namens zeichnen in dem Kugel Klasse:

 private Funktion draw (): void var g: Graphics = this.graphics; g.beginFill (0xEEEEEE); g.DrawCircle (0, 0, 5); g.endFill (); 

Und wir nennen diese Funktion aus dem Kugel Konstrukteur:

 öffentliche Funktion Bullet () draw (); 

Jetzt fügen wir der Aufzählungslinie einige Eigenschaften hinzu. Fügen Sie vier Variablen hinzu: Geschwindigkeit, speed_x, schnell und Beschädigung, Vor dem Kugel Konstrukteur:

 private var Geschwindigkeit: Anzahl; private var speed_x: Anzahl; private var speed_y: Anzahl; öffentlicher Var-Schaden: int;

Wozu dienen diese Variablen??

  • Geschwindigkeit: Diese Variable speichert die Geschossgeschwindigkeit.
  • speed_x und schnell: Diese speichern die x- und y-Komponenten der Geschwindigkeit, so dass die Berechnung des Zerreißens der Geschwindigkeit in ihre Komponenten nicht immer wieder durchgeführt werden muss.
  • Beschädigung: Dies ist die Menge an Schaden, die die Kugel einem Feind zufügen kann. Wir halten diese Variable öffentlich, da wir dies in unserer Spielschleife in der Main Klasse.

Wir initialisieren diese Variablen im Konstruktor. Aktualisieren Sie Ihre Kugel Konstrukteur:

 öffentliche Funktion Aufzählungszeichen (Winkel: Anzahl) Geschwindigkeit = 5; Schaden = 1; speed_x = Math.cos (Winkel * Math.PI / 180) * Geschwindigkeit; speed_y = Math.sin (Winkel * Math.PI / 180) * Geschwindigkeit; zeichnen(); 

Beachten Sie die Winkel Variable erhalten wir im Konstruktor. Dies ist die Richtung (in Grad), in der sich die Kugel bewegt. Wir brechen einfach die Geschwindigkeit in seine x- und y-Komponenten und zwischenspeichern, um sie später zu verwenden.

Das letzte, was in der bleibt Kugel Klasse soll eine haben aktualisieren Funktion, die aus der Spieleschleife aufgerufen wird, um die Kugel zu aktualisieren (zu bewegen). Fügen Sie am Ende des Programms die folgende Funktion hinzu Kugel Klasse:

 öffentliche Funktion update (): void x + = speed_x; y + = Geschwindigkeit_y; 

Bingo! Wir sind fertig mit unserem Kugel Klasse.


Schritt 3: Aktualisieren der Hauptklasse

Wir haben eine Menge Bullet-Code von entfernt Main Klasse für sich Kugel Klasse, also bleibt viel Code ungenutzt in Main und viel muss aktualisiert werden.

Löschen Sie zuerst das createBullet () und moveBullet () Funktionen. Entfernen Sie auch die bullet_speed Variable.

Gehen Sie als nächstes zum schießen Funktion und aktualisieren Sie es mit dem folgenden Code:

 privates Funktionsshooting (e: MouseEvent): void for each (var-Revolver: Revolver in Revolver) var new_bullet: Bullet = neues Bullet (Turret.rotation); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; addChild (new_bullet); 

Wir benutzen das nicht mehr createBullet Funktion zum Erstellen von Aufzählungszeichen verwenden Sie stattdessen die Kugel Konstruktor und übergeben Sie den Turm Drehung Dies ist die Bewegungsrichtung der Kugel, und wir müssen sie nicht in der Kugel speichern Drehung Eigentum wie früher. Wir hängen auch keinen Listener an der Kugel an, da die Kugel innerhalb der nächsten Spielschleife aktualisiert wird.


Schritt 4: Speichern der Bullet-Referenzen

Jetzt, da wir die Kugeln aus der Spielschleife aktualisieren müssen, benötigen wir eine Referenz, um irgendwo gespeichert zu werden. Die Lösung ist die gleiche wie für die Türmchen: Erstellen Sie einen neuen Array genannt Kugeln und drücken Sie die Kugeln darauf, wenn sie erstellt werden.

Zuerst deklarieren Sie ein Array direkt unter dem Türmchen Array-Deklaration:

 private var ghost_turret: Turm; private var-Geschütztürme: Array = []; private var Aufzählungszeichen: Array = [];

Nun füllen Sie dieses Array aus. Wir machen das immer dann, wenn wir eine neue Kugel erstellen - also in der schießen Funktion. Fügen Sie unmittelbar vor dem Hinzufügen des Aufzählungszeichens zur Bühne Folgendes hinzu:

 var new_bullet: Bullet = neues Bullet (Turret.rotation); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet);

Schritt 5: Aktualisieren Sie die Aufzählungszeichen

So wie wir die Türme der Spielschleife aktualisieren, werden auch die Kugeln aktualisiert. Aber diesmal anstelle von ein für jeden Schleife verwenden wir eine grundlegende zum Schleife. Zuvor müssen wir zwei Variablen oben in der Spielschleife hinzufügen, damit wir wissen, welche Variablen in der Spielschleife verwendet werden und diese für die Speicherbereinigung freigeben können.

 var Turm: Turm; var bullet: Kugel;

Fügen Sie am Ende der Spielschleife den folgenden Code hinzu:

 für (var i: int = Geschosse. Länge - 1; i> = 0; i -) Geschoss = Geschosse [i]; if (! bullet) weiter; bullet.update (); 

Hier durchfahren wir alle Kugeln auf der Bühne und nennen sie ihre aktualisieren Funktion, die sie bewegen lässt. Beachten Sie hier, dass wir das iterieren Kugeln Array umgekehrt. Warum? Wir werden das vor uns sehen.

Jetzt haben wir eine Turm außerhalb bereits deklarierte Variable, müssen wir sie nicht erneut innerhalb von deklarieren für jeden Schleife von Türmchen. Ändern Sie es zu:

 für jedes (Revolver in Revolver) turret.update (); 

Schließlich fügen wir die Randüberprüfungsbedingung hinzu. Dies war zuvor in der Kugel ENTER_FRAME aber jetzt überprüfen wir es in der Spieleschleife:

 if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); fortsetzen; 

Wir prüfen, ob sich das Geschoss außerhalb der Bühnengrenze befindet. Wenn dies der Fall ist, entfernen wir zuerst seine Referenz von der Bühne Kugeln Array mit der spleißen Funktion, und entfernen Sie die Kugel von der Bühne und fahren Sie mit der nächsten Iteration fort. So sollte Ihre Spielschleife aussehen:

 private Funktion gameLoop (e: Event): void var turret: Turret; var bullet: Kugel; für jedes (Revolver in Revolver) turret.update ();  für (var i: int = Aufzählungszeichen.Länge - 1; i> = 0; i--) Aufzählungszeichen = Aufzählungszeichen [i]; if (! bullet) weiter; bullet.update (); 

Wenn Sie das Spiel jetzt ausführen, sollten Sie dieselbe Funktionalität wie in Teil 1 haben, wobei der Code viel sauberer und übersichtlicher ist.


Schritt 6: Den Feind präsentieren

Jetzt fügen wir eines der wichtigsten Elemente des Spiels hinzu: den Feind. Als Erstes erstellen Sie eine neue Klasse mit dem Namen Feind Erweiterung der Sprite Klasse:

 package import flash.display.Sprite; public class Enemy erweitert Sprite public function Enemy () 

Jetzt fügen wir der Klasse einige Eigenschaften hinzu. Fügen Sie sie vor Ihrem hinzu Feind Konstrukteur:

 private var speed_x: Anzahl; private var speed_y: Anzahl;

Wir initialisieren diese Variablen im Feind Konstrukteur:

 öffentliche Funktion Enemy () speed_x = -1.5; speed_y = 0; 

Als nächstes erstellen wir die zeichnen und aktualisieren Funktionen für die Feind Klasse. Diese sind denjenigen von sehr ähnlich Kugel. Fügen Sie den folgenden Code hinzu:

 private Funktion draw (): void var g: Graphics = this.graphics; g.beginFill (0xff3333); g.DrawCircle (0, 0, 15); g.endFill ();  public function update (): void x + = Geschwindigkeit_x; y + = Geschwindigkeit_y; 

Schritt 7: Timing der Spielereignisse

In unserem Spiel müssen wir viele Ereignisse haben, die zu bestimmten Zeiten oder wiederholt in bestimmten Abständen stattfinden. Ein solches Timing kann mit einem Zeitzähler erreicht werden. Der Zähler ist nur eine Variable, die mit der Zeit im Spiel erhöht wird. Wichtig ist hier, wann und um wie viel Betrag der Zähler erhöht wird. Das Timing wird in der Regel auf zwei Arten durchgeführt: Zeitbasiert und Frame-basiert.

Der Unterschied besteht darin, dass die auf Zeit basierende Spieleinheit auf Echtzeit basiert (d. H. Die Anzahl der durchgelassenen Millisekunden). In einem Frame-basierten Spiel basiert die Schritteinheit jedoch auf Frame-Einheiten (d. H. Die Anzahl der durchlaufenden Frames).

Für unser Spiel verwenden wir einen Frame-basierten Counter. Wir haben einen Zähler, den wir in der Spielschleife, in dem jeder Frame ausgeführt wird, um eins erhöhen, und im Grunde die Anzahl der Frames angeben, die seit dem Start des Spiels vergangen sind. Gehen Sie voran und deklarieren Sie eine Variable nach den anderen Variablendeklarationen in der Main Klasse:

 private var ghost_turret: Turm; private var-Geschütztürme: Array = []; private var Aufzählungszeichen: Array = []; private var global_time: Anzahl = 0;

Wir erhöhen diese Variable in der Spielschleife oben:

 global_time ++;

Basierend auf diesem Zähler können wir beispielsweise Feinde erstellen, was wir als nächstes tun werden.


Schritt 8: Lassen Sie uns einige Feinde erstellen

Was wir jetzt tun wollen, ist, alle zwei Sekunden Feinde auf dem Spielfeld zu erstellen. Aber wir haben es hier mit Frames zu tun. Nach wie vielen Frames sollten wir also Feinde erstellen? Nun, unser Spiel läuft mit 30 FPS und erhöht damit die Geschwindigkeit global_time 30 mal pro Sekunde. Eine einfache Berechnung sagt uns, dass 3 Sekunden = 90 Frames sind.

Fügen Sie am Ende der Spielschleife Folgendes hinzu ob Block:

 if (global_time% 90 == 0) 

Worum geht es in diesem Zustand? Wir verwenden den Modulo-Operator (%), der den Rest einer Division ergibt - also global_time% 90 gibt uns den Rest wann global_time wird durch geteilt 90. Wir prüfen, ob es sich um den Rest handelt 0, da dies nur dann der Fall sein wird, wenn global_time ist ein Vielfaches von 90 - das heißt, die Bedingung kehrt zurück wahr wann global_time gleich 0, 90, 180 und so weiter… Auf diese Weise erreichen wir alle 90 Bilder oder 3 Sekunden einen Trigger.

Bevor wir den Feind erstellen, deklarieren Sie ein anderes Array namens Feinde knapp unterhalb der Türmchen und Kugeln Array. Dies wird verwendet, um Verweise auf Gegner auf der Bühne zu speichern.

 private var ghost_turret: Turm; private var-Geschütztürme: Array = []; private var Aufzählungszeichen: Array = []; private var-feinde: Array = []; private var global_time: Anzahl = 0;

Erkläre auch eine Feind Variable am oberen Rand der Spielschleife:

 global_time ++; var Turm: Turm; var bullet: Kugel; var Feind: Feind;

Fügen Sie schließlich den folgenden Code in das ein ob Block, den wir zuvor erstellt haben:

 Feind = neuer Feind (); Feind.x = 410; Feind.y = 30 + Math.random () * 370; Feinde.push (Feind); addChild (Feind);

Hier erstellen wir einen neuen Feind, positionieren ihn zufällig rechts von der Bühne, schieben ihn in die Feinde Array und fügen Sie es der Bühne hinzu.


Schritt 9: Aktualisieren der Feinde

So wie wir die Kugeln in der Spielschleife aktualisieren, aktualisieren wir die Feinde. Fügen Sie den folgenden Code unter dem Revolver ein für jeden Schleife:

 for (var j: int = Feinde. Länge - 1; j> = 0; j--) Feind = Feinde [j]; Feind.Update (); wenn (Feind.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

So wie wir eine Begrenzungsprüfung für Kugeln durchgeführt haben, überprüfen wir auch die Feinde. Bei Feinden prüfen wir nur, ob sie die linke Seite der Bühne verlassen haben, da sie sich nur von rechts nach links bewegen. Sie sollten Feinde von rechts kommen sehen, wenn Sie das Spiel jetzt ausführen.


Schritt 10: Gib den Feinden etwas Gesundheit

Jeder Feind hat etwas Leben / Gesundheit und unsere auch. Wir werden auch die verbleibende Gesundheit der Feinde zeigen. Lässt einige Variablen im deklarieren Feind Klasse für das Gesundheitswesen:

 private var health_txt: TextField; private var Gesundheit: int; private var speed_x: Anzahl; private var speed_y: Anzahl;

Wir initialisieren die Gesundheit Variable im Konstruktor weiter. Fügen Sie Folgendes dem hinzu Feind Konstrukteur:

 Gesundheit = 2;

Jetzt initialisieren wir die Health-Textvariable, um sie im Zentrum des Feindes anzuzeigen. Wir machen das im zeichnen Funktion:

 health_txt = new TextField (); health_txt.height = 20; health_txt.width = 15; health_txt.textColor = 0xffffff; health_txt.x = -5; health_txt.y = -8; health_txt.text = Gesundheit + ""; addChild (health_txt);

Alles, was wir tun, ist ein neues zu erstellen Textfeld, Legen Sie die Farbe fest, positionieren Sie sie und setzen Sie ihren Text auf den aktuellen Wert von Gesundheit Schließlich fügen wir eine Funktion hinzu, um die Gesundheit des Feindes zu aktualisieren:

 öffentliche Funktion updateHealth (Betrag: int): int Gesundheit + = Betrag; health_txt.text = Gesundheit + ""; Gesundheit zurückgeben; 

Die Funktion akzeptiert eine Ganzzahl, die zum Zustand hinzugefügt werden soll, aktualisiert den Zustandstext und gibt den endgültigen Zustand zurück. Wir rufen diese Funktion in unserer Spieleschleife auf, um die Gesundheit jedes Gegners zu aktualisieren und festzustellen, ob er noch lebt.


Schritt 11: Die Feinde schießen.

Zuerst können wir unsere ändern schießen ein bisschen funktionieren. Ersetzen Sie das vorhandene schießen Funktion mit Folgendem:

 private Funktion schießen (Turm: Turm, Feind: Feind): void Var Winkel: Anzahl = Math.atan2 (Feind.y - Turm.y, Feind.x - Turm.x) / Math.PI * 180; Drehbewegung = Winkel; var new_bullet: Bullet = new Bullet (Winkel); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet); 

Das schießen Funktion akzeptiert jetzt zwei Parameter. Der erste ist ein Hinweis auf einen Turm, der das Schießen übernimmt. Die zweite ist ein Hinweis auf einen Feind, auf den er schießen soll.

Der neue Code ähnelt dem in der Turm Klasse aktualisieren Funktion, aber anstelle der Mausposition verwenden wir jetzt die Koordinaten des Feindes. So können Sie jetzt den gesamten Code aus dem entfernen aktualisieren Funktion des Turm Klasse.

Wie kann man die Türmchen auf Feinde schießen lassen? Nun, die Logik ist für unser Spiel einfach. Wir lassen alle Türmchen auf den ersten Feind im schießen Feinde Array. Was? Lassen Sie uns etwas Code setzen und dann versuchen zu verstehen. Addiere folgende Zeilen am Ende des für jeden Schleife zum Aktualisieren der Revolver:

 für jedes (Revolver in Revolver) turret.update (); für jeden (Feind in Feinden) schießen (Turm, Feind); brechen; 

Für jeden Turm aktualisieren wir ihn jetzt, iterieren dann die Feinde schießen Sie den ersten Gegner im Array und brechen Sie die Schleife ab. Im Wesentlichen schießt jeder Turm auf den frühesten erschaffenen Gegner, da er sich immer am Anfang des Arrays befindet. Versuchen Sie, das Spiel auszuführen, und Sie sollten Türme sehen, die die Feinde erschießen.

Aber warten Sie, was fließt dieser Kugelstrom? Sieht aus, als würden sie zu schnell schießen. Mal sehen warum.


Schritt 12: Türmchen schießen zu schnell

Wie wir wissen, läuft die Spielschleife jedes Bild, d. H. In unserem Fall 30-mal pro Sekunde, so dass die Schießaussage, die wir im vorherigen Schritt hinzugefügt haben, mit der Geschwindigkeit unserer Spielschleife aufgerufen wird, und wir sehen einen Fluss von Kugeln. Sieht aus, als hätten wir auch einen Timing-Mechanismus in den Türmchen. Wechseln Sie zum Turm Klasse und fügen Sie den folgenden Code hinzu:

 private var local_time: Anzahl = 0; private var reload_time: int;
  1. Ortszeit: Unser Zähler wird gerufen Ortszeit im Gegensatz zu dem global_time in dem Main Klasse. Dies hat zwei Gründe: erstens, weil diese Variable lokal ist Turm Klasse; Zweitens, weil es nicht immer so vorgeht wie bei uns global_time Variable - wird im Laufe des Spiels mehrmals zurückgesetzt.
  2. Nachladezeit: Dies ist die Zeit, die der Revolver nach dem Schießen einer Kugel zum Laden benötigt. Im Grunde ist es der Zeitunterschied zwischen zwei Kugelschüssen eines Turms. Denken Sie daran, dass alle Zeiteinheiten in unserem Spiel Frames sind.

Inkrementieren Sie die Ortszeit Variable in der aktualisieren Funktion und initialisieren die Nachladezeit im Konstruktor:

 öffentliche Funktion update (): void local_time ++; 
 öffentliche Funktion Turret () reload_time = 30; zeichnen(); 

Als nächstes fügen Sie die folgenden zwei Funktionen am Ende von hinzu Turm Klasse:

 public function isReady (): Boolean return local_time> reload_time;  public function reset (): void local_time = 0; 

ist bereit gibt nur dann true zurück, wenn der aktuelle Ortszeit ist größer als der Nachladezeit, d. h. wenn der Turm erneut geladen wurde. Und das zurücksetzen Funktion setzt einfach das zurück Ortszeit Variable, um es erneut zu laden.

Nun zurück in die Main Klasse, ändern Sie den Schießcode in der Spielschleife, die wir im vorherigen Schritt hinzugefügt haben:

 für jedes (Revolver in Revolver) turret.update (); if (! turret.isReady ()) fortsetzen; für jeden (Feind in Feinden) schießen (Turm, Feind); turret.reset (); brechen; 

Wenn nun der Turm nicht fertig ist (ist bereit() kehrt zurück falsch) fahren wir mit der nächsten Iteration der Revolverschleife fort. Sie werden sehen, dass die Türmchen jetzt im Abstand von 30 Bildern oder 1 Sekunde ausgelöst werden. Cool!


Schritt 13: Begrenzen Sie den Revolverbereich

Immer noch etwas nicht richtig. Die Türme schießen auf Feinde ungeachtet der Entfernung zwischen ihnen. Was hier fehlt, ist das Angebot eines Turms. Jeder Turm sollte eine eigene Reichweite haben, in die er einen Gegner schießen kann. Fügen Sie eine weitere Variable zum hinzu Turm Klasse aufgerufen Angebot und setze es auf 120 im Konstruktor:

 private var reload_time: int; private var local_time: Anzahl = 0; privater var Bereich: int;
 öffentliche Funktion Turret () reload_time = 30; Bereich = 120; zeichnen(); 

Fügen Sie auch eine aufgerufene Funktion hinzu canShoot am Ende des Unterrichts:

 public function canShoot (Feind: Feind): Boolean var dx: Number = enemy.x - x; var dy: Anzahl = Feind.y - y; if (Math.sqrt (dx * dx + dy * dy) <= range) return true; else return false; 

Jeder Turm kann nur dann auf einen Gegner schießen, wenn er bestimmte Kriterien erfüllt. Zum Beispiel können Sie den Turm nur auf rote Gegner mit weniger als der Hälfte ihrer Lebensdauer und höchstens 30 Pixeln entfernt lassen. All diese Logik, um zu bestimmen, ob der Turm einen Feind abschießen kann oder nicht, geht in die canShoot Funktion, die zurückkehrt wahr oder falsch nach der Logik.

Unsere Logik ist einfach. Wenn der Feind in Reichweite ist, kehre zurück wahr; sonst falsch zurückgeben. Wenn also die Entfernung zwischen dem Turm und dem Feind (Math.sqrt (dx * dx + dy * dy)) ist kleiner oder gleich Angebot, es kehrt zurück wahr. Ein wenig mehr Modifikation im Shoot-Bereich der Spielschleife:

 für jedes (Revolver in Revolver) turret.update (); if (! turret.isReady ()) fortsetzen; für jeden (Feind in Feinden) if (turret.canShoot (Feind)) schießen (Turret, Feind); turret.reset (); brechen; 

Nur wenn der Feind sich innerhalb der Reichweite des Turms befindet, wird der Turm schießen.


Schritt 14: Kollisionserkennung

Ein sehr wichtiger Teil jedes Spiels ist die Kollisionserkennung. In unserem Spiel wird eine Kollisionsprüfung zwischen Kugeln und Feinden durchgeführt. Wir werden den Kollisionserkennungscode in die für jeden Schleife, die die Kugeln in der Spielschleife aktualisiert.

Die Logik ist einfach. Für jede Kugel durchqueren wir die Feinde Array und überprüfen, ob es eine Kollision zwischen ihnen gibt. In diesem Fall entfernen wir die Kugel, aktualisieren die Gesundheit des Feindes und brechen aus der Schleife, um andere Feinde zu überprüfen. Fügen wir etwas Code hinzu:

 für (i = Geschosse. Länge - 1; i> = 0; i--) Geschoss = Geschosse [i]; // Wenn das Aufzählungszeichen nicht definiert ist, fahren Sie mit der nächsten Iteration fort. if (! bullet) continue; bullet.update (); if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); fortsetzen;  for (var k: int = Feinde. Länge - 1; k> = 0; k--) Feind = Feinde [k]; if (bullet.hitTestObject (Feind)) bullets.splice (i, 1); bullet.parent.removeChild (bullet); if (enemy.updateHealth (-1) == 0) enemies.splice (k, 1); Feind.Parent.RemoveChild (Feind);  brechen; 

Wir verwenden ActionScript hitTestObject Funktion zur Überprüfung auf Kollision zwischen Kugel und Feind. Wenn die Kollision auftritt, wird das Geschoss auf dieselbe Weise entfernt wie beim Verlassen der Bühne. Die Gesundheit des Feindes wird dann mithilfe von aktualisiert updateHealth Methode, zu der Kugel's Beschädigung Eigentum wird übergeben. Wenn die updateHealth Funktion gibt eine ganze Zahl zurück, die kleiner oder gleich ist 0, Das bedeutet, dass der Feind tot ist, und wir entfernen ihn genauso wie die Kugel.

Und unsere Kollisionserkennung ist fertig!


Schritt 15: Warum die "Für" -Schleifen umkehren??

Denken Sie daran, dass wir die Feinde und Kugeln in unserer Spielschleife rückwärts durchqueren. Lass uns verstehen warum. Angenommen, wir haben einen Aufstieg benutzt zum Schleife. Wir sind im Index i = 3 und wir entfernen eine Kugel aus dem Array. Bei Entfernung des Artikels an Position 3, Sein Platz wird durch den Artikel dann an Position gefüllt 4. Also jetzt der Artikel vorher an Position 4 ist um 3. Nach der Iteration ich erhöht sich um 1 und wird 4 und so Position an Position 4 wird geprüft.

Ups, siehst du, was gerade passiert ist? Wir haben den Artikel gerade auf Position verpasst 3 die als Ergebnis des Spleißens nach hinten verschoben. Und so verwenden wir eine Umkehrung zum Schleife, die dieses Problem beseitigt. Sie können sehen warum.


Schritt 16: Anzeige der Reichweite des Turms

Lassen Sie uns ein paar Extras hinzufügen, damit das Spiel gut aussieht. Wir werden Funktionen hinzufügen, um den Bereich eines Revolvers anzuzeigen, wenn die Maus darauf bewegt wird. Wechseln Sie zum Turm Klasse und fügen Sie einige Variablen hinzu:

 privater var Bereich: int; private var reload_time: int; private var local_time: Anzahl = 0; privater var Körper: Sprite; private var range_circle: Sprite;

Nächstes Update der zeichnen Funktion auf Folgendes:

 private Funktion draw (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.DrawCircle (0, 0, Bereich); g.endFill (); range_circle.alpha = 0,2; range_circle.visible = false; addChild (range_circle); body = new Sprite (); var g: Graphics = Körpergrafiken; g.beginFill (0xD7D700); g.DrawCircle (0, 0, 20); g.beginFill (0x800000); g.DrawRect (0, -5, 25, 10); g.endFill (); addChild (Körper); 

Wir unterteilen die Grafiken des Revolvers in zwei Teile: den Körper und die Bereichsgrafik. Wir tun dies, um die verschiedenen Teile des Turms zu ordnen. Hier brauchen wir die range_circle Hinter dem Körper des Turms zu sein, und so fügen wir ihn zuerst der Bühne hinzu. Zum Schluss fügen wir zwei Maus-Listener hinzu, um die Bereichsgrafik umzuschalten:

 private Funktion onMouseOver (e: MouseEvent): void range_circle.visible = true;  private Funktion onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Verbinden Sie nun die Listener mit den jeweiligen Ereignissen am Ende des Konstruktors:

 body.addEventListener (MouseEvent.MOUSE_OVER, onMouseOver); body.addEventListener (MouseEvent.MOUSE_OUT, onMouseOut);

Wenn Sie das Spiel ausführen und versuchen, einen Turm bereitzustellen, wird ein Flackern angezeigt, wenn Sie sich auf den Platzhaltern befinden. Warum das?


Sehen Sie das Flimmern?

Schritt 17: Entfernen des Flimmerns

Erinnern wir uns, dass wir die mouseEnabled Eigentum des Geisterturms an falsch? Wir haben das getan, weil der Geisterrevolver Mausereignisse erfasst hat, indem er zwischen Maus und Platzhalter kam. Die gleiche Situation ist wieder da, da der Turm selbst jetzt zwei Kinder hat - seinen Körper und das Range-Sprite -, die die Mausereignisse dazwischen erfassen.

Die Lösung ist die gleiche. Wir können uns individuell einstellen mouseEnabled Eigenschaften zu falsch. Eine bessere Lösung ist jedoch die Einstellung der Geisterrevolver mouseChildren Eigentum an falsch. Dies bedeutet, dass alle Kinder des Geister-Revolveres Mausereignisse erhalten. Ordentlich, was? Gehen Sie voran und setzen Sie es auf falsch in dem Main Konstrukteur:

 ghost_turret = new Turret (); ghost_turret.alpha = 0,5; ghost_turret.mouseEnabled = false; ghost_turret.mouseChildren = false; ghost_turret.visible = false; addChild (ghost_turret);

Problem gelöst.

Schritt 18: Wie geht es weiter??

Wir könnten diese Demo um weitere erweiterte Funktionen erweitern und sie in ein spielbares Spiel verwandeln. Einige davon könnten sein:

  1. Bessere KI-Logik zum Auswählen und Schießen von Feinden.
  2. Verschiedene Arten von Geschützen, Kugeln und Feinden im Spiel.
  3. Komplexe feindliche Wege anstelle von geraden Linien.

Mal sehen, was Sie aus dieser grundlegenden Demo herausfinden können. Ich freue mich über Ihre Tower Defense-Spiele und Ihre Kommentare oder Vorschläge für die Serie.