Erstelle animierte Retro-Spiele mit JavaScript | Frank Dvorak | Skillshare
Suchen

Playback-Geschwindigkeit


1.0x


  • 0.5x
  • 0.75x
  • 1x (normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Erstelle animierte Retro-Spiele mit JavaScript

teacher avatar Frank Dvorak, Creative Coding

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Wähle aus einer Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Wähle aus einer Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Einheiten dieses Kurses

    • 1.

      Projekt 1 Einführung

      1:02

    • 2.

      Funktionen von Projekt 1

      1:27

    • 3.

      Projekteinrichtung

      4:24

    • 4.

      Spiel- und Spielerobjekte

      5:36

    • 5.

      Tastatursteuerungen

      7:26

    • 6.

      Objektpool

      13:01

    • 7.

      Feindliche Wellen

      13:33

    • 8.

      Kollisionserkennung

      8:05

    • 9.

      Partitur und Statustext

      12:48

    • 10.

      Neustart-Methode

      5:46

    • 11.

      Beetlemorph-Feind-Klasse

      4:50

    • 12.

      Sprite-Animation erklärt

      10:35

    • 13.

      Animations-Timing

      10:15

    • 14.

      Player-Animation

      13:08

    • 15.

      Zusätzliche Funktionen: Gepanzerte Feinde

      0:57

    • 16.

      Feind-Kurs Rhinomorph

      9:24

    • 17.

      Zusätzliche Funktionen: Boss-Schlachten

      0:30

    • 18.

      Boss-Kurs

      9:27

    • 19.

      Boss-Bewegung

      10:22

    • 20.

      Boss vs. Spieler-Kollision

      7:07

    • 21.

      Zusätzliche Funktionen: Superwaffen

      0:43

    • 22.

      2 Laserkurse

      8:01

    • 23.

      Laserschäden

      8:01

    • 24.

      Ressourcenmanagement

      7:05

    • 25.

      Eaglemorph-Feind-Klasse

      6:55

    • 26.

      Feindliche Projektile

      6:21

    • 27.

      Projektil-Interaktionen

      4:48

    • 28.

      Squidmorph-Feind-Kurs

      9:18

    • 29.

      Feind-Klasse Lobstermorph

      2:30

    • 30.

      Projekt 2: JavaScript Planet Defense

      3:18

    • 31.

      Projekt 2: Planet und Spielkurs

      8:10

    • 32.

      Projekt 2: Mausposition

      6:18

    • 33.

      Projekt 2: Spieler-Raumschiff

      4:42

    • 34.

      Projekt 2: Ein bisschen Mathe

      7:47

    • 35.

      Projekt 2: Verstehe die Leinwand-Rotation

      6:15

    • 36.

      Projekt 2: Debug-Modus

      2:25

    • 37.

      Projekt 2: Objekt-Pool

      12:22

    • 38.

      Projekt 2: Spielerprojektile

      6:10

    • 39.

      Projekt 2: Feind-Pool

      7:25

    • 40.

      Projekt 2: Kollisionserkennung

      2:20

    • 41.

      Projekt 2: Periodische Ereignisse

      8:06

    • 42.

      Projekt 2: Asteroidenfeind

      4:21

    • 43.

      Projekt 2: Sprite-Animation

      9:15

    • 44.

      Projekt 2: Lobstermorph-Feind

      9:15

    • 45.

      Projekt 2: Spieltext

      5:26

    • 46.

      Projekt 2: Der Spieler lebt

      5:09

    • 47.

      Projekt 2: Beetlemorph-Feind

      1:36

    • 48.

      Projekt 2: Rhinomorph-Feind

      2:43

    • 49.

      Projekt 3: JavaScript-Mobilspiel

      1:08

    • 50.

      Projekt 3-Setup

      2:02

    • 51.

      Alles reaktionsschnell machen

      6:15

    • 52.

      Feind-Klasse

      7:58

    • 53.

      Designmuster für Objektpools

      3:35

    • 54.

      Periodische Auslöser

      6:50

    • 55.

      Maussteuerungen

      1:31

    • 56.

      Kollisionserkennung

      7:50

    • 57.

      Touch-Events

      1:37

    • 58.

      Spieltext

      7:04

    • 59.

      Neustart starten

      3:34

    • 60.

      Vollbild-Spiele

      4:11

    • 61.

      Simple Crew-Mitglieder

      1:30

    • 62.

      Einfacher Feindtyp

      4:23

    • 63.

      Sprite-Animation

      4:20

    • 64.

      Animations-Timing

      4:08

    • 65.

      Debug-Modus

      2:09

    • 66.

      Feindliche Vielfalt

      4:29

    • 67.

      Randomisierte Weltraumbesatzung

      3:47

    • 68.

      Zustandsmanagement in Spielen

      4:27

    • 69.

      State-Design-Muster

      15:23

    • 70.

      Sounds

      9:49

  • --
  • Anfänger-Niveau
  • Fortgeschrittenes Niveau
  • Fortgeschrittenes Niveau
  • Jedes Niveau

Von der Community generiert

Das Niveau wird anhand der mehrheitlichen Meinung der Teilnehmer:innen bestimmt, die diesen Kurs bewertet haben. Bis das Feedback von mindestens 5 Teilnehmer:innen eingegangen ist, wird die Empfehlung der Kursleiter:innen angezeigt.

98

Teilnehmer:innen

--

Projekte

Über diesen Kurs

Alt ist Gold. Lass uns von den klassischen Spielen aus den 80er Jahren inspirieren und unsere eigenen Grafiken und Spielfunktionen hinzufügen. Im ersten Projekt werden wir die interstellare Leere erkunden und 3 verschiedene Waffentypen verwenden, um durch Schwärme von Weltraumwanzen verschiedener Typen zu verbrennen, einige größer als die anderen.

Wir beginnen mit einem Spiel, das lose vom klassischen Space Invaders-Arcade inspiriert ist, und wir werden mit verschiedenen zusätzlichen Funktionen wie Boss-Schlachten, Superwaffen und verschiedenen Feindtypen experimentieren.Lasst uns

die objektorientierte Programmierung mit JavaScript erkunden und einen nützlichen Satz grundlegender 2D-Spielentwicklungstechniken implementieren, wie z.B. Designmuster für Objektpools, Sprite-Animation, Timing und Staffelungstechniken mit Zeitstempeln und vieles mehr.

Vergiss nicht, alle Bonusgeschenke herunterzuladen. Die Teilnehmer dieses Kurses erhalten ein Paket mit 2D-Spielkunst in Premiumqualität zu einem Science-Fiction-Weltraumthema. Du kannst den Quellcode auch aus mehreren Phasen des Projekts herunterladen, wenn wir nach und nach weitere Funktionen hinzufügen.

Wir werden mehrere Feindtypen implementieren:

Beetlemorph - Grundfeind, 1 Treffer reicht

Rhinomorph - gepanzerter Feind, mehrere Leben, mehrere Schadenszustände

Mantismorph - massiver Feind in Bossgröße, wachsender Lebenspool

Eaglemorph - wenn er getroffen wird, opfert er ein Körpersegment und spuckt es zurück

Squidmorph - aufblasbarer Alien-Käfer, kann er unsere Waffen absorbieren

Lobstermorphinstabile DNA, teilt sich beim Klick in mehrere kleinere Klone auf

Viel Spaß!

Triff deine:n Kursleiter:in

Teacher Profile Image

Frank Dvorak

Creative Coding

Kursleiter:in

Hello, I'm Frank. I'm a front-end web developer, owner of Frank's Laboratory YouTube channel. Come explore creative coding, game development and generative art with me.

Vollständiges Profil ansehen

Level: Intermediate

Kursbewertung

Erwartungen erfüllt?
    Voll und ganz!
  • 0%
  • Ja
  • 0%
  • Teils teils
  • 0%
  • Eher nicht
  • 0%

Warum lohnt sich eine Mitgliedschaft bei Skillshare?

Nimm an prämierten Skillshare Original-Kursen teil

Jeder Kurs setzt sich aus kurzen Einheiten und praktischen Übungsprojekten zusammen

Mit deiner Mitgliedschaft unterstützt du die Kursleiter:innen auf Skillshare

Lerne von überall aus

Ob auf dem Weg zur Arbeit, zur Uni oder im Flieger - streame oder lade Kurse herunter mit der Skillshare-App und lerne, wo auch immer du möchtest.

Transkripte

1. Einführung: Reisen wir zurück in das goldene Zeitalter der Arcade-Videospiele. In diesem Kurs werden wir HTML, CSS und Javascript verwenden, um ein Spiel zu erstellen, das von den Klassikern inspiriert ist, aber wir werden ihm unseren eigenen Kunststil und benutzerdefinierte Funktionen geben . Wir werden ein Raumschiff steuern , das sich durch immer größere Wellen von Feinden kämpfen immer größere Wellen von muss, mit gelegentlichen Explosionen dazwischen Je tiefer wir ins Unbekannte vordringen, desto mehr Waffen schalten wir frei und begegnen noch gefährlicheren außerirdischen Spezies Der Kurs ist nichts für absolute Anfänger, aber wenn du die Grundlagen wie Variablen, Arrays und Loops bereits die Grundlagen wie Variablen, Arrays kennst, erkunde mit mir Spieleentwicklung und Chat-Skript-Animation Die Schüler dieses Kurses erhalten kostenlos eine Menge hochwertiger Spielgrafiken für zwei Spiele Sie können es später auch für Ihre eigenen persönlichen Projekte verwenden und damit Ihre eigenen Spiele erstellen. Sie können auch den vollständigen Quellcode aus mehreren Phasen des Projekts herunterladen mehreren Phasen des Projekts wir ihn erweitern und immer mehr Funktionen hinzufügen. 2. Projektfunktionen: Zunächst werden wir drei Waffentypen einsetzen einen Basisangriff, einen Langstreckenlaser und einen Superlaser. Wir werden ihn brauchen, weil wir uns durch fünf verschiedene Feindtypen kämpfen müssen . Zuerst stoßen wir nur auf ein normales Leerzeichen hinten. Ein Treffer mit unserer Basiswaffe reicht aus. Wenn wir weiter forschen, stoßen wir auf gepanzerte Feinde Wir müssen sie fünfmal treffen. Wir werden den allmählichen Schaden an ihrem dicken Exoskelett nach jedem Treffer sehen können ihrem dicken Exoskelett nach jedem Treffer Wenn wir nicht vorsichtig sind, könnten wir auf einen riesigen Feind mit gefährlichen Klauen stoßen Jedes Mal, wenn ein neuer auftaucht, hat er mehr Leben als der vorherige, der die Chal***ge für den Spieler langsam erhöht Im Laufe des Spiels sind einige außerirdische Spezies bereit, sich zu rächen. Schau dir das an. Jedes Mal, wenn wir es treffen, opfert es ein Körpersegment und wirft es als mutiertes Projektil aus Eis, Schleim und Tentakeln auf uns zurück als mutiertes Projektil aus Eis, Schleim und Tentakeln auf uns aus Eis, Schleim und Tentakeln Wir werden auch gegen einen Feind kämpfen müssen, der unsere Projektile absorbiert und sich aufbläst, unsere Projektile Damit gebe ich dir eine Version der Tabellenkalkulationen in normaler Größe und in beiden Größen , damit dein Spiel noch abwechslungsreicher und unterhaltsamer Wir werden alles wie gewohnt von Grund auf neu programmieren , ohne Frameworks und ohne Bibliotheken. Ich wünsche dir viel Spaß bei diesem Java-Script-Weltraumabenteuer. Lass uns gehen. 3. Projekteinrichtung: In Index-HTML erstellen wir eine einfache leere Webseite. Ich gebe ihr einen Titel. Ich verlinke das CSS-Stylesheet, ich erstelle ein HTML-Five-Canvas-Element mit der ID Canvas One. Und ich verlinke das Skript der GS-Datei im CSS-Stil. Ich ziele auf mein Canvas-Element und gebe ihm einen Rand. Ich möchte es vertikal und horizontal in der Mitte der Webseite zentrieren. Eine Möglichkeit, dies zu tun, besteht darin, diese vier CSS-Zeilen zu verwenden. Wir können transform translate verwenden, oder wir können auch einfach die translate-Eigenschaft wie folgt verwenden. Seien Sie vorsichtig mit der Syntax, es gibt kein Komma zwischen den Werten Alles andere in dieser Klasse wird in einfachem Vanilla-Javascript geschrieben Keine Frameworks und keine Bibliotheken. In der modernen Programmierung können wir unseren Code auf viele verschiedene Arten strukturieren. Es gibt einige gängige und etablierte Programmierparadigmen , von etwas sehr Einfachem wie prozeduraler Programmierung bis hin zu funktionaler und objektorientierter Programmierung Objektorientierte Programmierung ist wahrscheinlich das beliebteste Paradigma Das Kernkonzept besteht darin, dass wir unsere Daten in Objekten organisieren. Es stützt sich normalerweise stark auf Klassen, die es uns ermöglichen, viele ähnliche Objekte auf der Grundlage eines gemeinsamen Entwurfs zu erstellen Grundlage eines gemeinsamen Entwurfs In der heutigen Klasse werden wir unseren Code sauber und organisiert halten, indem wir alles zu einem Objekt machen Und wir werden diese Objekte dazu bringen, miteinander zu sprechen. Wenn beispielsweise ein Projektilobjekt auf ein feindliches Objekt trifft, wird das gegnerische Objekt gelöscht Eines der Hauptprinzipien der objektorientierten Programmierung ist die Kapselung, was bedeutet, dass wir Daten und Methoden, die mit diesen Daten arbeiten, in separate Bündel Wir können auch den Zugriff auf diese Daten von außerhalb des Bundles einschränken , also Schließungen, private Felder usw. Wir werden unsere Daten in vier separate Klassen unterteilen, die miteinander kommunizieren und zusammenarbeiten, um das endgültige Spiel zu entwickeln Spielerklasse wird sich um die Bewegung und Animation der Hauptfigur, des Roboter-Raumschiffs Die Spielerklasse wird sich um die Bewegung und Animation der Hauptfigur, des Roboter-Raumschiffs, Diese Klasse wird nur eine Instanz haben. Klassen werden als Baupläne verwendet , um viele ähnliche Objekte zu erstellen In diesem Fall muss der Spieler keine Klasse sein, wir können ihn einfach als ein einfaches Objekt deklarieren Ich werde daraus eine Klasse machen, um die Codestruktur in der gesamten Codebasis gleich zu halten . Ich möchte, dass das für Anfänger leicht zu lesen ist. Projektilklasse wird mit Lasern umgehen , die der Spieler abfeuern wird Wir werden lernen, wie wir daraus einen wiederverwendbaren Objektpool machen können, um die Leistung massiv zu verbessern Weil das Erstellen und Löschen von Tausenden von Projektilobjekten viel Speicherplatz verschwenden würde Die feindliche Klasse wird Space Invaders zeichnen und animieren. Wir werden lernen, sie in Rastern zu organisieren und sie in immer größeren Wellen erscheinen zu lassen Im weiteren Verlauf des Spiels wird die Game-Klasse die Hauptlogik der Codebasis enthalten Dies ist das Haupthirn, das überall Befehle sendet und das alles zusammenhält. Wir werden ausgefeilte professionelle Spielgrafiken verwenden , die ich dir völlig kostenlos zur Verfügung stelle. Und wir müssen sicherstellen, dass Javascript erst ausgeführt wird nachdem alle unsere Grafikdateien geladen und verfügbar sind. Ich werde mein Projekt erst starten und einrichten, nachdem Ladeereignis für das Fensterobjekt ausgelöst wurde, wenn die gesamte Seite geladen wurde, einschließlich aller abhängigen Ressourcen wie Stylesheets, Skripten, Bilder usw. In der Callback-Funktion des Load-Event-Listeners können wir unser Canvas-Element einrichten Wie üblich verwende ich vier einfache Codezeilen. Zuerst richte ich das Java-Skript anhand seiner ID auf mein Canvas-Element. Ich speichere diese Referenz in einer Variablen, CTX-Context-Canvas-Variable dot get context und übergebe sie an die Dadurch wird eine in die Canvas-API integrierte Instanz erstellt , sodass wir auf alle Zeichenmethoden und Leinwandeigenschaften wie Füllstil oder Linienbreite zugreifen Leinwandeigenschaften wie Füllstil oder Linienbreite Weil wir sicherstellen wollen, dass Leinwandzeichnungen nicht verzerrt sind Wir legen die Größe der Leinwand nicht mit CSS fest, da dies nur die Elementgröße festlegen würde, wenn ich die Breite und Höhe der Leinwand so festlege Mit Javascript setzen wir sowohl die Elementgröße als auch die Größe der Zeichenfläche auf dieselben Werte ein. HTML-Leinwand hat zwei Größen Sie müssen auf denselben Wert eingestellt werden, um Verzerrungen zu vermeiden 4. Spiel- und Spielerobjekte: Das Spielobjekt muss sich der Leinwandgröße bewusst sein. Der Konstruktor erwartet also als Argument eine Referenz , die auf das Canvas-Element zeigt Im Inneren konvertieren wir es in eine Klasseneigenschaft. Nehmen Sie die als Argument übergebene Leinwand und konvertieren Sie sie in diese Punkt-Canvas-Eigenschaft diese Instanz der Spielklasse. Von dort aus können wir eine Leinwandbreite von 600 Pixeln und eine Leinwandhöhe von 800 Pixeln extrahieren . Das ist sehr wichtig. Und diese Werte werden in unserer gesamten Codebasis benötigt. Denn zum Beispiel müssen Spieler und Projektil wissen, ob sie sich innerhalb oder außerhalb des sichtbaren Leinwandbereichs befinden außerhalb des sichtbaren Leinwandbereichs Das Spielobjekt wird eine benutzerdefinierte Methode haben. Ich nenne zum Beispiel Render. Diese Methode wird 60 Mal pro Sekunde ausgeführt und zeichnet und aktualisiert alles. Lassen Sie uns zunächst einfach ein Consolo hineinlegen. Ich erstelle eine Instanz von Game Class mit dem neuen Schlüsselwort in Zeile 14. Ich kann mir vorstellen, dass es Canvas als Argument erwartet. Ich übergebe ihm Canvas aus Zeile 25. Wenn ich diese neue Spielvariable verwende, kann ich eine Instanz meiner Spielklasse sehen Wenn ich sie öffne, sehe ich, dass diese Eigenschaften perfekt sind. Ich kann auch die benutzerdefinierte Render-Methode nennen, die wir in Zeile 19 geschrieben haben. Und jetzt kann ich sehen, dass es mit einer Breite von 600 Pixeln und einer Höhe von 800 Pixeln korrekt 600 Pixeln und einer Höhe von 800 Pixeln Unser Spiel ist fertig eingerichtet und funktioniert. Ich möchte zeichnen und animieren Der Konstruktor der Spielerklasse erwartet einen Verweis auf das Hauptspielobjekt als Argument einen Verweis auf das Hauptspielobjekt aus Zeile 13. Da wir Zugriff auf Breite und Höhe und viele andere Dinge benötigen , die in der Hauptspielklasse enthalten sein werden , benötige ich Zugriff auf all diese Eigenschaften von der Spielerklasse aus Denkt daran, dass ich auf diese Weise keine Kopie des Hauptspielobjekts erstelle. Ich zeige nur auf den Bereich im Speicher, in dem sich das Spielobjekt innerhalb der Spielerklasse befindet. Ich speichere diese Referenz, da die Breite der Punktspieleigenschaft des Spielers 100 Pixel betragen wird. Die Höhe wird ebenfalls 100 Pixel betragen. Derzeit beträgt die horizontale X-Koordinate 200 und die vertikale Y-Koordinate ebenfalls 200 Pixel. Geben wir ihm eine benutzerdefinierte Zeichenmethode. Es wird ein Verweis auf den Kontext erwartet um anzugeben, auf welcher Leinwand wir zeichnen möchten. Ich mache das so, um jede Klasse so eigenständig wie möglich zu halten . Wir werden den Kontext daraus übernehmen. Wir nennen das eingebaute Füllrechteck und geben ihm X, Y, Breite und Höhe. Die aktuelle Position und Größe des Spielers. Ich werde meinen Code so strukturieren , dass sich alles auf dem Hauptspielobjekt befindet , sodass ich alles von dort aus zeichnen und aktualisieren kann . Im Spielklassenkonstruktor erstelle ich eine benutzerdefinierte Dist-Player-Eigenschaft und setze sie einer Instanz der Spielerklasse gleich Mit dem neuen Schlüsselwort erwartet der Spieler einen Verweis auf das Hauptspielobjekt Ich habe dieses Schlüsselwort übergeben, weil wir uns hier in dieser Spielklasse befinden. Nachdem wir den Player erstellt haben, können wir ihn zeichnen, indem wir die Methode draw aus Zeile neun aufrufen. Dieser Spieler aus Linie 27 zieht. Ich kann sehen, dass die Draw-Methode Kontext erwartet und ich werde diese Phil-Rectangle-Methode aus dem Kontext aufrufen. Ich weiß auch, dass die Phil-Rectangle-Methode auf meiner CT X-Variablen hier unten sitzt. Ich übergebe CTX zum Rendern, render erwartet diesen Wert als Argument und speichert ihn als Variable namens Kontext Ich werde es an Player Draw weitergeben , als ob diese Methode bereits Kontext erwartet Und so kann dieses Füllrechteck genannt werden. Hier in Zeile zehn, nett, wir zeichnen ein schwarzes Quadrat, das den Spieler darstellt. Ich kann die X- und Y-Koordinaten ändern , um den Spieler zu bewegen. Was ist, wenn ich möchte, dass sich der Spieler horizontal in der Mitte befindet? Die Punktbreite dieses Spiels multipliziert mit 0,5 Dann müssen wir diesen Wert um die Hälfte der Breite des Spielers ausgleichen. Jetzt ist es perfekt vertikal zentriert. Ich möchte, dass der Spieler unten im Spielbereich sitzt. Die Höhe des Spiels minus die Größe des Spielers. Perfekt. Wir haben ein funktionierendes Spielobjekt und ein Spielerobjekt. Ich möchte den Spieler dazu bringen, sich nach links und rechts zu bewegen. Wenn wir die Pfeiltasten auf der Tastatur drücken, gebe ich dem Spieler diese Eigenschaft für die Punktgeschwindigkeit. Ich möchte, dass es sich beispielsweise um fünf Pixel pro Animationsframe bewegt . Ich gebe ihm eine Aktualisierungsmethode, die die horizontale Position des Players anhand seiner Geschwindigkeit aktualisiert . Um diesen Code auszuführen, muss ich ihn von hier unten aufrufen. Nichts passiert wirklich, weil dieser Code nur einmal ausgeführt wird. Um eine Bewegung zu erzeugen, muss ich den Spieler zeichnen, seine Position aktualisieren, ihn erneut zeichnen, erneut aktualisieren und so weiter. Lassen Sie uns eine Animationsschleife erstellen. Benutzerdefinierte Funktion. Ich nenne Animate. Ich habe Game Dot Render hineingelegt. Dann rufen wir Request Animation Frame Sits auf dem Hauptfensterobjekt auf und ich übergebe Animate den Namen seiner übergeordneten Funktion, um eine Schleife zu erstellen Wenn ich den Fensterpunkt hier lösche, funktioniert es trotzdem. Javascript weiß, wo diese Methode zu finden ist. Wir nennen Animate. Wir zeichnen und aktualisieren das Spiel und lösen Animate Das wird sich immer und immer wieder wiederholen. Ich muss nur Animate aufrufen, um die erste Schleife wie diese auszulösen Wir sehen alte Farbe. Ich möchte nur den aktuellen Animationsframe sehen. Zwischen jeder Schleife lösche ich die gesamte Leinwand von der Koordinate 002. Leinwand mit Leinwandhöhe. Schön, der Spieler bewegt sich nach rechts, fünf Pixel pro Animationsframe. 5. Tastatursteuerungen: Wenn ich die Geschwindigkeit auf minus eins setze, bewegt sich der Spieler pro Animationsframe um ein Pixel nach links . Wenn die Geschwindigkeit Null ist, gibt es keine Bewegung. Fügen wir Tastatursteuerungen hinzu. Der gesamte Code in einem Klassenkonstruktor wird automatisch ausgeführt, wenn wir mit dem neuen Schlüsselwort eine Instanz der Klasse erstellen neuen Schlüsselwort eine Instanz der Klasse erstellen Normalerweise verwenden wir das Leerzeichen nur, um unserem Objekt Eigenschaften zuzuweisen, aber wir können tatsächlich jeden Code hineinlegen, den wir zu diesem Zeitpunkt ausführen möchten , wenn dieses bestimmte Objekt erstellt wird Wenn ich das zum Beispiel mache, werden Event-Listener automatisch angewendet Wenn eine neue Instanz eines Spielobjekts erstellt wird, erstelle ich einen Event-Listener für das Key-Down-Event Callback-Funktion hat Zugriff auf automatisch generiertes Ereignisobjekt, das alle verschiedenen Eigenschaften und Informationen über das gerade ausgelöste Key-Down-Ereignis enthält alle verschiedenen Eigenschaften und Informationen über das gerade ausgelöste Key-Down-Ereignis Um es zu verwenden, müssen wir ihm hier nur einen Variablennamen geben und Javascript wird wissen, was zu tun ist Ich nenne es für Event Inside. Ich werde trösten. Ich speichere meine Änderungen, wähle das Canvas-Element und drücke etwas auf meiner Tastatur Keydown-Ereignis wird ausgelöst und automatisch generierte Ereignisobjekt wird in der Browserkonsole angezeigt Wenn ich es mir anschaue, kann ich alle Arten von Informationen sehen. Heute interessieren wir uns nur noch für diese wichtige Eigenschaft. Genau hier können Sie sehen, dass ich den Buchstaben auf meiner Tastatur gedrückt habe, in diesem Fall, wenn ich die Taste Consolo Wenn ich jetzt zufällige Tasten drücke, wird der Name der Taste angezeigt, die gedrückt wurde Notieren Sie sich diese Werte. Beachten Sie, wie die Pfeiltasten geschrieben werden, da wir diese Werte benötigen werden Jetzt müssen wir sicherstellen, dass wir exakt dieselbe Schreibweise verwenden, einschließlich Groß - und Kleinschreibung Auf meinem Hauptspielobjekt werde ich ein Array haben, das als Punkttasten bezeichnet wird. Wenn beim Tasten-Down-Ereignis eine Taste gedrückt wird, füge ich den Namen der Taste, die gerade gedrückt wurde, in dieses Array ein. Wenn die Taste losgelassen wird, entfernen wir sie. Ich nehme diese Punkttasten hier und rufe die Push-Methode auf. Ich drücke die Punkttaste auf den Namen der Taste, die in das Array gedrückt wurde. Ich speichere es und teste es. Wenn ich anfange, auf meiner Tastatur zu tippen, erhalte ich einen Konsolenfehler. Das liegt daran, dass wir einen Event-Listener platzieren, der sich auf einem Fensterobjekt in unserem benutzerdefinierten Spielobjekt befindet der sich auf einem Fensterobjekt in unserem benutzerdefinierten Spielobjekt Hier versuche ich, auf diese Eigenschaft mit den Punkttasten zuzugreifen , die sich auf dem Spielobjekt befindet Aber diese Callback-Funktion erinnert sich nicht wirklich , wo im Code sie ursprünglich deklariert wurde, sie erinnert sich nicht an ihren lexikalischen Geltungsbereich Wir müssen das Schlüsselwort so binden, dass es innerhalb dieser Callback-Funktion auf das Spielobjekt zeigt auf das Spielobjekt innerhalb dieser Callback-Funktion Alternativ können wir die Syntax der ES-Sechspfeil-Funktion verwenden Syntax der ES-Sechspfeil-Funktion Pfeilfunktionen erben dieses Schlüsselwort automatisch aus dem übergeordneten Bereich Jetzt zeigt dieser hier verwendete Punkt korrekt auf das Spielobjekt und mein Code wird funktionieren Bei den beigefügten Unterlagen handelt es sich um fortgeschrittene Programmierthemen. Wenn Sie ein Anfänger sind, müssen Sie es zu diesem Zeitpunkt noch nicht vollständig verstehen Irgendwann wird es sinnvoller sein. Schreiben Sie einfach weiter Code. Ich protokolliere diesen Esel und wir können sehen, dass er immer größer wird, je mehr Tasten ich drücke Außerdem lade ich neu und stelle fest, dass es immer wieder hinzugefügt wird, wenn ich den Pfeil nach oben drücke es immer wieder hinzugefügt wird, wenn ich den Ich möchte nur einen Eintrag pro Taste hinzufügen. Wenn der Pfeil nach oben bereits im Array ist, füge ihn nicht erneut hinzu. In Java Script kann die Methode index off für ein Array aufgerufen werden. Sie gibt den ersten Index zurück , an dem ein bestimmtes Element im Array gefunden werden kann. ist wichtig, sich daran zu erinnern, dass es minus eins zurückgibt , wenn das Element nicht vorhanden ist. das wissen, können wir sagen, ob dieser Tastenindex von dokey gleich minus eins ist, wenn die Taste, die gedrückt wurde, nicht in dieser Punkttastenanordnung vorhanden ist nicht in dieser Punkttastenanordnung vorhanden Erst dann drücken Sie die Taste in das Array. Jetzt kann ich den Pfeil nach unten immer wieder drücken, und er wird erst dann zum Array hinzugefügt, wenn er perfekt ist. Ich möchte auch den Schlüssel aus dem Array entfernen , wenn der Schlüssel losgelassen wird. Wenn das Key-Up-Ereignis ausgelöst wird, kopiere ich diesen Codeblock. Um es übersichtlicher zu machen, erstelle ich eine konstante Variable mit Blockbereich , hier Index genannt. Und es wird der Index des Schlüssels sein, der innerhalb dieses Punktschlüssel-Arrays veröffentlicht wurde , wenn der Index mehr als minus eins ist heißt, wenn sich der Schlüssel, der freigegeben wurde, in diesem Punktschlüssel-Array befindet, was immer der Fall sein sollte. Übrigens werden wir die Splice-Methode verwenden, um diesen Eintrag, diesen Schlüssel, aus diesem Punktschlüssel-Array zu entfernen Schlüssel, aus diesem Punktschlüssel-Array integrierte Java-Skript-Spleißmethode kann verwendet werden, um vorhandene Elemente aus einem Array zu ersetzen oder zu entfernen Wenn der Schlüssel in dem Array gefunden wird, das ich Splice nenne, finde ich den Index dieses Elements im Array und entferne ihn Splice kann verwendet werden, um mehrere Elemente zu entfernen. Wir müssen es hier übergeben, weil wir nur das eine Element entfernen wollen , das wir dort gefunden haben Okay, also drücke ich die Enter-Taste auf meiner Tastatur. In dieser Punkttastenanordnung ist kein Eintrag vorhanden. Wir drücken es da rein, ich lasse Enter los. Wir finden den Index dieses Eintrags in diesem Punktschlüssel-Array und entfernen ihn mit der Splice-Methode Ich drücke die Pfeiltaste nach oben, es wird hinzugefügt, ich lasse es los Es wurde entfernt. Ich tippe ein bisschen mehr. Es funktioniert gut. Großartig. Es ist eine gute Praxis, Consolo immer zu entfernen, wenn wir sie nicht mehr benötigen Jetzt haben wir beim Hauptspielobjekt diese Punkttastenanordnung, die die Tasten aufzeichnet, die gerade gedrückt werden Dank dieser Punktspieleigenschaft hier in der Aktualisierungsmethode der Spielerklasse haben Sie vom Spielerobjekt aus Zugriff auf alles in der Aktualisierungsmethode der Spielerklasse haben Sie vom Spielerobjekt dieser Punktspieleigenschaft hier , was sich auf dem Spielobjekt befindet. Ich kann sagen, wenn der Index des linken Pfeils im Spiel größer als minus eins ist, was bedeutet, dass der Pfeil nach links gedrückt wurde, wird horizontale Position des Spielers immer wieder erzeugt , indem die Geschwindigkeit von Linie 8 aus verzerrt wird, sodass er sich nach links bewegt Eine weitere If-Anweisung mit dem Pfeil nach rechts und wir sagen Plus, hat Geschwindigkeit bewirkt, dass sie sich nach rechts bewegt Jetzt muss ich die Verzerrungsgeschwindigkeit auf etwas anderes als Null setzen auf etwas anderes als Null Hier zum Beispiel fünf Pixel pro Frame. Jetzt kann ich die Pfeiltasten links und rechts drücken und der Spieler bewegt sich nach links und rechts. Der Vorteil dieser Codestruktur ist, dass wir die Geschwindigkeit der Spieler leicht erhöhen können. Oder wir können den Spieler verlangsamen, indem diese Geschwindigkeitseigenschaft in Zeile acht ändern. Das Problem ist, dass sich der Spieler ganz außerhalb des Spielbereichs bewegen kann . Ich werde die Geschwindigkeit auf zehn Pixel pro Frame einstellen. Im Moment kümmern wir uns hier um horizontale Bewegungen. Ich werde hier einige horizontale Grenzen einführen. Wenn das x die horizontale Position des Spielers ist, die obere linke Ecke des die obere linke Ecke des Spielerrechtecks kleiner als Null ist, x entsteht eine harte linke Grenze L. Wenn der Spieler den Spielbereich im selben Animationsframe nicht links und rechts berühren kann , können wir L verwenden, wenn hier x größer ist als die Breite des Spiels minus die Breite des Spielers, x entspricht der Breite des Spiels minus der Breite des Spielers. Dadurch entsteht auf der rechten Seite eine harte Grenze. Ich teste es, der Spieler kann sich auf beiden Seiten nicht mehr außerhalb des Spielbereichs bewegen . Nett. 6. Objektpool: Jetzt, wo wir die Tastatursteuerung haben, möchte ich, dass der Spieler Laser abfeuert , wenn wir etwas drücken. Die Projektilklasse wird sich darum kümmern. In einem Spiel wie diesem werden wir eine große Anzahl von Projektilen abfeuern. Wir könnten jedes Mal ein neues Projektilobjekt erstellen und es verwerfen wenn es mit etwas kollidiert oder wenn es Aber wie ich in der vorherigen Lektion erklärt habe, führt das zu Leistungseinbußen Erstellen und Verwerfen großer Mengen von Objekten In diesem Spiel werden wir insgesamt nur zehn Projektilobjekte haben nur zehn Projektilobjekte Und wir werden dieselben zehn Objekte immer wieder verwenden, um die Leistung massiv zu verbessern Der Leistungsvorteil wird sich vor allem in stärker animierten Teilen des Spiels bemerkbar Glauben Sie mir, dieses Spiel kann sehr geschäftig und animiert werden, wenn Wellen von Feinden kommen. Wir müssen sicherstellen, dass wir die Leistung unserer Spiele optimieren . Das werden wir erreichen, indem wir ein Objektpool-Entwurfsmuster verwenden. Jedes Projektil hat die Eigenschaften Breite, Höhe, X, Y und Geschwindigkeit Da wir daraus ein wiederverwendbares Objektpool-Mitglied machen wollen , müssen wir ihm auch diese Eigenschaft ohne Punkte zuweisen Wenn diese Punktfreiheit wahr ist, befindet sich das Projektil im Pool und kann verwendet werden Es spielt derzeit keine aktive Rolle im Spiel. Es ist nicht sichtbar. Wenn dieser Punkt frei falsch ist. Das heißt, wir haben es aus dem Pool geholt und wir benutzen es. Es ist nicht mehr kostenlos, es ist nicht mehr verfügbar. Das Objektpool-Entwurfsmuster ist ein kreatives Entwurfsmuster Es ermöglicht uns, Leistungsprobleme im Zusammenhang mit automatischer Speicherzuweisung und Speicherbereinigung zu vermeiden im Zusammenhang mit automatischer Speicherzuweisung und Speicherbereinigung zu , die ausgelöst werden, wenn wir große Mengen von Javascript-Objekten erstellen und löschen Stattdessen werden wir einen kleinen Pool mit zehn Projektilobjekten erstellen einen kleinen Pool mit zehn Projektilobjekten Und wir werden sie aus dem Pool holen, wenn wir sie brauchen. Wenn die Projektile mit Feinden kollidieren oder wenn sie vom Bildschirm fliegen, wir sie einfach zurück in den Pool Wir setzen ihre Eigenschaften zurück und stellen sie zur Verfügung, damit sie wieder herausgezogen werden können. Sie werden benötigt Dadurch wird die Verlangsamung, die durch die Speicherzuweisung verursacht wird, wenn wir Objekte einzeln erstellen, vollständig beseitigt Speicherzuweisung verursacht wird, wenn wir Objekte einzeln erstellen Außerdem wird der Garbage-Collection-Prozess, automatisch und regelmäßig Objekte, auf die in unserem Spiel nicht mehr verwiesen wird, aus dem Speicher löscht Objekte, auf die in unserem Spiel nicht mehr verwiesen wird, aus dem Speicher , überhaupt nicht ausgelöst, da wir diese Projektile nicht wegwerfen werden Das Entwurfsmuster für Objektpools hat noch mehr zu bieten, aber dieser Kurs Heute werde ich Ihnen zeigen, wie Sie eine sehr einfache, aber leistungsstarke Implementierung in einem echten Arbeitsprojekt durchführen können. Wenn du mehr darüber lesen möchtest, verlinke ich in der Beschreibung unten einige gute Artikel. Wir geben ihm eine Ziehmethode, wollen aber nur aktive Projektile zeichnen , wenn das Projektil gerade nicht frei ist Das Gleiche gilt für das Update. Jeder Code innerhalb der Aktualisierungsmethode wird nur für die aktiven Objektpoolmitglieder ausgeführt. Nur wenn free falsch ist, bedeutet Ausrufezeichen falsch wenn Projektile nicht aktiv sind, wenn sie nur im Pool herumliegen und darauf warten, benutzt zu werden, werden wir sie nicht zeichnen und wir werden sie nicht aktualisieren Wenn ein Projektilobjekt aus dem Objektpool gezogen wurde und es nicht frei ist Im Moment zeichnen wir ein Rechteck, das das Projektil darstellt. Dies ist die Aktualisierungsmethode für alle aktiven Projektile die Aktualisierungsmethode für alle aktiven Wir werden sie auf der vertikalen Y-Achse in negativer Richtung nach oben bewegen lassen der vertikalen Y-Achse in negativer Richtung nach oben Ich möchte, dass sie sich relativ schnell bewegen. Versuchen wir es mit 20 Pixeln pro Animationsframe und sehen wir, wie das aussieht. Jedes Mitglied des Objektpools benötigt zwei Hilfsmethoden. Eine Methode, die ausgeführt wird, wenn das Objekt aus dem Objektpool genommen wird. Ich nenne es Start. Eine weitere Methode, die ausgeführt wird, wenn das Objekt nicht mehr benötigt wird und es wieder in den Pool verfügbarer Objekte zurückkehrt. Wenn wir anfangen, das Objekt zu benutzen, dem wir freien Lauf lassen, wird das Objekt benutzt, es ist nicht mehr im Pool verfügbar. Reset-Methode bringt das Objekt zurück in den Pool, indem die Eigenschaft free auf true gesetzt wird. Das bedeutet, dass das Objekt aufgrund dieser beiden Prüfungen nicht mehr gezeichnet und aktualisiert wird, das Objekt aufgrund dieser beiden Prüfungen nicht mehr gezeichnet und aktualisiert wird nachdem die Reset-Methode ausgeführt wurde. Ich hoffe, es macht Sinn , einen Kommentar zu hinterlassen, wenn Sie ihn verstehen oder wenn Sie eine ausführlichere Erklärung benötigen. Sie können einfach einen Kommentar mit einem einfachen Daumen-hoch-Emoji Wenn du es gut verstanden hast, werde ich wissen, was du meinst, und alle anderen werden durch diesen Kommentar verwirrt sein Das ist die Projektilklasse. Es ist ein Mitglied des Objektpools und hat alles, was es braucht, um ein Objektpool-Entwurfsmuster in unserer Codebasis zu erstellen Objektpool-Entwurfsmuster in unserer Codebasis Jetzt brauchen wir nur noch eine Möglichkeit, diesen Objektpool mithilfe dieser Projektilklasse zu erstellen diesen Objektpool mithilfe dieser Projektilklasse Wir werden es im Hauptgehirn unserer Codebasis tun. Hier in der Spielklasse gebe ich ihm eine Eigenschaft namens Projektilpool und setze sie auf ein leeres Array Die Anzahl der Projektile wird zehn sein. Ich möchte hier nur eine sehr kleine Anzahl verwenden. In diesem Fall wird es auch gut für das Gameplay funktionieren. Ich werde dir zeigen, warum ich nur zehn Projektile haben will , wenn wir eine Animation haben Entwurfsmuster für den Objektpool benötigt eine Methode, die den Pool zunächst mit zehn inaktiven Projektilobjekten füllt den Pool zunächst mit zehn inaktiven Projektilobjekten Auf diese Weise weisen wir den Speicher für alte Objekte auf einmal vorab zu, was sich positiv Es ist schneller, dies auf diese Weise zu tun und alle Objekte im Pool auf einmal zu erstellen , als wenn wir Java-Skript dazu bringen, für jedes Projektil einzeln nach freiem Speicherplatz zu suchen , und zwar einzeln, wenn wir sie benötigen Dies ist eine sehr einfache Methode, um zu erklären, wie und warum das Objektpool-Entwurfsmuster Probleme bei der Speicherzuweisung löst , indem wiederverwendet, anstatt sie zu löschen. Außerdem vermeiden wir vollständig, dass ein Garbage-Collection-Prozess ausgelöst Ein weiterer Leistungsvorteil ist die benutzerdefinierte Methode, die ich Create Projectiles nenne. Sie wird zehnmal laufen. Jedes Mal, wenn es läuft, zieht es Projektile an und schiebt ein neues Projektilobjekt hinein, das uns geschaffen hat In der Klasse, die wir in Zeile 23 geschrieben haben, benötigen wir eine letzte Hilfsmethode Eine Methode, die es uns ermöglicht, ein freies Projektilobjekt aus dem Pool zu nehmen , wenn wir es brauchen Ich nenne die Methode zum Beispiel „Projektil hineinholen“. Wir werden eins nach dem anderen durch den Projektilpool fahren. Sobald wir ein Element finden bei dem drei Eigenschaften auf true gesetzt sind, geben wir dieses Element zurück und beenden die Suche Das Schlüsselwort return stoppt die Ausführung dieser vier Schleifen. Ich möchte Projektile automatisch erzeugen. Wenn hier eine Instanz der Spielklasse erstellt wird, wird Projektilpool erstellt und automatisch mit zehn inaktiven und einsatzbereiten Projektilobjekten gefüllt zehn inaktiven und einsatzbereiten Projektilobjekten Lass uns überprüfen, ob es bei Consolo funktioniert hat. In diesem Projektilpool kann ich sehen, dass es sich um ein Array mit zehn Ich öffne einfach eines und überprüfe , ob ich keine undefinierten oder undefinierten Werte sehe , die auf ein Problem innerhalb meiner Projektilklasse hinweisen würden . Hier ist alles gut Projektil hat eine Startmethode, die ausgeführt wird, wenn wir das Projektil aus dem Pool holen Und wir wollen damit anfangen, es im Spiel zu verwenden , weil das Projektil herausfliegen wird Startmethode erwartet X- und Y-Koordinaten des Spielers als Argument Sie setzt diesen Punkt und dieses Y des Projektils auf die aktuellen X - und Y-Koordinaten des Um diese große Logikschleife zu vervollständigen, geben wir dem Spieler schließlich eine benutzerdefinierte Methode namens Shoot Inside. Ich erstelle eine konstante Blockvariable namens Projektil Und das entspricht der Methode „ Projektil abrufen “, die wir gerade über die Hauptspielklasse hier in Zeile 92 geschrieben die Hauptspielklasse hier Sie durchsucht den Projektilpool, ob eines der zehn Projektile frei und verfügbar ist, sie gibt es uns, wenn alle zehn Projektile gerade im Spiel verwendet werden, es wird kein freies gefunden Wir würden hier also eine Fehlermeldung bekommen. Nur wenn wir das Projektil gefunden haben und es nicht undefiniert ist, rufen wir die Startmethode für dieses inaktive Projektil im Pool auf , um es zu aktivieren Wir wissen, dass die Startmethode X - und Y-Position des Ich übergebe Punkt x Punkt Y. Wir übergeben die aktuelle X- und Y-Position des egal wo sich der Spieler gerade befindet, wenn die Startmethode ausgeführt wird, Startmethode es erwartet. Sie setzt die X- und die Y-Koordinaten des Spielers auf die X- und Y-Koordinaten des Projektils Und es aktiviert es, indem es seine Eigenschaft free auf Falls setzt, was bedeutet, dass der Code innerhalb der Draw - und Update-Methoden diese Prüfung bestehen wird Es beginnt, das Projektil zu zeichnen und zu aktualisieren Wie lösen wir diese benutzerdefinierte Schussmethode aus, die wir gerade geschrieben haben? Ich könnte zum Beispiel hierher kommen und den Key-Down-Event-Listener aufrufen Ich sage, wenn die Taste, die gedrückt wurde, Enter ist, achten Sie auf die Schreibweise Klein- und Großbuchstaben sind hier sehr wichtig. Von hier aus können Sie die Tastenkombination überprüfen, um zu sehen, wie ein bestimmter Schlüsselwert geschrieben wird Vielleicht möchte ich mehrere verschiedene Angriffe. Basic Shot wird ausgelöst, wenn wir die Nummer eins auf der Tastatur drücken. In diesem Fall handelt es sich bei Zahlen Zahlen. In diesem Zusammenhang handelt es sich um einen Zeichenkettendatentyp. Wenn Nummer eins auf der Tastatur gedrückt wird, wird die Schussmethode des Spielers ausgeführt. Um es tatsächlich zu sehen, muss ich alle zehn Projektilobjekte innerhalb der Projektilspulenanordnung durchgehen alle zehn Projektilobjekte innerhalb der Projektilspulenanordnung und sie zeichnen und aktualisieren Ich mache es von hier unten. Diese Projektilspule für jedes Projektilobjekt im Array nennen wir Update und wir nennen ihre Draw-Methode Wir wissen, dass die Auslosung Kontext voraussetzt . Wir übergeben es einfach mit dem Kontextwert aus Zeile 84. Dieser Kontextwert wird hier erwartet. Wenn das Projektil aktiviert ist, wird seine Eigenschaft free auf False gesetzt Kontext wird verwendet, um die Phil-Rectangle-Methode aufzurufen , die das Projektil für uns zeichnet Ich speichere meine Änderungen und drücke eine Taste auf der Tastatur. Die Shoot-Methode wird ausgeführt. Wir holen uns ein freies Projektilobjekt aus dem Objektpool und lösen dessen Startmethode Projektile starten immer von aktuellen X - und Y-Position des Spielers aus, auch wenn sich der Spieler bewegt Weil wir diese aktuellen Spielerkoordinaten an die Startmethode weitergeben Spielerkoordinaten an die Startmethode Hier kann ich nur zehnmal schießen. Und dann haben wir alle Objekte im Objektpool aufgebraucht und ich kann nicht mehr schießen. Ich möchte , dass Projektile zurückgesetzt werden und wieder im Pool verfügbar sind , wenn sie vom Bildschirm fliegen Wenn die vertikale Y-Koordinate des Projektils kleiner als Null minus der Höhe des Projektils ist , wenn sich das gesamte Projektil hinter den oberen Bildschirmrand bewegt hat , rufen wir die Reset-Methode Reset-Methode wird die freie Eigenschaft des Projektils auf „true“ gesetzt , sodass es wieder in den Pool zurückkehrt und wieder verfügbar sodass Jetzt kann ich nonstop schießen. Wenn ich sehr schnell schieße , erhalte ich zehn Projektile in einem kurzen Wenn sie den Bildschirm verlassen, werden sie zurückgesetzt und können wieder herauskommen Perfekte Rechtecke auf Leinwand werden von der oberen linken Ecke aus gezeichnet Spieler X und Y sind diese obere linke Ecke. Eigentlich möchte ich, dass sie horizontal aus der Mitte des Players herauskommen. Wir geben X des Spielers plus die Hälfte der Spielerbreite weiter. Es ist immer noch nicht perfekt zentriert, was noch deutlicher wird wenn ich die Projektile breiter mache Vielleicht sieht mein Sekundärangriff ungefähr so aus. Ich möchte, dass er über dem Spieler zentriert ist, egal welche Form das Projektil Ich mache das von hier aus, innerhalb der Projektilklasse Denn hier haben wir Zugriff auf die Breite des Projektils selbst Wir können die horizontale Position, die vom Spieler kommt , auf diese Weise um die Hälfte der Projektilbreite ausgleichen. Egal wie schmal oder breit die aktuellen Projektile sind, sie kommen immer horizontal genau aus der Mitte des Spielers Lassen Sie uns vorerst acht Pixel machen. Tolle Arbeit. Wir haben den Spieler, sich bewegen und Projektile abschießen kann Die Techniken, die wir gerade gelernt haben, sind extrem nützlich und mächtig und man kann damit Hunderte von verschiedenen Spielen machen , wenn der Spieler, den wir haben, sehr breit ist Aus irgendeinem Grund, wenn ich den Spieler stoppe , wenn er die Kanten so berührt, werden wir Schwierigkeiten haben, Feinde abzuschießen, die sich näher an den Rändern des Spielfeldes befinden Mit unserem Basisangriff werden wir sie nicht erreichen können. Das kann zu allerlei Problemen bei der Gestaltung unseres Spiels führen Ich werde es hier im Bereich der horizontalen Grenzen beheben. Ich werde dem Spieler tatsächlich erlauben, sich hinter dem Bildschirmrand zu verstecken , bis die Hälfte versteckt ist. Erst dann werden wir eine Stoppgrenze einführen. Jetzt kann ich Feinde ganz links erreichen. Ich werde das auch für den rechten Rand des Spielbereichs tun. Egal wie schmal oder breit der Spieler ist, unser einfacher Laserangriff kann nun überall ankommen und Feinde können sich nirgends verstecken. Perfekt. 7. Feindliche Wellen: Apropos Feinde, das wird interessant sein. Lasst uns Feinde erschaffen, die in einer Rasterformation auf uns zukommen. Und jedes Mal, wenn wir eine Welle besiegen, kommt eine größere Welle von Feinden. Bevor ich das mache, geben wir unserem Spiel einige Hintergrundinformationen. Ich gebe Ihnen ein komplettes, ausgefeiltes und professionelles Set mit hochwertigen Kunstwerken für dieses Projekt. Sie können alles im Abschnitt Ressourcen unten herunterladen . Nehmen Sie das PNG-Hintergrundbild, das Sie dort finden , und legen Sie es in Ihren Projektordner Verwenden Sie es dann als Hintergrundeigenschaft. Wir können diesen Teil tatsächlich mit CSS machen. Css ist GPU-beschleunigt. Auf diese Weise ist es leistungsschneller, als dasselbe Bild auf Navas immer wieder neu zu zeichnen , während unser Spiel Jetzt sind meine Leinwandzeichnungen vor dem neuen Hintergrund hier unten zu dunkel vor dem neuen Hintergrund hier unten Wenn alles geladen und Navas eingerichtet ist, stelle ich den Füllstil für alles ein, was auf Navas gezeichnet wird, die zu breit Im Moment ist das in Ordnung. Ich werde einzelne Feinde mit unserer benutzerdefinierten Feindklasse erstellen . Sie benötigen Zugriff auf einige Methoden und Eigenschaften, die ich der Hauptspielklasse zugewiesen habe. werden wir auch einen Verweis auf Von hier aus werden wir auch einen Verweis auf die Spielklasse haben. So wie das. Es gibt ein paar Herausforderungen, die wir hier lösen müssen. Weil wir wollen, dass sich Feinde in einem Raster bewegen und wir wollen, dass sie sich in dieselbe Richtung bewegen. Ich möchte, dass die gesamte Welle von Feinden demselben Bewegungsmuster folgt. Wir müssen innehalten und darüber nachdenken, wie wir unseren Code organisieren können. Um das zu erreichen, müssen jeder Feind, also eigentlich alle Objekte in unserem Spiel, die Eigenschaften Breite, Höhe, X und Y haben , hauptsächlich zur Kollisionserkennung. benutzerdefinierte Zeichenmethode verwendet den Kontext als Argument und wir streichen ein Rechteck, das jeden Feind darstellt. Im Moment wollte ich auch Feinde zu einem wiederverwendbaren Objektpool machen . Aber ich möchte diese Klasse anfängerfreundlich halten und einen Objektpool mit der Grid-Logik kombinieren , die wir gleich schreiben werden, könnte es schwierig werden, ihnen zu folgen. Im Moment werden Feinde sehr einfache Basis-Objekte sein, hier gibt es keine Objekt-Pool-Funktionalität. Im Moment bewegt die Aktualisierungsmethode den Feind. Aber wenn ich möchte, dass sich alle Gegner in einer Rasterformation bewegen, also im gleichen Bewegungsmuster, können wir hier nicht die Geschwindigkeit individuell festlegen und jeden Feind einzeln aktualisieren. Positionswert der gesamten Welle von Feinden muss irgendwo an einer Stelle in unserem Code gespeichert werden . Und jeder Feind wird wissen, wo er sich im Verhältnis zu diesem Wert befindet. Wenn sich die Welle bewegt, bewegen sich alle Feinde mit ihr. Lass mich dir genau zeigen, was ich meine. Erstellen Sie eine Wrapper-Klasse namens Wave Constructor. Auch hier benötigen Sie Zugriff auf Eigenschaften und Methoden des Hauptspielobjekts Wir wissen, dass alle Objekte in unserem Spiel eine Breite, Höhe, X- und Y-Position haben müssen . Lassen Sie uns die Position vorerst auf 00 in der oberen linken Ecke des Spielbereichs setzen . Hier im Spielklassenkonstruktor entferne ich dieses Consolo In jeder Welle wird es eine Reihe von Feinden geben, die in einem Raster angeordnet sind. Die erste Welle wird ein Raster von neun Feinden sein in drei Spalten und drei Reihen angeordnet sind. Außerdem benötige ich die Größe des Feindes, also die Größe jedes einzelnen Feindes um global verfügbar zu sein, überall in meiner Codebasis. Weil wir uns dessen an mehreren Stellen im Code bewusst sein müssen . Im Moment wird jeder Feind ein Quadrat von 60 mal 60 Pixeln sein . Ich möchte, dass das gesamte Gegnerraster zwischen dem linken und dem rechten Rand des Spielbereichs springt Jedes Mal, wenn die Welle von Feinden den Rand berührt, springt sie einen Schritt nach unten, näher an den Spieler heran Die Breite der gesamten Welle von neun Feinden, die in drei Reihen und drei Spalten angeordnet sind, wird diesem Punktspiel entsprechen. Punktspalten aus der Reihe 95 mal die Größe des Feindes aus Zeile 97 Höhe werden in diesem Punktspiel das Rosé mal die Größe des Feindes ergeben. Bei dieser Rendermethode wird die gesamte Welle von Feinden gezeichnet und aktualisiert. Es wird wie gewohnt Kontext benötigen, dann werden wir diesen Kontext verwenden. Und vorerst werden wir ein Rechteck streichen , das den gesamten Wellenbereich darstellt. Haben Sie Geduld mit mir, es wird in einer Minute mehr Sinn machen. Jede Welle enthält eine bestimmte Anzahl von Feinden abhängig vom Wert der Spalten Didot Rose und Did Wir beginnen mit drei mal drei. Die erste Welle wird neun Feinde haben. Jedes Mal vernichten wir alle Feinde in einer Welle. Die nächste Welle wird automatisch ausgelöst. behalten Hauptspielklasse werden wir alle aktiven Wellen innerhalb des Didotwellen-Arrays Sobald wir die Spielklasse erstellen, wird automatisch die erste Welle aus drei mal 39 Gegnern erzeugt und in dieses Punktwellen-Array geschoben in dieses Punktwellen-Array Ich habe dieses Schlüsselwort übergeben, um das Hauptspielobjekt darzustellen , in dem wir uns gerade befinden. Es wird hier erwartet und in diese Eigenschaft der Punktspielklasse umgewandelt. Von hier aus haben wir Zugriff auf Spalten, Reihen und die Größe des Feindes. Jetzt kann ich ein Didot-Wellen-Array nehmen, wir haben eine Welle hineingeschoben Ich rufe bereits nach jedem Wave-Objekt innerhalb dieses Wellen-Arrays Derzeit ist nur eines drin. Wir werden es Surrender Method nennen. Wir übergeben es im Kontext. Wir können sehen, wie die Welle hier gezeichnet wird, D-Strichstil zu breit, sodass es einfacher ist, sie als Linie mit beispielsweise 35 Pixeln zu erkennen . Größe der Welle passt sich an alle Gegner an, die sie enthält. Wenn wir fünf Reihen und acht Reihen von Feinden haben, also insgesamt 40 Gegner, werden sie innerhalb dieses Bereichs angeordnet. Die gesamte Welle von Feinden wird sich als eine Einheit bewegen. Die Geschwindigkeit hängt vom Wellenobjekt ab. Für jeden Animationsframe erhöhen wir die horizontale Position um die Geschwindigkeit, erhöhen wir die horizontale Position um wenn x kleiner als Null ist oder wenn x größer ist als die Breite des Spiels abzüglich der Breite der Welle. Das bedeutet, dass jedes Mal, wenn das Wellenrechteck den linken oder rechten Rand des Spielbereichs berührt, seine horizontale Geschwindigkeit auf den entgegengesetzten Wert umgestellt wird. Dadurch springt die Welle endlos nach links und rechts. funktioniert. Machen wir es dreimal drei. vertikale Geschwindigkeit ist Null , wenn sie eine der Kanten berührt. Ich möchte, dass es, je nach Größe des Feindes, vertikal nach unten auf den Spieler zuspringt . Ich schneide diese Zeile aus und füge sie hier ein. Und ich erhöhe auch die vertikale Y-Position um Geschwindigkeit y. Wenn wir das H auf diese Weise berühren, bleibt Geschwindigkeit y hoch und die Welle bewegt sich weiter nach unten. Ich möchte nur, dass sie in einem Animationsframe nach der Größe des Feindes springt und dann wieder auf Geschwindigkeit y Null zurückkehrt. Ja, das ist das Bewegungsmuster, nach dem ich gesucht habe. Wir haben eine Welle, die sich über dem Spielfeld nach links und rechts und unten bewegt . Sie wird ein Netz von Feinden enthalten. Jeder Feind bewegt sich mit der Welle verwendet dabei die X- und Y-Position dieser Welle sowie die relative horizontale und vertikale Position des einzelnen Feindes innerhalb dieses Rasters. Innerhalb der Welle nenne ich diese Werte diese Position x und diese Punktposition Y. Position X und Position Y die Position des Feindes innerhalb der Welle Jeder Feind muss genau wissen, wo er sich innerhalb des Rasters befindet Breite des Feindes ergibt sich aus diesem Wert aus Zeile hundert und 11, ich habe ihn auf 60 Pixel gesetzt. Dieses Spiel punktet mit der Größe des Feindes. Auch für Breite und Höhe. Feinde werden quadratisch sein. X und Y werden 00 sein. Wenn wir einen Feind erschaffen, geben wir ihn zunächst als Argument an der relativen Position zwischen dem Feind und der gesamten Welle an. Werte für Position X und Position Y bestimmen, wo sich die Welle im Verhältnis zur oberen linken Ecke des Wellenrechtecks befindet. Dieser spezielle Feind befindet sich hinter Position x und Position Y, da Argumente in Klasseneigenschaften umgewandelt werden . Hier verwendet die Aktualisierungsmethode X- und Y-Position der Welle als Argumente. Und es wird die X - und Y-Position des Feindes relativ zu diesem aktuellen Wert festlegen . Exposition des Feindes ist die Position der Welle plus die relative horizontale Position des Feindes in dieser Welle Y-Position des Feindes ist die vertikale Position, Y-Position der Welle plus relative vertikale Position dieses bestimmten Feindes in der Welle Es könnte eine bessere Möglichkeit geben, diesen Code zu strukturieren. Ich habe mir das gerade ausgedacht. Lass uns sehen, wie es für uns funktioniert. Jede Welle wird ihre eigene Feindgruppe haben. Wir geben ihr eine benutzerdefinierte Erstellungsmethode im Inneren, wir werden eine Viererschleife haben. Wir wollen etwas in einem Raster organisieren, was bedeutet, dass wir zwei verschachtelte Schleifen mit vier Schleifen innerhalb einer anderen Schleife benötigen zwei verschachtelte Schleifen mit vier Schleifen innerhalb einer anderen Schleife Schreiben wir es zuerst und dann erklären wir, wie es funktioniert. Die äußeren vier Schleifen behandeln Zeilen, während wir Zeile für Zeile nach unten gehen. Die vertikale Y-Koordinate wird zunehmen. Die innere For-Schleife behandelt Spalten, wenn wir uns von links nach rechts bewegen. In jeder Zeile nimmt die horizontale X-Koordinate zu. Nehmen wir an, wir sind hier. Das ist das aktuelle gegnerische Quadrat 60 mal 60 Pixel. Die Zeile ist Null. In der äußeren Schleife gelangen wir in die innere Schleife und durchqueren alle horizontalen Positionen innerhalb dieser Reihe, bis wir die letzte Spalte erreichen. Wir verlassen die innere Schleife, betreten die äußere Schleife, erhöhen die vertikale Y-Position und springen zur nächsten Reihe. Auch hier treten wir in die interne Schleife Wir durchqueren die horizontalen Positionen durch die Spalten von links nach rechts. Wir verlassen die innere Schleife, betreten die äußere Schleife, erhöhen Y und springen zur nächsten Reihe. Wir treten durch alle horizontalen Positionen in den Zyklus zwischen den Schleifen ein. Dort wiederholen wir das immer wieder, bis wir das gesamte Raster der gesamten Welle erstellt haben . Ich muss die Größe des Feindes berücksichtigen , wenn Eigenschaften von Feind X und Feind Y erstelle. Ich hätte alternativ auch hier und hier nach dem Wert für die Größe des Feindes springen können hier und hier nach dem Wert für die Größe des Feindes springen Das Ergebnis wird dasselbe sein. Jedes Mal springen wir zu einer neuen Zelle im Raster. Wir nehmen diese feindliche Welle und schieben ein neues feindliches Objekt hinein. Wir können sehen, dass der Konstruktor der gegnerischen Klasse das Spiel und die relative X- und Y-Position dieses Feindes im Raster erwartet und die relative X- und Y-Position dieses Feindes im Raster Ich übergebe es dieses Spiel ab Zeile 78. Ich übergebe es Feind X und Feind Y. Okay, wenn wir eine neue Welle erstellen, wir einen Blick in Spielklassenkonstruktor, um den aktuellen Wert von Zeilen und Spalten zu sehen den aktuellen Wert von Zeilen und Spalten Auf dieser Grundlage erstellen wir ein Raster von Feinden. Ich möchte, dass die Methode create automatisch ausgelöst wird, wenn wir eine neue Welle erstellen. Ich nenne es hier, das heißt , sobald wir eine Instanz der Wave-Klasse erstellen, wird das Array dieses Feindes automatisch mit feindlichen Objekten gefüllt. Und ihre X- und Y-Koordinaten werden innerhalb des Rendervorgangs in einem Raster angeordnet. Ich kann jetzt dieses Array nehmen und für jedes feindliche Objekt darin rufe ich seine Aktualisierungsmethode auf und es ist eine Draw-Methode. Ich übergebe den Kontext der Draw-Methode wie gewohnt. Hier können wir sehen, dass die Aktualisierungsmethode X- und Y-Position der Welle erwartet , sodass ich sie verwenden kann, um Feinde in einer gitterartigen Formation zu positionieren. Immer wenn ich Feind-Update aufrufe, muss ich ihr die X - und Y-Position ihres Wrapper-Wellenobjekts übergeben , um sicherzustellen, dass sich alle Gegner auf dem Bildschirm bewegen Damit ziehen wir dreimal drei Gegner. Entferne dieses Rechteck, das die gesamte Welle durchzieht. Das sollte uns nur helfen, uns vorzustellen, was vor sich ging. Wir sind immer noch dabei, die Feinde anzulocken. Wir haben eine Welle von drei mal drei Feinden, die sich synchron auf dem Bildschirm bewegen . Perfekt. Ich will, dass die Feinde Welle für Welle kommen. Und ich brauche jede Welle, um aus dem Bildschirm zu starten und in den Spielbereich zu schweben. Ich habe gesagt, dass die anfängliche Y-Koordinate der Welle Null minus der Höhe der Welle sein sollte, was bedeutet, dass sie zunächst hinter dem oberen Rand des Spielbereichs versteckt sein wird . Die Welle wird außerhalb des Bildschirms erzeugt und ich möchte, dass sie schnell nach unten schwebt, bis sie vollständig sichtbar ist. Dann beginnt sie mit ihrem Links-Rechts-Bewegungsmuster. Wenn dieses Y kleiner als Null ist, erhöhen Sie Y um fünf Pixel pro Animationsframe. Es schwebt auf diese Weise hinein und fängt an nach links und rechts zu springen, wenn alle Reihen und Spalten von Feinden, die gesamte Welle im Spielbereich vollständig sichtbar ist Ich kann es testen, indem ich die Anzahl der Zeilen und Spalten ändere. Sie können sehen, dass es immer noch funktioniert. Es schwebt immer noch nach unten, bis alle Gegner in dieser Welle sichtbar sind und von Spielerlasern ins Visier genommen werden können Ich versuche es mit fünf Spalten und sieben Reihen, was mir eine Welle von 35 Feinden 8. Feindliche Wellen: Das läuft wirklich gut. Wir haben den Spieler, Projektile und Feinde. Ich möchte, dass die Projektile und Feinde interagieren. Wenn ein Feind von einem Projektil getroffen wird, Feind zerstört und das Projektilobjekt wird wieder in den Objektpool zurückgebracht Jedes Objekt in unserem Spiel hat die Eigenschaften X, Y, Breite und Höhe Lassen Sie uns eine wiederverwendbare Methode zur Kollisionserkennung erstellen, die zwei beliebige rechteckige Objekte vergleicht , die wir ihr als Argumente übergeben. Und sie gibt wahr oder falsch zurück je nachdem, ob diese beiden Rechtecke kollidieren oder nicht Für die Erkennung von Doppelkollisionen müssen Rechtecke achsengleich angeordnet sein Wenn sie in irgendeiner Weise gedreht würden, müssten wir sie wie Polygone behandeln Kollisionserkennung müsste es sich um eine komplexere Technik handeln, beispielsweise um eine Formel für den Trennachsensatz Heute werden alle Rechtecke achsenorientiert und nicht gedreht sein achsenorientiert und nicht gedreht Wir können also die einfachste und unkomplizierteste Formel zur Kollisionserkennung zwischen zwei achsigen, also nicht gedrehten Rechtecken verwenden unkomplizierteste Formel zur Kollisionserkennung , also nicht gedrehten Rechtecken wiederverwendbar sind und die benutzerdefinierte Methode zur Kollisionsprüfung Wir nehmen zwei Argumente, Rechteck A und Rechteck B. Es gibt viele Online-Artikel zu dieser Technik Ich kann sie referenzieren. Ich werde einige Links im Abschnitt Ressourcen unten hinterlassen. Die Formel prüft immer, ob das Rechteck A horizontal und auch vertikal innerhalb der Grenzen von Rechteck B liegt der Grenzen von Rechteck B . Dazu müssen wir vier Prüfungen durchführen, denen alle vier Kanten des Rechtecks verglichen werden. Wenn alle vier dieser Prüfungen wahr sind, sonst eine Kollision vorliegt, selbst wenn eine von ihnen falsch ist, liegt keine Kollision vor. Lassen Sie mich Ihnen zeigen, wie das funktioniert. Diese Formel ist übrigens sehr billig, wenn es um die Leistung geht, weil die Berechnungen so einfach sind. Wenn unser Spieldesign es zulässt, sollten wir versuchen, es zu verwenden. Manche Spiele erfordern natürlich komplexere Techniken zur Kollisionserkennung. Es ist nicht immer für unser Projekt geeignet. Heute ist es perfekt. Wir müssen vier Dinge überprüfen. Zuerst prüfen wir, ob die horizontale Position des Rechtecks, das wir hier als Argument a übergeben , kleiner ist als die horizontale Position des Rechtecks B plus die Breite des Rechtecks. Wir prüfen, ob sich das links davon befindet. Gleichzeitig doppelter Prozent-Betreiber. Wir müssen auch prüfen, ob die horizontale Position des Rechtecks a plus die Breite des Rechtecks a ist als die horizontale Position des Rechtecks. Wir prüfen, ob sich das rechts davon befindet. Wenn diese beiden Prüfungen zutreffen, wissen wir, dass Rechtecke horizontal kollidieren, aber sie können vertikal immer noch weit voneinander entfernt sein voneinander entfernt Wir müssen dieselben beiden Prüfungen durchführen, diesmal jedoch für die vertikale Y-Achse Ich überprüfe, ob die vertikale Position des Rechtecks A kleiner ist als die vertikale Position des Rechtecks B zuzüglich seiner Höhe. Liegt dieser Wert über dieser letzten Prüfung, prüfen wir, ob die vertikale Position von Rechteck A zuzüglich seiner Höhe größer ist als die vertikale Position von Rechteck B. Falls diese Position darunter liegt. Nur wenn alle vier dieser Prüfungen zutreffen, liegt eine Kollision vor. Wenn alle falsch sind oder mindestens eine von ihnen falsch ist, wissen wir, dass es keine Kollision gibt. Wir wissen, dass die beiden Rechtecke weit voneinander entfernt sind zumindest in einer Richtung Ich kann das sogar vereinfachen und ich kann dafür sorgen, dass diese Methode diesen Ausdruck automatisch auswertet und direkt wahr oder falsch zurückgibt Auf diese Weise kann ich einfach Check Collision aufrufen , wenn zwei rechteckige Objekte passiert sind, zum Beispiel Projektil und Feind und es wird direkt wahr oder falsch zurückgegeben Für mich ist das unsere wiederverwendbare Methode zur Kollisionserkennung. Denken Sie daran, dass alle Objekte, die wir als Rechteck A oder Rechteck übergeben , die Eigenschaften X, Y, W und Hyde haben müssen Eigenschaften X, Y, W und Hyde Damit dieser Code funktioniert, möchte ich eine benutzerdefinierte Check-Kollisionsmethode verwenden Jetzt haben wir alle zehn wiederverwendbaren Projektilobjekte. Hier im Projektilpool-Array werden Feinde separat im gegnerischen Array gespeichert Für jede Welle hier werden diese Gegner anhand der Klasse erstellt, der Bauplan, den wir hier in Zeile 57 geschrieben haben Es ist also sinnvoll, den Kollisionscheck hier einzufügen. Jedes Mal, wenn die Aktualisierungsmethode für jedes gegnerische Objekt ausgeführt wird, vergleichen wir X, Y innerhalb der Höhe dieses bestimmten Feindes mit allen zehn Projektilobjekten Wir werden nur mit dem Projektilpool der aktiven Projektile vergleichen Für jedes Projektil rufen wir unsere benutzerdefinierte Check-Kollisionsmethode Wir wissen, dass sie das rechteckige Objekt a und das rechteckige Objekt als Argumente erwartet a und das rechteckige Objekt als Argumente Wir wissen, dass beide Objekte X-, Y-, Breiten - und Höheneigenschaften haben müssen, - und Höheneigenschaften damit die Check-Collision-Methode mit ihnen arbeiten kann. Ich übergebe dieses feindliche Objekt als Rechteck A und übergebe es nacheinander an allen zehn Projektilen , während wir durch den Projektilpool fahren Für jede Methode haben wir Check Collision Method geschrieben , um direkt „true“ oder „false“ zurückzugeben Also kann ich einfach sagen ob und es so in Klammern setzen. Wenn dieser Feind und das Projektil kollidieren, läuft dieser Codeblock Wir löschen den Feind. Ich sagte, die Eigenschaft „ Löschen“ ist auf „Wahr“ markiert. Außerdem muss ich diese Eigenschaft im Konstruktor der feindlichen Klasse deklarieren . Hier oben in der Render-Methode für das Wellenobjekt werden wir Feinde, die zerstört wurden, aus dem Array des Feindes herausfiltern zerstört wurden, aus dem Array des Feindes herausfiltern eingebaute Array-Filtermethode erstellt eine Kopie eines bestimmten Arrays und diese gefilterte Kopie enthält nur die Elemente, die hier eine bestimmte Bedingung erfüllt haben. Ich sage im Grunde, dass Sie eine Kopie dieses Punktfeinde-Arrays erstellen und nur interne Elemente zulassen , Eigenschaft zum Löschen markiert auf falsch gesetzt Überschreiben Sie dann dieses ursprüngliche Dis-Punkt-Feinde-Array mit diesem neuen gefilterten Array. Auf diese Weise entferne ich alle Gegner, die mit Projektilen kollidiert sind , weil die Eigenschaft „ Zum Löschen markiert “ auf true gesetzt Ich erhalte einen Konsolenfehler. Das liegt daran, dass ich Projectile Pull hier falsch geschrieben habe. Ja, es soll Projektile ziehen mit einem S buchstabiert werden. Wenn eine neue Welle von Feinden kommt, kannst du sehen, dass der Feind unten links Das liegt daran, dass alle meine inaktiven Projektile zunächst in dieser Ecke an der Koordinate 00 sitzen Wir zeichnen sie nicht und aktualisieren sie nicht, aber wir berechnen immer noch die Kollision zwischen diesen inaktiven Projektilen und Ich behebe das Problem, indem ich nur die Kollision überprüfe , wenn projectile free falsch ist Wenn das Projektil gerade aktiv ist, wollen wir inaktive Projektile ignorieren wenn es um die Kollisionserkennung geht Ich möchte auch das Projektil zurücksetzen und es zurück Es kollidiert mit einem Feind, sodass jedes Projektil nur einen Feind zerstören kann Dafür haben wir hier eine Reset-Methode , die das freie Eigentum auf true setzt und das Projektil wieder in den Pool ungenutzter verfügbarer Objekte zurückbringt den Pool ungenutzter verfügbarer Objekte zurückbringt Wenn es zu einer Kollision zwischen Feind und Projektil kommt, rufe ich die Reset-Methode für dieses Projektil auf, um es zu deaktivieren Ja, das ist das Gameplay, nach dem ich gesucht habe. Wenn es zu einer Kollision zwischen Feind und Projektil kommt, wird der Feind gelöscht und das Projektilobjekt wird wieder in den Objektpool zurückgebracht 9. Kollisionserkennung: Ich möchte Statustexte zeichnen, zum Beispiel Punkte, Wellen, Zählerleben oder wie viel Energie wir derzeit für Laser und Spezialangriffe haben. Ich werde all das innerhalb der Methode Draw Status Text machen, ich nenne die eingebaute Fülltext-Methode , die mindestens drei Argumente benötigt: den Text, den wir zeichnen möchten, und die X- und Y-Koordinaten des Ankerpunkts, in Bezug auf den der Text positioniert werden soll. Ich nenne es hier in Render und ich möchte es hinter Feinden zeichnen. Also zeichne ich es zuerst. Es ist nur meine Wahl. Du kannst es draufzeichnen, wenn du willst. Ich habe den Kontext weitergegeben und los geht's. Ich habe die Schrift zum Beispiel auf einen Effekt von 30 Pixeln eingestellt, um schöne, große und leicht lesbare Buchstaben zu erhalten. Wir behalten den aktuellen Punktestand als Variable für das Hauptspielobjekt bei. Es wird bei Null beginnen. Innerhalb des Feldtextes werde ich diesen dynamischen Variablenwert neben dem Text wie folgt hinzufügen . Jetzt möchte ich, dass der Punktewert um eins steigt , wenn wir hier einen Feind vernichten. Wenn Projektil und Feind kollidieren, nehmen wir diese Spielpunktzahl und erhöhen sie um eins. So wie das Toll, ich schieße auf Feinde und erhöhe meine Punktzahl. Perfekt. Das Spiel geht verloren, wenn ein Feind den unteren Bildschirmrand berührt. Ich werde bei jedem Feind überprüfen, ob es sich um seine vertikale Y-Position plus seine Höhe handelt. Die untere Grenze des gegnerischen Rechtecks ist größer als diese Punkthöhe, die untere Grenze des sichtbaren Spielbereichs. Wenn das passiert, setze ich das Spiel auf „Wahr“ und vernichte den Gegner, indem ich seine Eigenschaft „Zum Löschen markiert“ auf „Wahr“ setze. Vorerst muss ich sicherstellen, dass ich das Spiel an dieser Stelle für Eigentum erkläre. Anfangs wird es falsch sein. Wenn Game Over wahr ist, möchte ich etwas Text zeichnen. Wie Sie sehen können, ist der Leinwandtext standardmäßig linksbündig ausgerichtet. Ich möchte, dass diese Game-Over-Nachricht mittig ausgerichtet ist. Die Schrift wird 100 Pixel groß sein. Im Impact-Text steht in Großbuchstaben das Spiel vorbei. Und es wird genau in der Mitte des Bildschirms sein. Breite des Spiels mal 0,5 und Höhe des Spiels mal 0,5 Schön, wir haben ein großes Spiel über den Text in der Mitte des Bildschirms. Sobald der Feind den unteren Rand des Spielbereichs berührt. Das Problem besteht darin, dass diese Textzeile und Schrifteigenschaften den gesamten Leinwandstatus ändern. Und jetzt gilt derselbe Textstil für Partiturfülltext in Zeile 194. Ich werde diesen gesamten Codeblock zwischen sicheren und wiederherstellenden integrierten Canvas-Methoden umschließen. Die sichere Methode speichert den Canvas-Status, einschließlich des Füllstils, der Textzeilenschrift und aller anderen Canvas-Eigenschaften. Es schafft einen sicheren Punkt, wie in einem Videospiel. Es merkt sich, welche Werte alle Canvas-Eigenschaften haben. An diesem Punkt können wir Canvas-Einstellungen nach Belieben ändern. Wenn ich dann restore aufrufe, werden alle Canvas-Eigenschaften auf den Stand zurückgesetzt, zu sie waren, als die sichere Methode aufgerufen wurde. Was passiert? Jetzt speichern wir den Canvas-Status, zeichnen Punkte mit einer Größe von 30 Pixeln und linksbündig, wir setzen den Text auf 200 Pixel mittig ausgerichtet, wir zeichnen das Spiel über und setzen diese Einstellungen zurück. Zurück in der nächsten Animationsschleife, wenn der Punktestand neu gezeichnet wird, ist er wieder auf 30 Pixel linksbündig ausgerichtet Es ist eine endlose Animationsschleife, die wir hier haben. Ich hoffe, es macht Sinn, wir können noch weiter gehen und da wir den gesamten Statustext zwischen „Sicher“ und „Wiederherstellen“ umschlossen haben, haben wir ihn von den übrigen auf der Leinwand gezeichneten Formen getrennt . Ich kann ihm hier auch ein paar Schatten geben. Schatten werden nur auf den Text angewendet, nicht aber auf Feinde, Projektile und Spielerformen Um Leinwandschatten anzuzeigen, müssen wir mindestens drei von vier verfügbaren Eigenschaften definieren von vier verfügbaren Eigenschaften Horizontaler Abstand zwischen der Form und dem Schatten. Vertikaler Abstand, Farbe des Schattens. Und wir können Unschärfe auch mithilfe der Eigenschaft Shadow Blur anwenden . Aber es ist optional. Unschärfe ist leistungsaufwändig. Dabei werden Opazität und Subpixelwerte berechnet. Ich werde es heute vermeiden. Wir zählen den Punktestand und ziehen Spiel um Botschaft, nett. Wenn ich eine ganze Welle von Feinden vernichte, möchte ich, dass automatisch eine neue größere Welle kommt. Wir werden sie zählen und die Spieler können sich selbst entscheiden, um zu sehen, wie viele Wellen von Feinden sie besiegen Die Wellenzählung beginnt mit Welle Nummer eins. Auf diese Weise werden wir die Wellenzahl hier ziehen, so wie hier. In unserer Methode zum vertikalen Zeichnen von Statustexten habe ich den Text auf 60 Pixel eingestellt. Nein, 80 Pixel von oben entfernt. Ja, die erste Welle wird ein kleines Gitter aus zwei mal zwei Feinden sein. Wenn ich diese vier Feinde vernichte, möchte ich, dass eine neue größere Welle erscheint. Wir werden diese Logik innerhalb einer Kundenmethode handhaben. Ich nenne zum Beispiel New Wave. Man kann sich diese Technik als eine sehr einfache Levelmechanik vorstellen. Wenn du eine Welle besiegst, erreicht eine größere, schwierigere Welle von Feinden ein neues Level. Wir können hier alle Bedingungen und Eigenschaften dieser neuen Welle hinzufügen . Zunächst kann ich die Größe der Welle um eine Spalte und um eine Zeile erhöhen . Lass uns das versuchen. Ich werde die Werte für Spalten und Zeilen um eins erhöhen und ein neues Wellenobjekt in dieses Wellenarray einfügen. Ich übergebe ihm einen Verweis auf das Hauptspielobjekt als Argument , weil es es erwartet, wie wir hier in Zeile 91 sehen können. Da wir den Wert von Spalten und Zeilen erhöhen, stellen diese Codezeilen sicher , dass die nächste Welle größer ist. Ich muss auch sicherstellen, dass jede Welle die nächste Welle nur einmal auslöst. Also gebe ich ihr eine Flagge , die anzeigt, ob sie bereits die nächste Welle ausgelöst hat oder nicht. Anfangs habe ich es falsch gesagt. Jede Welle enthält ein gegnerisches Array, das alle derzeit aktiven feindlichen Objekte in dieser Welle enthält. Während wir jede Methode auf allen Wellenobjekten in diesem Wellen-Array ausführen , überprüfe ich, ob das ***gte Feld des gegnerischen Arrays auf dieser Welle kleiner als eins ist Wenn gleichzeitig die Flagge, die ich aufgerufen habe, die nächste Welle ausgelöst wurde, noch nicht aktiviert wurde Und nur, wenn Game Over falsch ist. Denn wenn Game Over passiert, möchte ich, dass keine neuen Wellen mehr kommen. Wenn dieser gesamte Ausdruck als wahr ausgewertet wird, haben wir den if-Anweisungsblock darin eingegeben Ich nenne die New-Wave-Methode, die wir gerade geschrieben haben. Wir werden auch die Anzahl der Wellen um eins erhöhen. Schließlich habe ich die nächste Welle ausgelöst, um zu verhindern, dass diese bestimmte Welle weitere neue Wellen auslöst Ich teste es, wir haben eine Welle von zwei mal zwei Feinden. Wenn ich sie alle besiege, bekommen wir sofort die nächste Welle von dreimal drei Feinden. Perfekt ist es, denn hier bei der New-Wave-Methode fügen wir jedes Mal, wenn eine neue Welle von Feinden entsteht, immer eine weitere Spalte und eine weitere Zeile hinzu. Wenn ich möchte, dass die Wellen etwas langsamer wachsen, möchte ich in 50% der Fälle nach dem Zufallsprinzip eine neue Spalte hinzufügen, oder in den anderen 50% der Fälle möchte ich eine neue Zeile hinzufügen. Das können wir zum Beispiel machen. Auf diese Weise gibt uns die zufällige Mathematik, die für sich allein aufgerufen wird, einen Zufallswert von 0 bis 10 eingeschlossen, einer ausgeschlossen. Ich kann sagen, wenn die Mathematik dem Zufallsprinzip einzeln aufgerufen wird, weniger als 0,5 beträgt, wird das gegnerische Raster in etwa 50% der Fälle um eine Spalte erhöht, andernfalls wird stattdessen eine Zeile hinzugefügt Auf diese Weise werden die Wellen immer noch wachsen, aber etwas langsamer. Es wird länger dauern, bis sie einen großen Teil des Bildschirms ausfüllen und das Spiel wird später schwieriger. Ich werde auch Bosskämpfe hinzufügen, Funktionen nach dem Zufallsprinzip. Anstatt einer neuen Welle könnten wir uns einfach einen großen Boss mit viel Gesundheit holen. Wenn wir den Boss besiegen, werden die Wellen wie gewohnt kommen. Ich werde auch sicherstellen müssen, dass wir nur bis zu einem bestimmten Punkt neue Spalten hinzufügen. Nehmen wir an, bis die Breite der gesamten Welle weniger als 80% der Bildschirmbreite beträgt. Wenn wir mehr als das zulassen, kann das Spiel schnell zu schnell und völlig unspielbar werden schnell und völlig unspielbar Sie können versuchen, das Spiel in diesem Stadium zu spielen, in dem wir keine Beschränkungen für Spalten und Reihen haben . Siehst du, was ich meine. Ich werde auch nur Reihen hinzufügen, wenn die Höhe der gesamten feindlichen Welle weniger als 60% der Spielhöhe beträgt. Ich habe es getestet, indem ich die Startaufstellung auf fünf mal fünf Gegner gesetzt habe. Neue Welle. Und ich kann sehen, dass eine neue Spalte hinzugefügt wurde. Lassen Sie mich schnell mit ihnen umgehen. Ich muss später ein paar stärkere Waffen hinzufügen. Jetzt wurde eine neue Zeile hinzugefügt. Wir haben ein Raster von sechs mal sechs. Jetzt wurde eine weitere Zeile hinzugefügt. Wir haben ein Raster von sechs mal sieben. Wenn wir dieses Stadium erreichen, möchte ich, dass Feinde irgendwie reagieren , wenn sie den Spieler berühren. Der erste Schritt bei dieser Interaktion besteht darin, eine Kollision zwischen Feinden und dem Spieler zu erkennen. Wenn du die Option Kollision zwischen diesem Feind und dem Spieler wahr markiert hast, setze bei Feind die Option „ Löschen“ auf „Wahr“, um ihn zu zerstören. In dieser If-Aussage überprüfen wir erneut, ob das Spiel vorbei falsch ist. Wenn die Punktzahl größer als Null ist, reduzieren wir die Punktzahl um eins. Wir werden den Spieler dafür bestrafen, dass er den Feind nicht aus der Ferne erschießen kann Ich werde auch die Leben der Spieler um eins reduzieren. Dann überprüfe ich, ob die Zahl der Lebenspunkte des Spielers kleiner als eins ist. Wenn ja, setzen wir das Spiel auf „Wahr“. Ein netter Spieler beginnt mit, sagen wir, drei Leben. Um zu überprüfen, ob es funktioniert, werden wir die Leben in der Textmethode Status ziehen visualisieren . Aber lassen Sie mich Ihnen einen anderen Weg zeigen, wie Sie sie anzeigen können. Anstatt einfach eine Zahl mit der Aufschrift Drei zu ziehen, werde ich eine Vierer-Schleife erstellen, die einmal pro Leben eines Spielers läuft. Jedes Mal, wenn es ausgeführt wird, füllt es ein Rechteck an den Koordinaten 1010. Die Höhe des Rechtecks beträgt ebenfalls zehn mal zehn Pixel. Jetzt zeichne ich drei Quadrate übereinander. Ich möchte, dass das eine Ladeleiste ist. Ich multipliziere einfach die horizontale Position mit dem Tams-Index der vier Schleifen Jetzt werden die drei Quadrate, die das Leben der Spieler repräsentieren , nebeneinander gezogen Dieser Wert entspricht effektiv dem Abstand dazwischen. Wenn die Breite des Rechtecks kleiner als der Abstand ist , werden sie als separate Formen wie diese Höhe gezeichnet , vielleicht 20 Pixel. Ich kann den linken Rand auch vertikal wie diesen hinzufügen. Ich positioniere sie 80 Pixel von oben, 100 Pixel von oben entfernt. Wenn ich den Spieler von Feinden treffen lasse, können wir zusehen, dass wir Leben verlieren und dann die Nachricht Game over kommt. Jedes Mal, wenn wir eine Welle von Feinden erfolgreich besiegen, entsteht eine neue Welle. Wir geben dem Spieler ein weiteres Bonus-Leben. Ich teste es, ich habe drei Leben. Ich besiege die Welle, ja, jetzt habe ich vier Leben. Ich habe das Raster auf fünf Spalten und 11 Zeilen festgelegt. Ich muss etwas testen. Wenn das Spiel vorbei ist, möchte ich verhindern, dass der Spieler weitere Punktepunkte erhält. Wir geben diese Punktepunkte hier. Ich werde es nur tun, wenn Game Over falsch ist. Wenn ich jetzt das Spiel beendet habe und mehr Gegner vernichte, bleibt der Spielstand gleich. Hier unten möchte ich noch eine Textzeile hinzufügen. Ich kopiere diese beiden Codezeilen. Die Schrift wird 20 Pixel groß sein. Im Text wird angezeigt, dass Sie R drücken, um neu zu starten. Ich bewege mich unter der Nachricht „Spiel über“ etwas nach unten. Wenn jetzt die Meldung Game over angezeigt wird, erhalten wir eine weitere Meldung, die besagt, dass R gedrückt werden soll, um neu zu starten. Lassen Sie uns diese Funktion tatsächlich implementieren. 10. Neustartmethode: Der Neustart eines Spiels kann auf viele verschiedene Arten erfolgen. Normalerweise mache ich das, wenn ich meine grundlegende Codestruktur finalisiere , wie wir es hier getan haben Zu diesem Zeitpunkt verstehen wir unsere Codebasis ziemlich gut und wir verstehen, welche Variablen Eigenschaften in unserer gesamten Codebasis auf einen Standardwert zurückgesetzt werden müssen , wenn das Spiel neu Ja, ich kann das Spiel auch neu starten indem ich einfach das Browserfenster neu lade, aber lass es uns diesmal richtig machen Zuerst muss ich den Player neu starten. Und ich kann das hier von der Spielerklasse aus machen. Ich werde dem Spieler hier eine benutzerdefinierte Neustartmethode geben. Ich weiß, dass ich beim Neustart möchte, dass der Spieler wieder in die Mitte des Bildschirms wechselt. Ich möchte die Spielerleben auf drei zurücksetzen. Möglicherweise gibt es hier noch mehr Dinge, die später neu gestartet werden müssen nachdem wir das Spielerraumschiff tatsächlich animiert Aber im Moment ist das alles, was wir in diesem Bereich tun müssen. In der Hauptspielklasse erstellen wir die benutzerdefinierte Hauptneustartmethode. Von hier aus rufe ich die Neustartmethode auf, die wir gerade für den Player definiert haben. Ich muss auch die Spieleigenschaften zurücksetzen. Ich gehe hier in den Game Class Constructor und muss mir überlegen, welche Eigenschaften von hier aus die Werte ändern, während wir das Spiel spielen Daher müssen sie auf die Standardeinstellungen zurückgesetzt werden , wenn wir das Spiel neu starten Ich kopiere den ganzen Code und füge ihn hier ein. Die Größe des Feindes wird gleich bleiben. Ich lösche das. Wenn das Spiel neu startet, möchte ich zu einem kleinen Gegnerraster zurückkehren, vielleicht zwei Spalten und zwei Reihen Ich werde alle Wellenobjekte aus diesem Wellenarray löschen , falls es noch welche gibt Wir werden automatisch eine neue Welle von zwei mal zwei Gegnern hineinschieben . Wir setzen den Wellenzähler wieder auf eins zurück, setzen den Punktestand auf Null und setzen das Spiel auf Stürze um. Ich sollte sicherstellen , dass die Werte für Spalten und Zeilen auch hier identisch sind. Denn während ich das Spiel spiele, die Anzahl der Spalten und Reihen nimmt die Anzahl der Spalten und Reihen zu, wenn ich Feinde besiege. Welle für Welle. Wir müssen sicherstellen, dass die Neustart-Methode das gegnerische Grid wieder auf diese geringe Größe zurücksetzt das gegnerische Grid wieder auf diese geringe Größe zurücksetzt Hier unten haben wir einen Key-Down-Event-Listener drin Ich sage, wenn die Taste, die gedrückt wurde, ein kleines R ist, rufen wir die benutzerdefinierte Neustartmethode Wir haben das Spiel gerade geschrieben. Ich zerstöre einige Feinde. Wir drücken R und die erste Welle kommt wieder raus. Ich spiele, das gegnerische Raster wird größer. Ich drücke R und wir setzen das Spiel zurück und die Gegner kehren zum Grundraster von zwei mal zwei zurück. Ja, das funktioniert eindeutig. Sie können hier unseren Operator verwenden und mehrere Tasten haben, die die Neustartmethode auslösen Zum Beispiel Klein- und Großbuchstaben R. Wenn du möchtest, möchte ich nicht, dass der Spieler das Spiel neu starten kann, es sei denn, wir sehen das Spiel auf dem Bildschirm Wir werden die Neustart-Methode nur ausführen , wenn das untere R gedrückt wurde. Wenn Game Over wahr ist, also so, drücke ich jetzt R und es wird nicht neu gestartet Ich erhöhe die Rollen auf zehn, nur um ein Spiel schnell zu beenden, wenn Feinde den unteren Rand des Spielbereichs berühren oder wenn Spielerleben aufgebraucht Ich sehe jetzt, dass das Spiel über dem Bildschirm ist. Wenn ich R auf meiner Tastatur drücke, setzen wir das Spiel Wenn ich die Nummer eins gedrückt halte, um zu schießen, schießt der Spieler alle zehn Projektile sehr schnell in einer einzigen Sequenz ab Und das macht das Spiel tatsächlich zu einfach und trivial. Vielleicht möchte ich verhindern, dass der Spieler das tut. Und ich möchte sichergehen, dass wir jedes Mal, wenn wir den einfachen Laserangriff, das Basisprojektil, abfeuern wollen , den Knopf erneut drücken müssen Wenn wir die Taste gedrückt halten, kommt nur ein Laserprojektil heraus Wir werden später stärkere Angriffe hinzufügen, bei denen ein massiver Laserstrahl abgefeuert wird solange wir eine Taste gedrückt halten Aber dieser starke Angriff wird Energiekosten verbunden sein. Wir können keinen Spam-Mable-Superangriff wie diesen haben, sonst ist das Spiel zu einfach und ohne Punkte Ich werde hier eine einfache Flagge namens fired erstellen. Das bedeutet, dass die Angriffstaste gedrückt und ein Laserprojektil innerhalb des Key-Down-Events abgefeuert wurde Das nehme ich, ich lege es hier hin. Danach habe ich diesen Punkt auf „True“ gesetzt. Und wir werden nur Spieler schießen lassen. Wenn „gefeuert“ falsch ist, bedeutet das, dass nur ein Paar abgeschossen wird. Ereignis „Key-down“. Dann habe ich bei Key Up gesagt, dass dieser Punkt wieder falsch abgefeuert wurde. Okay, jetzt bedeutet ein Tastendruck ein Projektil. Wir müssen die Taste loslassen und erneut drücken , um ein weiteres Projektil abzufeuern. Gut gemacht, bis jetzt vergessen. Wenn Sie sich die erweiterte Version ansehen, steht ein Code aus dieser Phase zum Herunterladen zur Verfügung Im Bereich Ressourcen haben wir ein sehr solides Spielskelett. In unserem Spiel kommen Feinde in immer größeren Wellen. Wir verwenden den Objektpool , um Projektile wiederzuverwenden und die Leistung und Speicherverwaltung unseres Spiels zu verbessern Speicherverwaltung unseres Spiels Lassen Sie einen Spieler mit der Tastatur steuern, der für jede gegnerische Welle, die wir besiegen, ein zusätzliches Bonus-Leben erhält einen Spieler mit der Tastatur steuern, der für jede gegnerische Welle, die wir besiegen, ein zusätzliches Bonus-Leben Und wir haben bereits viele andere kleinere Funktionen implementiert. Es ist an der Zeit, dass wir über Grafik und Animation sprechen. In diesem Projekt konzentrieren wir uns auf die Optimierung der Leistung. Lassen Sie mich Ihnen zeigen, wie Sie das Spiel großartig aussehen lassen, aber gleichzeitig ein sehr leichtes Animationssystem verwenden, das sicherstellt, dass unser Spiel auch auf den ältesten Maschinen reibungslos läuft . 11. Beetlemorph-Feind-Kurs: Der erste Feindtyp, den ich implementieren möchte, ist ein einfacher Weltraumfehler Ein einfacher Feind mit einem Leben, der explodiert, wenn wir ihn treffen Bei einem Projektil können Sie die Tabelle im Abschnitt Ressourcen unten herunterladen . Platzieren Sie das Bild in Ihrem Projektordner und fügen Sie es in die HTML-Indexdatei So. Die Quelle ist Beetle Morph, PNG-ID ist Beetle Wir erklären unsere Feindklasse, ich möchte mehrere Feindtypen ich möchte Ich werde der übergeordneten Feindklasse Eigenschaften und Methoden zuweisen, die von allen Feindtypen gemeinsam genutzt werden. Hier werde ich für jeden Feindtyp separat eine kleinere Unterklasse erstellen für jeden Feindtyp separat eine kleinere Unterklasse Dort werden wir Eigenschaften und Methoden deklarieren , die nur für diesen Feindtyp spezifisch sind Wir werden das mit der Schlüsselwortklasse extends tun. Beetle Morph, erweitert den Feind. Das hier verwendete Schlüsselwort extense macht Beetle Morph zu einer untergeordneten Klasse der feindlichen Elternklasse Wir können dies auch eine Unterklasse nennen und Enemy ist eine Oberklasse . Ich werde dieser Unterklasse auch einen Konstruktor geben , wenn ich eine Instanz dieser Klasse erstelle Zuerst möchte ich den Superklassen-Konstruktor ausführen. Zuerst möchte ich hier den gesamten Code im Konstruktor der übergeordneten feindlichen Klasse auslösen im Konstruktor der übergeordneten feindlichen Klasse Um diesen Konstruktor aufzurufen, muss ich ihm die Spielposition x und die Position y übergeben . Der Konstruktor der Unterklasse erwartet dieselben Argumente Und wir rufen den Superklassenkonstruktor auf, der den Code innerhalb des Konstruktors für die gegnerische Klasse auslöst der den Code innerhalb des Konstruktors für die gegnerische Wir wissen, dass wir ihm diese Werte übergeben müssen, damit der Code tatsächlich Jetzt habe ich den Superclass-Konstruktor ausgelöst und sobald der gesamte Code hier ausgeführt ist, der Code, der Eigenschaften enthält, die von allen Feindtypen gemeinsam genutzt werden, kann ich dieses Schlüsselwort endlich hier verwenden und dieser Unterklasse einige Eigenschaften geben , die nur für diesen bestimmten Feindtyp spezifisch sind nur für diesen bestimmten Feindtyp spezifisch Jeder Feindtyp wird ein anderes Spritesheet-Bild haben. Bei diesem Punktbild handelt es sich um ein Dokument, das Element nach der ID Wir haben ihm in der Index-HTML-Datei die ID Beetle Morph zurückgegeben. Wenn Ihr Browserfenster größer ist als meins, können Sie das Spritesheet auch hier sehen Ich möchte das IMG-Element verstecken. Ich gebe ihm keine Anzeige mit CSS, wir wollen es nur mit Java Script auf Leinwand zeichnen. Wir werden dieses Bild hier in der Draw-Methode für die feindliche Klasse zeichnen. Zeichencode wir auch immer in diese Methode einfügen Welcher Zeichencode wir auch immer in diese Methode einfügen, bestimmt, wie jeder einzelne Feind aussieht. Ich werde die integrierte Draw-Image-Methode verwenden , die mindestens drei Argumente erwartet. Das Bild, das wir zeichnen möchten, Punktbild und die X- und Y-Koordinaten, wo es gezeichnet werden soll. Wenn ich die Änderungen jetzt speichere, erhalte ich eine Fehlermeldung. Diese Fehlermeldung bedeutet, dass das Java-Skript das Bild, das ich zeichnen möchte, nicht finden kann. Das liegt daran, dass ich eine Instanz der übergeordneten feindlichen Klasse erstelle . Diese Klasse hat keine Didot-Image-Eigenschaft. Ich muss die Unterklasse instanziieren und der Konstruktor der feindlichen Klasse wird auch Wenn wir Klassen wie diese erweitern, wird Javascript auch hinter den Kulissen die übliche prototypbasierte Vererbung erzeugen die übliche prototypbasierte Vererbung Wenn ich zum Beispiel die Methode draw und update von einer Instanz der Beetle Morph-Klasse aus der Unterklasse aufrufe , findet Javascript diese Methode in dieser untergeordneten Klasse nicht und sie folgt automatisch der Prototyp-Kette bis zur übergeordneten feindlichen Von dort aus werden die Methoden zum Zeichnen und Aktualisieren ausgelöst. Damit Javascript diese Image-Eigenschaft in der untergeordneten Klasse hier tatsächlich findet , muss ich eine Instanz von Beetle Morph erstellen Stattdessen hat, wie wir gerade erklärt haben, die untergeordnete Klasse Beetle Morph auch Zugriff alle Eigenschaften und Methoden auf alle Eigenschaften und Methoden der übergeordneten feindlichen Wir geben das Spiel und die relative X- und Y-Position des jeweiligen Feindes Welle innerhalb des Rasters Diese Eigenschaften werden hier erwartet . Sie werden an den Konstruktor der gegnerischen Klasse weitergegeben Der gesamte Code im Konstruktor der gegnerischen Klasse wird ausgeführt Darüber hinaus werden wir den Code hinzufügen, den wir hier haben und der nur für die Beetle Morph-Klasse spezifisch ist Beetle Morph ist eine untergeordnete Klasse, Enemy ist eine übergeordnete Klasse Enemy ist 12. Sprite-Animation erklärt: Wir haben diese seltsame Situation. Das liegt daran, dass wir einem Draw Image nur drei Argumente übergeben. Es zeichnet also das gesamte Bild, die gesamte Tabelle, die wir ihm hier übergeben haben , in Originalgröße. An diesen X- und Y-Koordinaten können wir das optionale vierte und fünfte Argument für Breite und Höhe an draw image übergeben . Und wenn wir das tun, wird das Bild zusammengedrückt oder gedehnt, sodass es in den angegebenen Bereich passt. Jetzt drücke ich alle 12 Frames des Spritesheets in den Bereich eines Feindes, eines Wir wollen einzelne Frames aus dem Spritesheet ausschneiden einzelne Frames aus dem Spritesheet Dafür benötigen wir die längste Version der Methode „Bild zeichnen“, mit der wir die meiste Kontrolle über das Bild haben, das wir zeichnen Wir werden der Draw-Image-Methode neun Argumente übergeben. Das Bild, für das wir die Quellquelle Y, die Quellbreite und die Quellhöhe eines Bereichs zeichnen möchten . Ich möchte aus dem Quellbild, dem Ziel X, dem Ziel Y, der Zielbreite und der Zielhöhe ausschneiden, um zu definieren, wo auf der Ziel-Leinwand wir das ausgeschnittene Bild platzieren möchten . Nehmen wir an, ich möchte den Bereich aus den Koordinaten 002, Koordinaten 2050 ausschneiden . Ich möchte diesen ausgeschnittenen Teil des Quellbilds über eine Fläche von 60 mal 60 Pixeln auf der Ziel-Leinwand ausdehnen Teil des Quellbilds über eine Fläche von 60 mal 60 Pixeln auf . Das bekommen wir Was ist, wenn wir einen Bereich mit 00-5050 Pixeln ausschneiden? Idealerweise möchten wir dieselbe Größe verwenden, also verwende ich, wobei die Höhe derzeit 60 mal 60 Pixel Ich habe die Tabellenkalkulationsrahmen mit einer Größe von 80 mal 80 Pixeln erstellt mit einer Größe von 80 mal 80 Pixeln Lassen Sie uns die Bilder in derselben Größe auf Leinwand zeichnen. Die Größe des Feindes wird 80 Pixel betragen. Wir stellen hier Breite und Höhe des Feindes auf die Größe des Feindes ein. Und dann verwenden wir hier diese Breite und Höhe in der Side-Draw-Bildmethode. Jetzt schneiden wir genau einen Frame aus dem Quellbild heraus genau einen Frame aus dem Quellbild Und wir zeichnen es in der gleichen Größe von 80 mal 80 Pixeln auf Leinwand, anstatt die Koordinate 00 fest zu codieren Hier werden wir diese in Variablen umwandeln. Ich werde sie der Unterklasse zuordnen, weil jeder Feindtyp eine andere Anzahl von Frames haben kann eine andere Anzahl von Frames haben In der Tabelle übernimmt Frame X horizontale Navigation innerhalb des Spritesheets In diesem Fall können wir aufgrund der Art und Weise, wie ich dieses Sprysheet strukturiert habe , erkennen, dass wir dadurch Verzerrungs- und Explosionsanimationen erhalten Verzerrungs- und Explosionsanimationen , die abgespielt werden, wenn wir den gegnerischen Frame treffen Y übernimmt die vertikale Navigation innerhalb des Spritesheets In diesem Fall erhalten wir dadurch verschiedene Varianten des Käfer-Morphs Sie können sehen, dass ich etwas Abwechslung geschaffen habe. Ich habe vier Versionen mit unterschiedlichen Flügeln, unterschiedlichen Rüstungen und unterschiedlichen Klauen erstellt unterschiedlichen Rüstungen und unterschiedlichen Klauen Es dient nur dazu, das Spiel visuell etwas interessanter zu Sie können sehen, dass sich jeder Feind etwas anders verzerrt Und wir werden unterschiedliche Explosionsbilder zeigen, wenn wir sie mit dem Projektil treffen Ich hätte die Explosionsanimation aus vielen Animationsframes glatt machen können, wie ich es in meinen anderen Klassen immer mache Aber heute möchte ich Ihnen zeigen, wie Sie die Leistung mit dieser Sprite-Animationstechnik verbessern können, bei der wir nur eine begrenzte Anzahl von Frames in jedem Spritesheet verwenden von Frames in jedem Spritesheet Es wird uns einen kleinen Retro-Effekt geben. Und es wird gut aussehen , weil Animationen auf Tastendrücke und auf Spielereignisse reagieren. Lass mich dir zeigen, was ich meine. Dies sind die Argumente Quelle X und Quelle Y die Methode draw image übergeben wurden. Sie bestimmen die X- und Y-Koordinaten des Schnittbereichs. Derzeit beschneiden wir ein Rechteck von 80 mal 80 Pixeln Ausgehend von den Koordinaten 00 kann ich mit W im Spritesheet herumspringen . Und eine Höhe wie diese, Null mal Breite, Null mal Höhe, ist immer noch dieser Frame ab der Koordinate Wenn ich sage eins mal Höhe, fallen wir hier von der Koordinate 080 ab Hier sehen wir die zweite Zeile im Spritesheet mit einer anderen gegnerischen Variante Ab hier wird das Zweifache der Höhe gekürzt Wenn wir einen weiteren Käfer sehen, dreimal so groß ist, erhält man den letzten Ich kann diesen Wert durch Frame Y ersetzen. Quelle X bewegt sich horizontal auf dem Spritesheet Ein Mal die Breite ergibt diesen Zuschneidebereich. Wenn wir zweimal breit sind, erhalten wir diesen Rahmen. Wenn ich es auf drei erhöhe, befinden wir uns außerhalb der Tabelle und sehen einen leeren Rahmen. Es wird immer noch funktionieren und nichts wird kaputt gehen, aber wir sehen einfach nichts. Ich werde hier die Variable Frame X verwenden. Jetzt kennen wir das Quell-Y-Argument, dieser Code bestimmt eine Rolle. Nun, wenn ich möchte, dass jedem feindlichen Objekt zufällig eines der vier verfügbaren visuellen Elemente zugewiesen wird, eine der vier verfügbaren Petal-Morph-Varianten Ich kann das einfach tun, indem ich Frame Y auf den Zufallswert 0-4 setze. Road 3.5 gibt es zum Beispiel nicht, also benötige ich Ich wickle es in Maskenboden , um die Zahl abzurunden. Jetzt gibt uns diese Codezeile entweder Null oder Eins, oder zwei oder drei. Wir können sehen, dass wir in der Welle zufällige Käfer-Morphen bekommen in der Welle zufällige Käfer-Morphen Perfekt, das ist vertikale Navigation. Jetzt möchte ich auch die horizontale Navigation in der Tabelle verwenden horizontale Navigation in der Tabelle Und ich möchte die Frames so abspielen. Wenn der Feind von einem Projektil getroffen wird, gehen wir zur übergeordneten Feindklasse Weil dieses Verhalten von allen Gegnertypen geteilt wird allen Gegnertypen geteilt Wir werden ihm eine benutzerdefinierte Methode geben. Ich nenne das zum Beispiel Treffer , der den Schadenswert als Argument verwendet. Ich mache das so , weil wir vielleicht Waffen haben werden , die mehr Schaden anrichten. Später werden wir Feindtypen mit mehr als nur einem Leben haben , die mehrfach getroffen werden müssen . Drinnen reduziere ich die Leben des Feindes um den Schaden. Jeder Feindtyp wird eine unterschiedliche Anzahl von Leben haben. Beetle Morph ist ein einfacher, einfacher, matschiger Feind mit Ich habe diese Eigenschaft hier der Unterklasse zugewiesen. Wir müssen auch Max Frame definieren. In diesem Fall haben wir Frames 01.2 Max Frame hier ist zwei. Ich werde auch eine Eigenschaft erstellen, die ich Max Lives nenne , und ich setze sie auf den anfänglichen Wert für entfernte Leben Das tun wir, weil ich möchte, dass sich das gegnerische Objekt an seine ursprüngliche maximale Anzahl von Lebenspunkten erinnert , damit wir dieselbe Anzahl an Punktepunkten belohnen können, sobald wir den Feind besiegt haben. Wir werden den Code in einer Minute schreiben. Wenn hier oben ein aktives Projektil mit einem Feind kollidiert, setzen wir die Markierung zum Löschen bei diesem Feind auf wahr Ich möchte diese Logik ändern. Anstatt den Feind sofort zu löschen, werden wir diese Punktreffermethode nennen, die wir gerade definiert haben. Ich gebe ihm einen Schaden in Höhe von einem Schaden wie diesem. Vorerst. Insider-Treffermethode reduzieren wir die Leben dieses Feindes um diesen Schadenswert. Wenn ihr euch erinnert, erstelle ich danach eine separate If-Aussage und sage, wenn das Leben weniger als eins ist, wenn wir den Feind so oft getroffen haben, dass er all seine Leben aufgebraucht hat, was in diesem Fall sehr einfach war weil jeder Käfer-Morph nur ein Leben hat Ich möchte eine horizontale Navigation rund um das Spritesheet auslösen , um diese Dieser Punktrahmen X plus. Jedes Mal, wenn wir Frame X um eins erhöhen, prüfen wir, ob das aktuelle Frame X mehr als das maximale Frame ist. Wir überprüfen, ob wir alle horizontalen Frames im Spritesheet abgespielt alle horizontalen Frames im Spritesheet Erst nachdem wir den Feind getroffen haben, haben wir ihm all seine Lebenspunkte aufgebraucht Und erst nachdem wir alle horizontalen Frames des Spritesheets abgespielt haben, sind wir bereit, Marked for Deletion auf true zu setzen Dadurch wird der Feind aus dem feindlichen Array herausgefiltert. Aufgrund der Logik, die wir zuvor geschrieben haben, automatische Garbage-Collection-Prozess wird der automatische Garbage-Collection-Prozess ihn irgendwann löschen, so wie das bei allen Variablen der Fall ist , so wie das bei allen Variablen der Fall die im Code nicht mehr verwiesen wird. Ich werde diese Codezeile auch erst streichen , wenn wir den Feind vernichtet haben. Wenn Game Over falsch ist und das Spiel immer noch läuft, erhöhen wir die Punktzahl um die ursprüngliche Anzahl feindlicher Leben. Bei dieser maximalen Anzahl an Lebenspunkten erhalten Gegner, die ein Leben hatten, einen Punktepunkt. Später werden wir einen anderen Feindtyp mit drei Leben haben , der uns drei Punkte einbringt, wenn er zerstört wird. Hier unten können wir sehen, dass wir Beetle Morph ein Leben geben, sodass wir nur einen Punktepunkt erhalten Im Code kann ich sehen, dass horizontale Frames abgespielt werden, aber sie werden sehr schnell abgespielt Wir können kaum bemerken , was passiert. Wir animieren wahrscheinlich mit 60 Bildern pro Sekunde für jeden Animationsframe Wir erhöhen Frame X um eins, und wir haben nur drei Frames Diese ganze Animation wird in einem Bruchteil einer Sekunde abgespielt. Wir brauchen eine Möglichkeit, die Geschwindigkeit der Sprite-Animation zu kontrollieren. Ich kann die Rechtecke um Feinde herum auskommentieren . Das sieht besser aus Ich möchte, dass sich die Wellen langsamer bewegen, vielleicht ein Pixel pro Animationsframe Ich möchte auch, dass die Wellen horizontal in der Mitte beginnen. Die anfängliche X-Koordinate der Welle entspricht der Breite des Spiels mal 0,5 minus der halben Breite der Welle. Ich möchte, dass sich jede Welle von dieser mittleren Position aus zufällig nach links oder rechts bewegt . Ich werde hier einen kleinen Trick machen. horizontale Geschwindigkeit entspricht einem Ausdruck, sogenannten ternären Operator, dem einzigen Java-Script-Operator mit drei Operanden Wir werden ihn hier als einfache einzeilige L-Anweisung verwenden. Wenn die Methode random kleiner als 0,5 ist ungefähr in 50% der Fälle, setze Geschwindigkeit x auf minus ein L festgelegte Geschwindigkeit x zwei plus einen ternären Operator, der als L-Anweisung verwendet wird, so lautet dieser Ausdruck, um zu bewerten, ob das Fragezeichen wahr ist, mach diesen L-Dolon, tu das Wir hätten hier auch eine reguläre FL-Syntax verwenden können, aber das ist schneller Wenn ich das Projekt jetzt ein paar Mal neu lade, kann ich sehen, dass Wellen in der Mitte beginnen und sich nach dem Zufallsprinzip mit einer Geschwindigkeit von einem Pixel pro Animationsframe nach links oder rechts bewegen einer Geschwindigkeit von einem Pixel pro Animationsframe nach links oder rechts Wenn wir einen Feind treffen, horizontale Frames abgespielt, aber sie werden sehr schnell abgespielt Wie können wir die Animationsgeschwindigkeit kontrollieren? Lass es mich dir zeigen. 13. Animationstiming: Auf Anfrage eingebaut. Die Animationsframe-Methode die wir hier verwenden, weist zwei Besonderheiten auf. Die erste besteht darin, dass sie automatisch die Geschwindigkeit anpasst, mit der der nächste Animationsframe angezeigt wird Es passt es an die Bildschirmaktualisierungsrate an, da es keinen Sinn macht, mehr Frames zu berechnen und zu zeichnen, als Sie mit der Bildwiederholfrequenz Ihres Computermonitors anzeigen können Das Zeichnen dieser zusätzlichen Frames wäre eine Verschwendung von Leistung, da wir sie sowieso nicht sehen könnten sie sowieso nicht sehen Die zweite Besonderheit besteht darin, dass automatisch ein Zeitstempel generiert wird, bei dem es automatisch ein Zeitstempel generiert wird, bei dem sich einfach um einen Wert von Millisekunden handelt, die seit dem Aufruf des ersten Anforderungsanimationsframes in der Sequenz vergangen sind dem Aufruf des ersten Anforderungsanimationsframes in der Sequenz Wir werden diese automatisch generierten Zeitstempel verwenden , um regelmäßige Ereignisse in unserem Spiel auszulösen Zum Beispiel kann ich den horizontalen Sprite-Frame im gegnerischen Spritesheet nur einmal alle 500 Millisekunden aktualisieren , was zwei Bildern pro Sekunde entspricht Wir können jede gewünschte Bildrate einstellen. Lassen Sie mich Ihnen zeigen, wie das geht. Wir werden einen Helfer brauchen, er lässt die Variable, die ich beim letzten Mal aufgerufen habe. Diese Variable enthält den Wert eines Zeitstempels aus der vorherigen Animationsschleife. Damit wir ihn mit dem Zeitstempel aus dieser Animationsschleife vergleichen können . Und wir können berechnen wie viele Millisekunden zwischen den beiden Animationsframes vergangen sind zwischen den beiden Animationsframes Normalerweise nennen wir diesen Wert Delta Time Request. Der Animationsframe generiert automatisch einen Zeitstempel und übergibt diesen Wert hier an die Animationsfunktion. Alles, was ich tun muss, um diesen Wert zu verwenden , ist, ihm einen Variablennamen zuzuweisen Ich werde es hier Timestamp nennen. Wenn Sie diesen Zeitstempel nun direkt in Animate protokollieren, werden Sie sehen, wie er Millisekunden seit dem Start der ersten Schleife zählt Millisekunden seit dem 1 Sekunde entspricht 1.000 Millisekunden. Deltazeit ist die Anzahl der Millisekunden unser Computer benötigt Sie ist die Differenz zwischen dem Zeitstempel dieser Animationsschleife dem Zeitstempel der vorherigen Sobald wir die Deltazeit berechnet haben, setzen wir last time auf den Zeitstempel dieser Animationsschleife , sodass dieser als alter Zeitstempel in der nächsten Animationsschleife verwendet werden kann . Während die Animationsschleife läuft und der Anforderungsanimationsframe neue Zeitstempel generiert, vergleichen wir immer den aktuellen Zeitstempel mit dem alten Zeitstempel aus der vorherigen Schleife und dieser Unterschied zwischen ihnen gibt uns Deltazeit. Wir benötigen diesen Delta-Zeitwert in der Render-Methode. Ich übergebe es hier als Argument für die Hauptspielklasse innerhalb von Render Ich stelle sicher, dass der Wert erwartet wird. Ich werde von hier aus Delta Time nachfragen , um sicherzugehen, dass es funktioniert hat. Wenn ich ganz nach oben scrolle, kann ich sehen, dass die ersten beiden Werte der Deltazeit keine Zahl sind . Dies könnte ein Problem sein, je nachdem, wie wir den Delta-Zeitwert verwenden wollen. Dieser Non hier könnte unseren Code knacken. Möglicherweise erhalten wir keine, weil hier die erste Animationsschleife ausgelöst wird, diese aber keinen automatisch generierten Zeitstempelwert Zeitstempel wird nur generiert, wenn die zweite Schleife hier durch den Anforderungsanimationsframe ausgelöst wird hier durch den Anforderungsanimationsframe Für die erste Schleife ist der Zeitstempel undefiniert und ich berechne die undefinierte Deltazeit minus eins, was mir keinen, keinen Zahlenwert ergibt keinen Zahlenwert Ich kann das einfach beheben, indem Null als ersten Zeitstempel übergebe Ab der nächsten Schleife werden wir beginnen, richtigen automatisch generierten Zeitstempel zu erhalten Wenn ich jetzt ganz nach oben scrolle, bekommen wir Zahlen, alle kommen hierher. Wie Sie sehen können, beträgt meine Delta-Zeit etwa 16,6 Millisekunden Mein Computer benötigt ungefähr 16,6 Millisekunden, um Millisekunden 1.000 Millisekunden geteilt durch 16,6 sind ungefähr 60. Ich kann sehen, dass ich eine Animationsgeschwindigkeit von 60 Bildern pro Sekunde erhalte Animationsgeschwindigkeit von 60 Wenn du einen Gaming-Bildschirm mit hoher Bildwiederholfrequenz hast, hast du möglicherweise eine viel geringere Delta-Zeit, was auch bedeutet, dass dein gesamtes Spiel schneller läuft als meins. Denn wie bereits erwähnt, Frame-Methode „Animation anfordern“ automatisch passt sich die Frame-Methode „Animation anfordern“ automatisch an die Bildwiederholfrequenz Ihres Bildschirms Wir können jetzt hier die FPS des gesamten Spiels steuern , indem wir einfach sagen, dass wir nur Render aufrufen, um den nächsten Spielframe zu aktualisieren und zu zeichnen Wenn zum Beispiel 50 Millisekunden vergangen sind, was uns 1000/50 ergeben würde, wäre das eine Animationsgeschwindigkeit von 20 FPS Im Grunde meine ich hier, dass wir diesen Delta-Zeitwert jetzt verwenden können , um alle periodischen Ereignisse in unserem Code auszulösen Die Deltazeit beträgt Millisekunden. Wir können also sagen, mach alle zehn Millisekunden etwas, mach alle 100 Millisekunden etwas. Wir können tun, was wir wollen. Es könnte sein, dass das gesamte Spiel alle 50 Millisekunden aktualisiert wird oder etwas anderes Wir können alle 15 Sekunden einen Boss, ein Power-Up oder einen Spezialfeind hinzufügen alle 15 Sekunden einen Boss, ein Power-Up oder einen Spezialfeind Das würde auch funktionieren. Ich werde Ihnen nun zeigen , wie Sie Delta Time verwenden, um Tabellenkalkulationsrahmen in unserer Codebasis regelmäßig zu aktualisieren. Weil wir die Animation verlangsamen wollen, die abgespielt wird , nachdem wir den Feind mit einem Projektil getroffen haben Wir werden dies später auch verwenden, um andere Dinge in unserem Spiel in diesem Retro-Stil mit niedriger Bildrate zu animieren , der es uns ermöglicht, Tabellen mit nur wenigen Frames zu erstellen, um eine nette Leistungssteigerung zu erzielen Es ermöglicht uns auch, die Dinge getrennt voneinander zu timen. In unserer Codebasis habe ich Spielerbewegungen, Projektile und Gegner, die mit vollen 60 FPS animiert Aber die Geschwindigkeit, mit der die Tabelle selbst Frames tauscht hat ihre eigene Lassen Sie mich Ihnen zeigen, wie Sie das umsetzen können. Ich gehe hier zur Hauptspielklasse, innerhalb der Render-Methode lösche ich dieses Consolo Selbst ein solches Consolo in unserem Spiel würde sich negativ auf die Leistung auswirken Es ist gut, es zum Buggen zu verwenden, aber vergiss nicht, dein Consolo immer zu löschen, besonders die, die für jeden Animationsframe ausgeführt werden, wie dieser Ich möchte hier in meiner Hauptspielklasse eine Flaggenvariable haben hier in meiner Hauptspielklasse Dieses Sprite-Update-Flag wird immer falsch sein. Es wird nur einmal alle , sagen wir 500 Millisekunden, auf true umgestellt sagen wir 500 Millisekunden Es wird für einen Animationsframe auf true umgestellt. Dadurch können alle Spritesheets in unserem Spiel aktualisiert werden und dann wird wieder auf Herbst umgeschaltet , dann wird wieder auf Herbst umgeschaltet bis die nächsten 500 Millisekunden Ich möchte alle Sprite-Frames in unserem Spiel regelmäßig aktualisieren . Nehmen wir an, wir können diesen Millisekundenwert einfach alle 500 Millisekunden in einen anderen Wert ändern Wert Später benötigen wir zwei Hilfsvariablen. Der Sprite-Timer zählt zwischen Null und Sprite-Intervall. Jedes Mal, wenn das Sprite-Intervall erreicht wird, wird Sprite Update auf true gesetzt und der Timer wird auf Null zurückgesetzt, sodass er bis zum nächsten periodischen Trigger erneut zählen kann Ich werde mich um die Sprite-Timing-Logik kümmern. Hier sage ich, wenn Sprite-Timer mehr als Sprite-Intervall ist , mach etwas anderes Mach etwas anderes. Wenn der Sprite-Timer so viel Delta-Zeit angesammelt hat, dass er jetzt mehr als 500 Millisekunden beträgt , setzen wir Sprite Update auf true Wir setzen den Sprite-Timer auf Null zurück , damit Sprite Update bleibt nur für einen einzelnen Animationsframe wahr, weil es in der nächsten Schleife, wenn die L-Anweisung ausgeführt wird, sie sofort wieder auf False zurückgesetzt wird Diese L-Anweisung bedeutet, dass Sprite-Timer kleiner als das Sprite-Intervall ist In diesem Fall möchten wir, dass der Timer die Delta-Zeit weiter akkumuliert , bis wir mehr als das Intervall akkumuliert haben Da die Deltazeit die tatsächliche Anzahl von Millisekunden ist, werden diese periodischen Ereignisse Auf langsam und schnell aktualisierten Bildschirmen wird es Es wird nicht genau dasselbe sein, denn wenn ich den Sprite-Timer hier auf Null zurücksetze, wird es in den meisten Fällen ein bisschen zusätzliche Delta-Zeit geben, die ich hier nicht berücksichtige, was einen Unterschied machen wird Damit bin ich einverstanden. Wenn Sie absolute Präzision wollen, nehmen Sie hier die Deltazeit, die den Intervallwert von 500 überschreitet , und weisen Sie den verbleibenden Wert hier der Startzeit zu Anstatt den Timer wieder auf Null zu setzen , sollte er dadurch noch genauer Ich glaube nicht, dass ich im Moment ein solches Maß an Genauigkeit für mein Spiel benötige . Ich möchte vorerst unnötige Berechnungen vermeiden. Für maximale Leistung haben wir jetzt diese Sprite-Update-Eigenschaft, die einmal alle 500 Millisekunden, nur für einen Animationsframe, auf true gesetzt wird In der restlichen Zeit ist sie auf False gesetzt. Wir können es verwenden, um Sprite-Animationen auszulösen, wenn wir Feind mit einem Projektil treffen und wenn die Lebenspunkte des Feindes weniger als Null sind Anstatt Frame X in der Tabelle sofort zu vergrößern X in der Tabelle sofort zu Wir werden es nur tun, wenn das Sprite-Update wahr ist. Lass es uns testen. Ich schieße auf einen Feind und alle 500 Millisekunden wird der nächste Animationsframe perfekt abgespielt der nächste Animationsframe perfekt Jetzt haben wir das Problem, dass der Feind Projektile stoppt, bis die Animation fertig abgespielt ist Projektile stoppt, bis die Sobald wir den Feind getroffen haben und Explosionsanimation langsam abgespielt wird, möchte ich, dass das nächste Projektil diesen Feind ignoriert, durch ihn hindurchfliegt und den Feind hinter ihm trifft Ich mache das, indem ich nur die Kollision zwischen Projektilen und Feinden überprüfe Kollision zwischen Projektilen und Wenn dieser Feind mehr als null Leben hat. Auf diese Weise haben wir die vollständige Kontrolle über die Geschwindigkeit der Streuanimation. Jetzt kann ich es hier unten ändern. Nehmen wir an, ich möchte, dass sie etwas schneller animiert Jetzt animieren sie schnell, aber es ist immer noch langsam genug, damit wir die einzelnen Frames sehen können, wonach ich gesucht habe Normalerweise erstelle ich beim Erstellen meiner Spritesheets komplexe Animationsschleifen mit 20 oder 30 Animationsframes Aber manchmal, wenn viele Gegner gleichzeitig auf dem Bildschirm animieren, kann das das Spiel verlangsamen, wenn Spritesheets mit weniger Frames verwendet werden. Wenn Sie steuern, wie wechseln schnell wir bei der Animation dieser Spritesheets von Bild zu Bild ist eine einfache und leistungsfreundliche Methode, um mit diesem Problem umzugehen 14. Spieleranimation: Ich wollte die Farbe von Projektillasern zum Beispiel auf Gold ändern Projektillasern zum Beispiel auf Gold Wenn ich den Phil-Stil so einstelle, ändert sich der gesamte Leinwandstatus Jetzt verwenden alle Formen auf der Leinwand, die Phil verwenden , diese goldene Farbe. Ich habe die Breite auf vier Pixel geändert, vielleicht nur auf drei. Ja, das sieht besser aus. Ich möchte diesen Phil-Stil nur auf dieses Phil-Rechteck beschränken . Cole, ich werde diesen Codeblock zwischen sicheren und wiederherstellenden Methoden einordnen. Auf diese Weise wird jeder Code , der sich außerhalb dieses Bereichs befindet , nicht von diesem Füllstil beeinflusst . Laser sind Gold, alles andere bleibt weiß. Wenn ich zurück zur Beetle-Morph-Klasse gehe und die Lebenspunkte auf drei setze, muss ich jeden Feind dreimal treffen, um ihn zu vernichten Jeder belohnt uns mit drei Punkten. Alles funktioniert wie vorgesehen. Ich setze es wieder auf eins zurück. Ich habe einen speziellen Plan für Feinde, die mehr als ein Leben haben. Weil ich den Schaden Schritt für Schritt zeichnen und animieren möchte , jedes Mal, wenn wir sie treffen, um ihr Leben zu reduzieren Zu diesem Teil kommen wir später. Es ist Zeit, den Spieler zu zeichnen und zu animieren. Ich möchte, dass der Spieler reagiert, wenn er ein Laserprojektil Später werde ich vielleicht auch fortgeschrittene Angriffe ausführen. Ich denke an etwas Ähnliches. Und das. Beachten Sie, dass das Raumschiff des Spielers für jeden Angriffstyp unterschiedlich animiert Gleichzeitig möchte ich auch Spielerjets, die Antriebsraketen, animieren, wenn wir uns nach links bewegen, oder? Und wenn wir stehen, werden die Antriebsraketen animiert, um diese Bewegung widerzuspiegeln. Ich möchte, dass die Aufnahmeanimation reagiert, wenn wir einen Hotbutton drücken Und ich möchte, dass die Jets völlig unabhängig davon reagieren. Bewegungstasten drücken Wie bewirken wir, dass die Spieleranimation auf zwei verschiedene unabhängige Eingaben reagiert, wenn wir die ? Lassen Sie mich Ihnen zeigen, dass Sie alle Kunst-Assets im Bereich Ressourcen herunterladen können . Im Folgenden werden wir Spieler-PNG-Jets und Player-Underscore-Jets, PN G, in das Projekt aufnehmen und ihnen die IDs der Spieler- und Spielerjets geben IDs der Spieler- und Spielerjets Auf diese Weise verstecke ich sie mit CSS und verwende dabei ihre IDs. Ich speichere Änderungen, die ich gerade in all meinen Dateien vorgenommen habe. Hier werden wir das Spritesheet des Haupt-Players als Image-Eigenschaft einfügen Und wir verweisen Java-Skript anhand seiner ID auf dieses Bildelement In unserer benutzerdefinierten Zeichenmethode kann ich die integrierte Methode zum Zeichnen von Bildern verwenden. Nochmals, um das Raumschiff auf Leinwand zu zeichnen. Wir wissen bereits, dass wir ihm mindestens drei Argumente geben müssen ihm mindestens drei Argumente geben Das Bild, das wir zeichnen wollen, und die X- und Y-Koordinaten. Wo soll ich es zeichnen? Dadurch wird das gesamte Spritesheet gezeichnet, alle vier Frames. Ich passe die Spielerbreite 240 Pixel und die Höhe 220 an die Framegröße Ich passe die Spielergeschwindigkeit auf fünf Pixel pro Animationsframe an. Wir benötigen die Eigenschaft Frame X, um horizontal im Spritesheet zu navigieren benötigen wir Frame Y nicht für die vertikale Navigation Diesmal benötigen wir Frame Y nicht für die vertikale Navigation, da dieses Spritesheet nur eine Zeile hat Wir haben auch schon gelernt , dass wir die Draw Image-Methode, optional Breite und Höhe, übergeben können optional Breite und Höhe Und es wird das gesamte Spritesheet, in diesem Fall alle vier Frames, auf die Fläche eines Frames zusammendrücken in diesem Fall alle vier Frames, auf die Fläche eines Frames Wir wollen jeweils nur diesen einen Frame ausschneiden. Wir müssen also die längste Version der Draw-Image-Methode verwenden . Ich füge Quelle x, Quelle Y, Quellbreite und Quellhöhe sowie die Werte hinzu, die wir an die Methode draw image übergeben , da diese Argumente bestimmen, welchen Bereich wir aus dem Quellbild ausschneiden möchten. Dieser Frame würde von der Koordinate 00 auf dem Bild bis zur Breite und Höhe des Players reichen, was der Breite und Höhe eines einzelnen Frames in meinem Spritesheet entspricht Höhe eines einzelnen Frames in meinem Spritesheet Ich möchte meine Kunstobjekte in derselben Größe erstellen , in der sie im Spiel angezeigt werden dabei darauf, dass deine Bilder scharf und detailliert genug sind, aber nicht größer als nötig sind. Ich möchte in der Tabelle navigieren. Ich werde wieder das Quell-X-Argument verwenden, um den horizontalen Zuschnitt in der Koordinate Null mal der Breite eines einzelnen Frames zu definieren den horizontalen Zuschnitt in . Ist dieser Rahmen einmal breit, ist dieser Bereich, zwei ist das und drei ist das. Wir werden einige davon später verwenden, wahrscheinlich für Superwaffen, die ich einsetzen möchte. Ich werde das weiße Rechteck auskommentieren. Wir brauchen es nicht mehr, es sei denn, wir wollen die genauen Grenzen von Spielerkollision, Trefferbox anzeigen . Ich ersetze das durch die Eigenschaft Frame X. Und jetzt kann ich ändern, welcher Frame im Spritesheet des Players hier angezeigt wird In diesem Bereich werde ich mich mit der internen Sprite-Frames-Logik befassen. Ich überprüfe, ob die Angriffstaste gedrückt wurde. In unserem Fall überprüfen wir, ob das Punkttasten-Array ist, das wir hier über die Didot-Spielreferenz zugreifen Und es befindet sich hier auf dem Hauptspielobjekt. Wenn eine Taste gedrückt wird, fügen wir sie dem Array hinzu. Wenn es losgelassen wird, entfernen wir es aus dem Array. Wenn das Tastenfeld die Nummer eins enthält, was bedeutet, dass wir die Taste Nummer eins auf der Tastatur gedrückt halten , Frame X eins. Frame X ist Null, wir springen zwischen diesen beiden Frames hin und her, wenn wir die einfache Angriffstaste drücken. Lass es uns testen. Eigentlich werde ich die Projektile hinter dem Spieler zeichnen , indem ich diese Codezeilen hier vertausche Wenn ich jetzt schieße, bekommen wir eine subtile Bewegung und Animation der Laserkanone auf unserem intergalaktischen Raumschiff Ich werde Bild zwei etwas später für einen weiteren Spezialangriff verwenden , aber wir können die Animation hier testen Ich habe den letzten Frame für den Superlaserangriff gemacht. Es funktioniert nicht wirklich gut mit dem Basisprojektil. Wir werden wahrscheinlich all diese Frames in einem Bonus-Abschnitt des Kurses verwenden , wenn du willst, dass ich das mache Im Moment haben wir nur ein einfaches Basis-Laserprojektil. Und die Spielertabelle reagiert und animiert. Wenn wir als Benutzer die Angriffstaste drücken, animiere ich Spielerjets, die Ich möchte, dass sie auf unterschiedliche Eingaben reagieren. Wird je nach Bewegungsrichtung animiert, je nachdem, welche Bewegungstaste gedrückt wird Am einfachsten ist es, diese Jets als separates Bild zu zeichnen , das wir dann zusammen mit dem Spieler überlagern können Lass mich dir zeigen, was ich meine. Wir haben das Player-Jets-Bild bereits in das Index-HTML aufgenommen und es mit CSS erreicht. Ich werde es hier mit Javascript referenzieren. Da diese Jets das Bild unterstreichen, kopiere ich diesen Zeichen-Bildcode und ich werde auch ein Jets-Bild wie dieses zeichnen Es sieht so aus, als ob Javascript das Bild nicht finden kann. Lassen Sie uns den HTML-Index einchecken. Oh ja, ich habe ihm eine Vorstellung von Player-Underscore-Jets gegeben. Jetzt zeichne ich diesen Rahmen. Ich werde eine Immobilie erstellen. Ich nenne zum Beispiel Jets Frame. Es wird dem gleichen Zweck dienen wie Frame X für die Tabelle der Spieler. Wir werden den Jets-Frame für die horizontale Navigation in der Tabelle verwenden . Ich werde es hier innerhalb des Quell-X-Arguments verwenden. Jetzt ist der Jets-Frame Null und wir sehen diesen Frame. Wenn der Jets-Frame eins ist, sehen wir diesen Frame. Und wenn es zwei sind, sehen wir diesen Rahmen. Wir beginnen mit Jets Frame eins, weil das der mittlere Grundzustand innerhalb der Aktualisierungsmethode ist. Wir verfolgen bereits das Drücken der Tasten Pfeil links und Pfeil rechts. Wir erweitern diesen Codeblock hier. Wenn wir uns nach links bewegen, wird der Jet-Frame Null sein. Ich teste es. Ja, L. Wenn wir uns nach rechts bewegen, werden zwei Düsen links, rechts, links, rechts sein. Ja, ich werde auch die L-Anweisung hinzufügen , wenn die Pfeiltaste links oder rechts nicht gedrückt werden. Wir setzen den Düsenrahmen auf eins. Toll, das ist eine Möglichkeit, eine Figur so zu animieren , dass sie auf mehrere unabhängige Gruppen von Eingaben reagiert mehrere unabhängige Gruppen von Eingaben Teilen Sie es einfach in mehrere Ebenen auf, die separat reagieren. Was wir hier gemacht haben, ist auch hervorragend für die Leistung. Wir haben nur ein paar Animationsframes, aber es fühlt sich sehr reaktiv und animiert an, weil es sofort reagiert. Wenn du mitprogrammierst, kannst du das Gameplay selbst testen und sehen, wie es sich anfühlt. Bevorzugst du es, wenn ich komplexe Animationssequenzen mache , wie ich es normalerweise für meine anderen Spiele mache, mit 30 Animationsframes pro Loop? Oder habe ich dich davon überzeugt, manchmal diese leistungsfreundlichere Animationstechnik auszuprobieren diese leistungsfreundlichere Animationstechnik , die nur eine minimale Anzahl von Frames verwendet? Ich werde eine größere Welle erzeugen, zwei mal acht Gegner. Und ich lasse zu, dass es ein Spiel beendet, indem es Spielerleben aufbraucht Beachten Sie, dass, wenn Feinde den Spieler treffen und er zerstört Sie spielen die Explosionsanimation nicht so ab, wie sie sollten. Wir kümmern uns hier um Kollisionen zwischen Feinden und dem Spieler Ich möchte diesen kollidierenden Feind nicht sofort zum Löschen markieren Ich lösche diese Codezeile und werde sie auch löschen. Ich möchte die Punktzahl nicht reduzieren, wenn ein Spieler mit einem Feind kollidiert Stattdessen setze ich die Lebenspunkte von Feinden einfach auf Null, wenn sie den Spieler treffen Dadurch wird der Codeblock in der folgenden Schleife ausgelöst. Jetzt habe ich ein anderes Problem. Weil das Treffen eines Feindes alle drei Spieler Leben kostet, weil wir mit dem Feind kollidieren , der bereits Null hat Und es reduziert das Leben eines Spielers pro Animationsframe. Dieser Feind hat keine Leben, aber er ist immer noch da drin. Es animiert die Explosionssequenz und wir kollidieren damit Ich möchte die Kollision zwischen Spieler und Feinden nur überprüfen , wenn das feindliche Objekt mehr als null Leben hat Jetzt sind Feinde animiert und jede feindliche Kollision nimmt nur einem Spieler das Leben. Ich kann R drücken, um das Spiel neu zu starten. Ich werde die Anzahl der Projektile im Objektpool auf 15 erhöhen , damit uns nicht das Wasser ausgeht. So oft werde ich die Sprite-Animation verlangsamen, vielleicht den nächsten Animationsframe bereitstellen Einmal alle 150 Millisekunden hier, wo wir mit lockerem Zustand umgehen, benutze ich unseren Operator und ich sage, ob dieses Spiel, Punktspieler, Punkt lebt, weniger als ein Triggerspiel von hier aus vorbei ist weniger als ein Triggerspiel Und in diesem Fall brauchen wir diesen Code nicht. Ich möchte auch nicht, dass die Feinde gelöscht werden , wenn sie den unteren Rand des Spielbereichs berühren. Ich lösche das auch. Ich sagte, die erste feindliche Welle ist eins mal eins. hier in der Spielerklasse Ich werde hier in der Spielerklasse Max Lives Property erstellen. Es werden zum Beispiel zehn sein. Innerhalb der Textmethode „Status zeichnen“. Ich kopiere das für eine Schleife, die Leben zeichnet, und ich werde zuerst die maximale Anzahl von Leben zeichnen. Die maximale Anzahl potenzieller Leben besteht nur aus Rechtecken mit leeren Strichen wie diesem Ich mache sie größer, vielleicht auch hier zehnmal 15 Pixel 20 Pixel Abstand hier. Und hier. Ich möchte, dass die Linienbreite nur ein Pixel beträgt, was auch der Standardwert auf der Leinwand ist. Ich muss es überhaupt nicht deklarieren, und das ist das Bild, das ich wollte. Wenn wir eine Welle besiegen, erhalten wir ein zusätzliches Leben und ein Strichrechteck wird zu einem Feldrechteck. Wir können leicht erkennen, dass wir derzeit fünf von maximal zehn möglichen Leben haben . Ich muss auch hier zur Linie 247 gehen. Wenn eine neue Welle ausgelöst wird, geben wir dem Spieler nur dann ein weiteres Leben, wenn die aktuellen Lebenspunkte des Spielers geringer sind als die maximalen Lebenspunkte des Spielers. Wir haben viele Funktionen implementiert und viele neue Techniken gelernt. Wir haben ein vollständig spielbares Spiel mit steigendem Schwierigkeitsgrad, je größer die Wellen werden. im Laufe des Spiels als Nächstes Was sollte ich im Laufe des Spiels als Nächstes implementieren Bosskämpfe, Superwaffen, Feindtypen. Wie wäre es mit Feinden, die mehrere Leben und mehrere beschädigte Zustände haben ? Lassen Sie mich in den Kommentaren wissen, ob Sie es hier bis zum Ende codieren und welche Bonusfunktionen ich als Nächstes von den von mir vorgeschlagenen Funktionen implementieren soll , oder Sie können mir Ihre ursprünglichen Ideen geben. Gibt es irgendwelche Features im ursprünglichen Space Invaders-Spiel, die ich vergessen habe und die ihr gerne in unserer Version sehen Klicken Sie auf „Gefällt mir“, wenn Sie einen Wert gefunden haben. Und schau dir die Videobeschreibung an, wenn du mehr Spiele mit mir machen willst. 15. Zusätzliche Funktionen: Gepanzerte Feinde: Was Feinde in einem zweitägigen Spiel wie diesem interessant macht , können Dinge wie ihre Verhaltenseigenschaften und auch ihre Optik sein und auch ihre Verschiedene Kombinationen von Gegnertypen in unseren Spielen sollten unterschiedliche Situationen und Herausforderungen sorgen für unterschiedliche Situationen und Herausforderungen sorgen, mit denen sich der Spieler Heute werden wir einen einzigartigen Feindtyp entwerfen und implementieren und ihn zu einem Teil unseres Spiels machen Okay, es sieht so aus, als ob wir uns irgendwo tief im Weltraum befinden und die interstellare Leere erkunden, die interstellare Leere erkunden die dem Kipergürtel in unserem Sonnensystem ähnelt Dieser unbekannte Teil des Weltraums hat einen riesigen Asteroidengürtel. Diese Asteroiden sind meistens nur feste Eis- und Staubbrocken Es scheint, dass diese außerirdischen Lebensformen gut gerüstet sind, um im Vakuumraum zu überleben , und sie haben ein dickes überschüssiges Skelett, das sie im Falle einer Kollision mit einem Asteroiden schützt sie im Falle einer Kollision mit einem Asteroiden Interessant, wie kommen wir an ihnen vorbei? Ich werde unsere Laser auf eine höhere Frequenz kalibrieren. Es sollte in der Lage sein, ihre Granaten zu knacken, aber vielleicht braucht es mehrere gezielte Treffer Perfekt. Lass es uns machen. 16. Rhinomorph-Feind-Kurs: Ich habe vier visuelle Varianten für die neue feindliche Klasse Rhinomorph entworfen für die neue feindliche Klasse Rhinomorph Wenn Sie sich das Spritesheet genau ansehen, stellen Sie fest, dass sie alle auf unterschiedliche Weise beschädigt werden Mein Ziel ist es, ihren Sprite-Frame grundsätzlich mit ihrer Gesundheit in Verbindung zu bringen Nehmen wir an, diese Spalte im Spritesheet ist der Feind mit voller Gesundheit Es wird vier Leben haben. Wenn wir es einmal drücken, wird dieser Frame angezeigt. Das ist der Feind mit zwei Leben und einem Leben. Beim letzten Treffer wird diese Animation abgespielt. Wie gewohnt können Sie alle Kunst-Assets in der Beschreibung unten herunterladen . Falls ihr gerade bei diesem Projekt mitmacht, ist Teil eins auch in der Beschreibung verlinkt. Ich habe die RhinoMorphPNG-Datei in meinen Projektordner gelegt. Ich füge es hier in meine Index-HTML-Datei ein. Und ich gebe ihm eine Rhinomorph-ID. Ich verwende seine ID, um es mit CSS zu verstecken , weil wir es nur mit Javascript auf Leinwand zeichnen wollen mit Javascript Später haben wir bereits etwas über Unterklassen in Java Script gelernt über Unterklassen in Java Script Wir wissen, dass der Feind die übergeordnete Klasse ist, auch Superklasse genannt wird Diese Klasse enthält alle Eigenschaften und Methoden, die allen verschiedenen Gegnertypen gemeinsam sind. Dann verwenden wir das spezielle Extense-Schlüsselwort create Beetle Morph child class, auch Unterklasse genannt Diese Klasse enthält nur Eigenschaften und Methoden, die nur für einen bestimmten Feindtyp Wenn wir zum Beispiel einen Beetle-Morph-Gegner mit einem Projektil treffen , lösen wir eine Treffermethode aus, aber das Javascript wird diese Methode in dieser Unterklasse nicht finden Das Schlüsselwort extends richtet standardmäßige JavaScript-Prototypenkette hinter den die standardmäßige JavaScript-Prototypenkette hinter den Kulissen ein. Javascript weiß, dass, wenn es eine Methode in der Unterklasse nicht finden kann, es automatisch in der Oberklasse danach sucht. In der übergeordneten Klasse folgt es der Prototypenkette, folgt es der Prototypenkette bis es die Methode findet Diese spezielle Treffermethode finden Sie hier . Wir haben die gesamte Logik im Griff und wir verstehen wie Eltern- und Unterklassen in Java-Skript funktionieren Lasst uns dieses Wissen nutzen, um einen anderen Feindtyp mit einzigartigen Eigenschaften und Grafiken zu implementieren einen anderen Feindtyp mit einzigartigen Eigenschaften und Grafiken Ich kopiere diesen Codeblock und werde ihn in Rhinomorph umbenennen Wir verweisen mit dieser Punktbildeigenschaft auf das neue Rhinomorphs-Triadsheet, das neue Rhinomorphs-Triadsheet, das wir gerade in die Index-HTML-Datei aufgenommen haben. Diese Tabelle hat 012345 horizontale Rahmen, maximal fünf Frames, Frame Y bleibt unverändert, weil diese Tabelle auch vier Zeilen hat, genau wie Beetle diese Tabelle Morph. Rhino Morph wird vier Leben haben. Wir müssen es mehrmals treffen, um es zu zerstören. Eine wichtige Sache hier ist das Eigentum von Max Lives. Es entsteht, wenn wir dieses feindliche Objekt erschaffen und es gilt immer. Und es merkt sich den Anfangswert dieses Punktes auch wenn das Spiel läuft und der feindliche Rhinomorph aktuell zum Beispiel nur zwei Leben hat , weil wir ihn schon einmal getroffen Diese Eigenschaft, die maximale Anzahl an Lebenspunkten hat, gilt immer. Und denken Sie an den Anfangswert von vier , diesen Maximalwert, wir werden das alles in einer Minute benötigen. Also versuche bitte, dich daran zu erinnern. Okay, wir haben also eine neue Unterklasse. Wie fügen wir diesen neuen Feindtyp dem Spiel hinzu? Ich gehe hier runter zu unserer Custom-Wave-Klasse. Diese Klasse erstellt eine Gruppe von Feinden und organisiert sie in einer gitterartigen Formation hier unten in der Erstellungsmethode für die Wellenklasse Zuerst werde ich einfach überprüfen, ob alles funktioniert, indem ich Beetle Morph durch Rhinomorph ersetze Schön, wir können Rhinomorp sehen, was passiert, wenn ich es treffe? Wir haben ihm vier Leben gegeben, also muss ich ihn mehrmals treffen Und wenn wir all seine Leben aufgebraucht haben, werden die restlichen Animationsframes abgespielt Derzeit verwendet dieser Feindtyp dieselbe Logik wie der erste einfache Feind Wir schlagen ihn immer wieder an, bis wir ihm das Leben genommen haben. Und dann wird der Rest der Tabelle abgespielt. Ich möchte, dass die Tabelle jedes Mal, wenn wir sie treffen, die Frames wechselt, um zu zeigen, welchen allmählichen Schaden wir ihr durch unsere Hochfrequenzlaser Um das zu tun, müssen wir noch etwas über die Erweiterung von Javascript-Klassen lernen noch etwas über die Erweiterung von Javascript-Klassen Das sind meine Unterklassen und beide erweitern diese feindliche Oberklasse, wenn wir einen Feind treffen, diese Methode ab Zeile hundert und 27. Ich möchte , dass die Dinge die gleichen bleiben wie für die Käfer-Morph-Klasse, aber ich möchte hier etwas mehr Logik für die Rhinomorph-Klasse hinzufügen hier etwas mehr Logik für Eine Sache, die ich tun kann, ist, diese Methode zu kopieren und sie hier auf Rhinomorph hinzuzufügen Auf diese Weise findet Javascript, wenn diese Methode auf Rhinomorph ausgelöst wird, diese Methode direkt in dieser Klasse und sucht nicht mehr in der feindlichen danach Im Grunde überschreibe ich diese Methode für Beetle Morph. Es wird immer noch so bleiben wie zuvor. Für diese Unterklasse gibt es keine Treffermethode . Javascript wird sich weiterhin auf diese ursprüngliche Treffermethode für die übergeordnete feindliche Klasse beziehen auf diese ursprüngliche Treffermethode für die übergeordnete feindliche Klasse Für Rhinomorph haben wir Vorrang. Ursprüngliche Treffermethode hier in Zeile 154. Jetzt kann ich einfach jede zusätzliche Logik in die Hit-Methode der Rhinomorph-Klasse einfügen jede zusätzliche Logik in Hit-Methode der Rhinomorph-Klasse Diese Logik gilt nur für diesen speziellen Feindtyp. Ich möchte die Frame-Nummer auf Rhinomorphspridesheet irgendwie an den aktuellen Wert der feindlichen Leben anhängen und verbinden Rhinomorphspridesheet . Wenn wir auf Rhinomorph klicken, setzen wir Frame auf die maximale Anzahl an Lebenspunkten abzüglich aktuellen Lebenspunkte Max lebt mit vier. Wie wir wissen, haben wir es einmal getroffen. Vier minus drei ist eins. Frame X ist eins. Wir haben es wieder getroffen. Sein derzeitiges Leben ist 24, minus zwei ist zwei. Frame X ist zwei. Wir haben es wieder getroffen. Wir leben derzeit bei 14, minus eins ist drei. Frame X ist drei. Ich hoffe, es macht Sinn. Ich teste es. Ich habe es einmal getroffen. Wir sehen diesen Rahmen. Ich habe es wieder getroffen. Wir sehen diesen Rahmen, ich habe ihn wieder getroffen. Wir sehen diesen Rahmen. Ich habe es noch einmal getroffen. Und der Rest der Tabelle wird perfekt funktionieren. Ich habe das Spiel eine Weile gespielt und Sie können sehen, dass wir zufällig vier Varianten der feindlichen Klasse Rhino Morph bekommen zufällig vier Varianten der feindlichen Klasse Rhino Morph Und sie werden alle Schritt für Schritt beschädigt. wir die Tabellenkalkulationsrahmen austauschen, indem wir sie treffen, werde ich vielleicht später andere Waffentypen einsetzen. Und dieser Schadenswert ist möglicherweise nicht immer eine Ganzzahl, er ist möglicherweise nicht immer ein Schaden pro Treffer. Dieser Schadenswert hat Dezimalstellen. Es würde tatsächlich diese Logik durchbrechen , Sprite-Frames auszutauschen Um das zu verhindern, werde ich diesen Punkt in den mathematischen Punktboden packen , um ihn auf die nächste niedrigere Ganzzahl abzurunden Und jetzt sind wir sicher, auch wenn wir eine Art Laserwaffe haben , die pro Treffer Schaden in kleineren oder größeren Werten verursacht. Selbst in diesem Fall funktioniert die Frame-Swapping-Logik hier immer noch Jetzt hatte ich Spaß beim Erstellen der Sprite Sheets und habe versucht, mir vier verschiedene Methoden auszudenken , wie feindliche Granaten knacken können, wenn wir sie beschädigen Schritt für Schritt haben wir diesen neuen Feindtyp eingeführt, und wahrscheinlich war er einfacher, als ihr es euch vorgestellt habt Ich würde jetzt gerne Ihre Designideen hören. Kannst du dir einen anderen Feindtyp mit einem einzigartigen Merkmal ausdenken, wenn du willst? Du kannst diesem Feindtyp auch einen Namen geben. Wenn dir etwas wirklich Gutes einfällt, entwerfe ich es vielleicht, implementiere es und ich werde es als Bonus-Episode in diesem Kurs veröffentlichen . Jetzt möchte ich, dass die Wellen der Feinde aus einigen Käfer-Morphen und einigen Nashorn-Morphen Und ich möchte kontrollieren können, wie viel Prozent der Welle aus welchem Feindtyp bestehen Um das zu tun, gehe ich hier in unsere benutzerdefinierte Wellenklasse. Hier auf der Linie 193 übertragen wir nur Rhino-Morphs Im Moment werde ich diesen Zufallstrick mit dieser Methode noch einmal machen. Wir haben es bereits verwendet, um nach dem Zufallsprinzip zu entscheiden, ob sich die Welle zuerst nach links oder nach rechts bewegt , wenn sie erzeugt wird zu entscheiden, ob sich die Welle zuerst nach links oder nach rechts bewegt . Auch hier gibt die Methode random, die alleine aufgerufen wird, einen Zufallswert 0-1 zurück . Die vollständige Definition ist, dass Method Random eine Pseudozufallszahl als Fließkommazahl zurückgibt , die größer oder gleich Null und kleiner als eins ist Wenn ich einen Ausdruck erstelle, der besagt, dass die Methode random kleiner als 0,5 ist , wird das in etwa 50% der Fälle zutreffen 50% der Welle werden aus Nashorn-Morphen bestehen, das heißt, die anderen 50% werden Käfer-Morphe erzeugen Wenn ich das Spiel jetzt spiele, kriegen wir beide Feindtypen und sie sollten irgendwie verteilt werden , 50, 50. Natürlich ist es zufällig, wir werden immer mehr von dem einen oder anderen bekommen, aber insgesamt wird es den Durchschnitt ergeben, sodass wir fast gleichmäßig verteilt sind. Nehmen wir an, ich möchte, dass nur 20% der Gegner Nashorn-Morphs und die restlichen 80% Käfer-Morphe sind Ich würde hier 0,2 verwenden. Dies gibt uns eine sehr einfache Möglichkeit, die Verteilung der Gegnertypen in unserem Spiel zu kontrollieren. Das Hinzufügen eines Feindtyps, etwas anders funktioniert, macht es unsere Spieler etwas interessanter, unser Spiel für unsere Spieler etwas interessanter, unser Spiel zu erkunden. Was sollten wir noch tun, um dieses Spiel noch weiter zu verbessern? Welche anderen Funktionen sollten wir implementieren? Geben Sie mir unten Ihre kreativen Ideen und helfen Sie mir, sie zu entwerfen. Wir sehen uns im nächsten Teil. 17. Zusätzliche Funktionen: Boss-Kämpfe: Ich habe keine Daten über diese Kreatur. Ihre Hülle besteht aus reinem Tellurpolymer, das nicht gescannt werden kann. Es gibt keine Aufzeichnungen über diese Spezies. Niemand, der diese Kreatur trifft, überlebt, um die Geschichte zu erzählen. Ich versuche, mehr Daten zu bekommen. In der Zwischenzeit ist die einzige Strategie, die wir haben, rohe Feuerkraft Schlag es mit allem, was wir haben, bevor es uns trifft. Und versuchen wir, diese riesigen Tellurkrallen zu vermeiden. Irgendetwas sagt mir, dass sie unsere Schilde durchschneiden würden unsere Schilde durchschneiden Ja, jeder enge Kontakt führt sofort zu Schutzschilden, Ausfall, sodass wir unbedingt Abstand halten müssen. 18. Kurs: Wenn Sie benutzerdefinierten Code für ein solches Projekt schreiben, ist es am wichtigsten, immer zu wissen, welcher Code nur einmal ausgeführt wird. Welcher Codeblock wird ausgeführt, wenn ein bestimmtes Ereignis eintritt. Und welcher Code bei jeder Animationsschleife in unserem Spiel immer wieder läuft , die Feinde kommen in immer größeren Wellen. Was ist, wenn ich möchte, dass jede dritte Welle eine zufällige Bossbegegnung Und im Laufe des Spiels möchte ich, dass die Bosse immer mehr Lebenspunkte haben Schrittweise Erhöhung der Chal***ge für den Spieler. Sie sehen sich die erweiterte Vollversion an. Der vollständige Quellcode jeder Phase ist im Download-Bereich unten enthalten. Wenn Sie sich eine kostenlose Youtube-Version ansehen, vergessen Sie nicht, auf „Gefällt mir“ zu klicken und mir mitzuteilen , dass Sie mehr Projekte wie dieses haben möchten. Wie üblich importiere ich zunächst die Kunst-Assets. Sie können die BOSS-PNG-Datei im Abschnitt Ressourcen unten herunterladen . Platzieren Sie das Bild im Projektordner und verknüpfen Sie es wie folgt in der Index-HTML-Datei. Ich gebe ihm eine Vorstellung von Boss, wir verstecken es mit CSS. Wenn Sie sich die Tabelle ansehen, habe ich vier verschiedene Boss-Kreaturen erstellt. Die Tabelle ist wie folgt organisiert. Die ersten beiden Frames werden abgespielt , wenn wir den Boss mit einem Projektil treffen . Der Boss bewegt sich ein wenig, um auf einen Treffer zu reagieren Wenn wir all seine Trefferpunkte und sein ganzes Leben aufgebraucht haben, wird der Rest der Tabelle abgespielt Ich hoffe, dir gefallen die Pop-Animationen, die ich gemacht habe. Ich hatte ein bisschen Spaß damit. Hier unten auf 960 erstelle ich eine benutzerdefinierte Boss-Klasse. Constructor wird nur einmal ausgeführt, um ein Boss-Objekt zu erstellen. Jedes Mal, wenn wir diese Klasse mit dem neuen Schlüsselwort aufrufen, zeigen wir wie zuvor auf das Hauptspielobjekt Um Zugriff auf alle Eigenschaften und Methoden zu erhalten , die sich dort befinden, möchte ich, dass der Boss viel größer ist als normale Gegner. Breite und Höhe werden 200 mal 200 Pixel betragen. Ich möchte, dass der Boss genau in der Mitte des Bildschirms horizontal beginnt . Die Punktbreite dieses Spiels multipliziert 0,5 minus der Hälfte der Breite des Endgegners. Zunächst wird es hinter dem oberen Rand des sichtbaren Spielbereichs versteckt . Die vertikale Y-Koordinate wird minus Punkthöhe sein. Horizontale Geschwindigkeit, Geschwindigkeit x wird zufällig minus eins oder plus eins sein. Dazu verwenden wir einen ternären Operator, mathrandomische Ausdrücke auswertet Wenn Mathrandom in etwa 50% der Fälle weniger als 0,5 beträgt , bewegt sich der Boss nach links, andernfalls bewegt sich der andernfalls Geschwindigkeit Y wird Null sein. Anfänglich hat der Boss immer , sagen wir, zuerst zehn Leben. Der nächste Boss wird mehr Gesundheit haben. Jedes Mal, wenn wir eine neue Boss-Welle erstellen, erhöhen wir die Anzahl der Lebenspunkte . Ich werde ihr auch maximale Anzahl an Leben geben. Diese Eigenschaft merkt sich den anfänglichen Wert von Dis Dot Lives, sodass ich dem Spieler die entsprechende Anzahl an Punkten gewähren kann dem Spieler die entsprechende Anzahl an Punkten gewähren , wenn der Boss besiegt wird. Als läsion markiert, wird der Wert falsch sein. Dieses Punktbild zeigt zunächst auf das Spritesheet, das ich habe und das vier verschiedene Boss-Kreaturen enthält Frame X übernimmt die horizontale Sprite-Navigation. Wir werden bei Frame Null beginnen. Der Rahmen Y in der linken Spalte bestimmt also , welcher dieser vier Bosse für die vertikale Sprite-Navigation zuständig ist Jedes Mal, wenn wir einen neuen Boss erstellen, möchte ich zufällig einen dieser vier auswählen Frame Y ist ein zufälliger Wert von 0-4 Wir verwenden hier Matt Floor, um den Wert auf die nächste niedrigere Ganzzahl abzurunden die nächste niedrigere Ganzzahl abzurunden Das tun wir, weil es im Spritesheet keine Zeile 1.5 Zum Beispiel wollen wir nur ganze Zahlen. Dieser Ausdruck gibt uns nun entweder Null oder Eins, oder zwei oder drei Wir müssen den maximalen horizontalen Frame kennen, um zu überprüfen, ob die gesamte Explosionsanimation vollständig abgespielt wurde, und wir können das Boss-Objekt aus dem Spiel entfernen. Ich werde den maximalen Frame auf 11 setzen. Das erinnert mich daran, dass ich in der vorherigen Lektion vergessen habe , eine Sache zu tun. Hier unten in der New-Wave-Methode der Hauptspielklasse schieben wir jedes Mal, wenn die vorherige Welle von Feinden besiegt ist, ein neues Wellenobjekt in dieses Punktwellen-Array . Wenn ich diese Wellen zusammenführe und spiele, besiege ich Welle eins, Welle zwei, Welle drei Wir können in der Konsole sehen, dass das Didot-Wellen-Array endlos wächst und die alten Wellen einfach da bleiben, obwohl sie keine aktiven Gegner mehr enthalten Um das zu beheben, gehe ich hier zu unserer benutzerdefinierten Wellenklasse. Ich gebe ihr eine Eigenschaft namens Zum Löschen markiert. Anfänglich wird es so eingestellt, dass es Render-Methode der Wave-Klasse fällt. Wir wissen, dass diese Methode für jeden Animationsframe ausgeführt wird. Bewegt die Welle im Spielbereich und filtert zerstörte Gegner aus der Reihe verzerrter Gegner heraus Wenn der Spieler alle Gegner und Didot-Gegner vernichtet hat, ist ***gth Sind in dieser Welle keine Feinde mehr übrig. wir den Wert „Zum Löschen markiert“ auf „true“ setzen dieses Wellenobjekt markieren wir den Wert „Zum Löschen markiert“ auf „true“ setzen. Wenn dieses Wellenobjekt eine Reihe von Feinden hat , die keine Feinde enthält, bedeutet das, dass wir alle besiegt haben und die neue Welle erstellt wurde. Wir wollen diese alte Welle hier unten löschen , wo wir eine neue Welle erzeugen. Wir werden jedes Mal, wenn wir dem Spiel eine neue Welle hinzufügen, alte Wellen herausfiltern Mal, wenn wir dem Spiel eine neue Welle hinzufügen, Schauen Sie sich dieses Punktwellen-Array an und wir filtern es. diesem Punktwellen-Array dürfen nur die Elemente verbleiben, deren Eigenschaften zum Löschen markiert wurden In diesem Punktwellen-Array dürfen nur die Elemente verbleiben, deren Eigenschaften zum Löschen markiert wurden. Wenn für sie die Option „Zum Löschen markiert “ auf „true“ gesetzt wurde, werden sie entfernt. Jetzt spiele ich das Spiel und sehe immer nur zwei Wellen, die alte und die neue , die gerade erstellt wurde. Wenn ich das Consolo hierher bewege und das Spiel spiele, können wir tatsächlich sehen, dass dieses Punktwellen-Array nur ein aktuell aktives Wellenobjekt enthält Ich hoffe, das ergibt Sinn. Bei Fragen dazu können Sie einen Kommentar hinterlassen. Gehen wir zurück zur Boss-Klasse. Sie benötigt eine benutzerdefinierte Zeichenmethode um den Boss im Spiel zu zeichnen, und eine aktualisierte Methode, um ihn zu bewegen , um sein Verhalten zu definieren. Wir haben ein Spritesheet mit mehreren Frames, zwischen denen wir wechseln möchten Das heißt, wir benötigen die längste Version der Draw-Image-Methode, eine Version, die neun Argumente benötigt Das Bild, das wir zeichnen wollen, Quelle X , Quelle Y, Quellbreite und Quellhöhe eines Bereichs. Wir möchten wie gewohnt aus dem Quellbild das Ziel X, das Ziel Y, die Zielbreite und die Zielhöhe ausschneiden Ziel X, das Ziel Y, die Zielbreite . Um zu definieren, wo auf der Ziel-Leinwand wir den ausgeschnittenen Rahmen platzieren möchten. Ich möchte den oberen linken Rahmen von der Koordinate 002 mit der Höhe ausschneiden . Ich möchte es auf der Leinwand an Position x Punkt y Punkthöhe platzieren . Auf diese Weise fängt der Boss komplett versteckt hinter dem oberen Rand der Leinwand an. Ich möchte, dass es sichtbar wird, bis es vollständig sichtbar ist. Wenn dieses Y kleiner als Null ist, entspricht dieser Punkt Y plus zehn Pixeln pro Animationsframe Das ist eine gute Grundkonfiguration für den Chef. Lassen Sie uns es jetzt tatsächlich zeichnen. Wir wissen also, womit wir hier unten in der Hauptspielklasse arbeiten . Wir wissen, dass der gesamte Code, den wir in einen Konstruktor einfügen , automatisch ausgeführt wird , wenn wir eine Instanz dieser Klasse mit dem neuen Schlüsselwort erstellen eine Instanz dieser Klasse mit dem neuen Schlüsselwort Ich schiebe automatisch Welle Nummer eins, die erste Gruppe von Feinden, ins Spiel Sobald wir das Spiel erstellt haben, lösche ich es vorerst Ich werde ein Boss-Array erstellen, das alle derzeit aktiven Bosse aufnehmen wird Wir werden zu dem Zeitpunkt wahrscheinlich nur einen aktiven Boss haben, aber wir könnten problemlos mehrere haben, wenn du das Spiel für unsere Spieler noch schwieriger machen willst . Hier unten haben wir eine benutzerdefinierte Neustartmethode , die ausgeführt wird, wenn wir ein Spiel beendet Und wenn wir hier den Buchstaben R drücken, können Sie sehen, dass ich gerade alles auf die Standardwerte zurücksetze. Das Problem ist, dass, wenn ich während der Entwicklung des Spiels irgendwelche Änderungen daran vornehmen möchte , ich die gleichen Änderungen innerhalb des Spielklassenkonstruktors an diesen Eigenschaften vornehmen muss des Spielklassenkonstruktors an diesen Eigenschaften Und auch hier unten in der Neustart-Methode , um sicherzustellen, dass sie mit der Zeit übereinstimmt, zu der das Spiel zum ersten Mal gestartet wird und wann es neu Ich könnte das vermeiden , indem ich die Restart-Methode vom Konstruktor aus aufrufe, was bedeutet, dass sie alle Werte zurücksetzt, sobald das Spiel startet alle Werte zurücksetzt, sobald das Spiel Vielleicht könnten wir diese Methode auch so etwas wie initialize nennen diese Methode auch so etwas wie initialize So oder so, jetzt, wo ich das getan habe, sind diese Anfangswerte von 11 hier nicht wirklich wichtig Denn hier, noch bevor das Spiel beginnt, setzt Neustart-Methode die Spalten und Zeilen auf zwei, weil das die Werte sind, die wir hier unten verwenden, wenn wir das Spiel neu starten. Aufräumen müssen wir auch das Boss-Array auf ein leeres Array wie dieses setzen . Da wir nun wissen, dass die Neustart-Methode sofort ausgeführt wird, können wir dort beliebigen Code einfügen, den wir ausführen möchten , wenn das Spiel startet und wenn das Spiel neu startet Ich schiebe hier schon automatisch die erste feindliche Welle Lassen Sie uns auch unsere neue benutzerdefinierte Boss-Klasse verwenden, um ein neues Boss-Objekt in das Boss-Array zu verschieben. 19. Boss-Bewegung: Ich muss hier in die Render-Methode gehen und wir müssen das Boss-Array zum Zeichnen und Aktualisieren aufrufen. für jedes Boss-Objekt Rufen Sie für jedes Boss-Objekt innerhalb des Boss-Arrays seine Draw-Methode auf. Wir wissen, dass es Kontext erwartet und auch sein Update aufruft. Toll, wir haben beide Feinde gleichzeitig. Jetzt wissen wir, dass wir hier mit der Restart-Methode weitermachen können . Und vielleicht wollen wir das Spiel mit einer Welle von Feinden beginnen . Oder vielleicht wollen wir mit einer Boss-Welle beginnen. Was ich jetzt tun werde. Da wir unsere Boss-Logik entwickeln und testen, kann ich diesen Wert so ändern, dass der Boss sichtbar wird. Schneller oder langsamer. Sie können Ihren eigenen Geschwindigkeitswert festlegen. Ich gehe hier von vier Pixeln pro Animationsframe aus. Ich möchte dem Boss das gleiche Bewegungsmuster für links und rechts geben , das wir der Welle von Feinden gegeben haben. Wenn die horizontale X-Koordinate des Bosses kleiner als Null ist, also wenn der Boss den linken Rand des Spielbereichs berührt oder wenn die horizontale Position des Bosses größer ist als die Spielbreite minus die Breite des Bosses. Das bedeutet, dass der Boss den rechten Rand des Spielbereichs berührt. In beiden Fällen tauschen wir den Wert für Geschwindigkeit x von Zeile 167 auf die entgegengesetzte Linie, wodurch der Boss nach links und rechts springt Jetzt muss ich nur diesen Punkt x für jeden Animationsframe um Geschwindigkeit x erhöhen diesen Punkt x für jeden Animationsframe um Geschwindigkeit x Ich werde auch Punkt Y um Geschwindigkeit Y erhöhen. Nett. . Ich werde auch Punkt Y um Geschwindigkeit Y erhöhen. Nett. Wir haben einen Boss , der in der Ansicht schwebt und nach links und rechts abprallt Ich möchte ihm auch die Geschwindigkeit y von 200 geben, wenn er einen der beiden Kanten berührt Aber das bedeutet, dass der Chef einfach so vorwärts eilen wird. Ich möchte, dass die Eigenschaft Geschwindigkeit Y nur für einen Animationsframe erhöht wird, um einen schnellen Sprung zu ermöglichen. Dann setzen wir sie sofort wieder auf Null. All dieser Bewegungskram ist genau dieselbe Logik, die wir zuvor für Wellenbewegungen verwendet haben. Jetzt funktioniert es. Ich mache den vertikalen Sprung gleich der Hälfte der Körpergröße des Chefs. So wie das. Wir können immer nur diesen Rahmen sehen, weil ich von 00 auf Breite und Höhe falle. Wenn ich stattdessen eine zufällige Zeile haben möchte, verwende ich dafür das Quell-Y-Argument Null mal Höhe ergibt diesen Frame einmal, Höhe ist dieser Frame zweimal Höhe, Höhe ist dieser Frame zweimal Höhe dann erhalten wir diese Version von Mantis dreimal so hoch sind, erhalten wir diesen tiefen Raum, tiefer, listiger Wir haben die Eigenschaft Frame Y bereits für diesen Zweck in Zeile 174 vorbereitet . Lassen Sie uns sie hier verwenden Source kümmert sich um die horizontale Sprite-Navigation. Es wird Frame X von 973 mal Breite von 963 sein. Wenn Frame x beispielsweise fünf ist, erhalten wir diesen Wenn Sie in diesem Kurs so weit gekommen sind, Sie diesen Teil meiner Meinung nach bereits verstanden. Also lass uns einfach weitermachen, wann immer wir das Spiel neu laden oder einen neuen Boss mit unserer benutzerdefinierten Boss-Klasse erstellen , wir werden zufällig eine dieser vier Boss-Kreaturen perfekt machen dieser vier Boss-Kreaturen perfekt Der erste Boss wird einfach sein, er wird nur zehn Leben haben Lass uns tatsächlich Boss-Leben zeichnen. Wir können wählen, ob wir es als Gesundheitsbalken oder als Zahl machen wollen. Ich werde diesen Code zwischen Safe und Restore umschließen, um sicherzustellen, dass alle Änderungen am Canvas-Status, die ich hier vornehme, nur auf diesen Bereich beschränkt sind . Ich rufe die integrierte Fülltext-Methode auf, die mindestens drei Argumente akzeptiert. Der Text, für den wir diesen Punkt zeichnen wollen, lebt von der Zeile 169 und den X und Y eines Ankerpunkts , in Bezug auf den der Text angezeigt wird. Fangen wir mit X und Y des Bosses an, was, wie wir wissen, die obere linke Ecke des Chefs ist. Klicken Sie auf das rechteckige Feld. Ich möchte den Text eigentlich etwas nach unten verschieben, vielleicht plus 50 Pixel vertikal und horizontal. Versuchen wir es mit der Breite von beiden mal 0,5. Dabei wird der Textankerpunkt genau in der Mitte platziert, aber standardmäßig ist der Text linksbündig ausgerichtet. Von dort aus verwende ich die Eigenschaft Text Align und setze sie auf die Mitte, weil wir diesen Codeblock zwischen Safe und Restore umschlossen haben . Das heißt, es wirkt sich nur auf das Leben des Chefs aus und nicht auf irgendeinen anderen Text, den wir auf Leinwand zeichnen , um den weißen Text vor dem Hintergrund besser sichtbar zu machen . Ich kann ihm auch einen Schatten von Satz X mit drei Pixeln geben, Schatten von Satz Y auch mit drei Pixeln. Und die Schattenfarbe kann schwarz sein. Nun, da wir beide Leben sehen, richten wir die Kollisionserkennung zwischen beiden und Projektilen in der Aktualisierungsmethode Bei beiden Klassen nehme ich eine Projektil-Abschleppanlage , die sich auf der Hauptspielklasse befindet Ich rufe jede Methode dazu auf. Für jedes Projektil in der Projektil-Pull-Reihe werde ich Folgendes überprüfen Wenn die Kollisionsmethode zwischen diesem Boss-Objekt und dem Projektil, mit dem wir gerade arbeiten , für jede Methode aktiviert ist, wenn die Kollisionsprüfung in den rechteckigen Trefferfeldern gleichzeitig den Wert true zurückgibt ( Doppel-Prozent-Operator Ich möchte nur überprüfen, ob das Projektil aktiv ist, weil Projektile nur dann aus einem wiederverwendbaren Objektpool stammen, nur dann aus einem wiederverwendbaren Objektpool wenn die freie Eigenschaft auf dem Projektil falsch ist, was bedeutet, dass das Projektil Es ist im Spiel aktiv. Noch einmal überprüfen, ob die Leben des Chefs mehr als Null sind. Denn wenn wir beide Leben aufgebraucht und die Explosionsanimationsframes abgespielt werden, möchte ich, dass die neuen Projektile das ignorieren und sie durchfliegen, statt mit ihnen zu kollidieren Erst wenn alle drei Prüfungen zutreffen, kollidieren beide und das Projektil mit einem aktiven Projektil und Wir reduzieren die Leben des Bosses um eins. Wir werden auch die Reset-Methode für das Projektil aufrufen, was bedeutet, dass das Projektilobjekt inaktiv wird und wieder in den Objektpool zurückkehrt. Ich teste es Ja, wenn ich den Boss treffe, rauben wir ihm Leben. Ja, das läuft wirklich gut. Anstatt Menschenleben auf diese Weise direkt zu reduzieren, werde ich eine Treffermethode entwickeln, Schadenswert als Argument verwendet wird. Und sie wird die Zahl der Menschenleben durch diesen Schaden verringern. Ich möchte, dass sich der Boss bewegt und animiert und auf einen Schlag reagiert Wir wechseln nur dann zwischen den Frames 0.1, wenn es mehrere Leben gibt Bei einem Treffer setzen wir Frame X auf einen Frame, der diesen Frame anzeigt. Jetzt verwende ich diese Methode hier und gebe ihr den Schaden von eins weiter. Okay, das funktioniert immer noch. Wir haben den Boss getroffen und Bild für Bild eins getauscht. diesem Bild möchte ich jetzt, dass es zum ursprünglichen Bild Null zurückkehrt, aber wir wollen warten, bis Sprite-Update-Flag wahr ist, um die Animation zu verlangsamen So wie wir es mit Feinden gemacht haben. Außerdem kehren wir nur dann zu diesem Frame zurück, wenn die Leben des Bosses mehr als Null sind, denn wenn sie auf Null gehen, wollen wir die gesamte Animationsrolle spielen. Nett. Wenn wir den Boss treffen, solange der Boss mehr als ein Leben hat, wechseln wir, solange der Boss mehr als ein Leben hat, zwischen Frame 0.1 hin und her, damit der Boss wackelt und reagiert, wenn er getroffen wird Wenn ich nachlade und eine andere Boss-Kreatur erhalte, können wir sehen, wie sie sich auf eine andere Art bewegt Ich habe jedem Boss eine eigene Möglichkeit gegeben , auf solche Treffer zu reagieren Es hilft, dem Spieler mehr visuelles Feedback zu geben , damit sich das Spiel lebendiger anfühlt. Ich denke, wir verwenden einfache statische Bilder, aber sie reagieren auf Eingaben und Aktionen. Spritesheets haben nur eine geringe Anzahl von Frames, aber alles ist reaktiv Dies ist eine der leistungsfreundlichen Methoden, um Ihre Spiele zu animieren Ich werde hier mehr als Null sagen. Wenn beide Leben Null erreichen, wollen wir den Rest des Spritesheets animieren, wo der Boss aufgeblasen und zerstört wird Wenn der Boss weniger als eins Leben hat. Wenn das Sprite-Update-Flag wahr ist, werden wir damit beginnen, die Anzahl der Frame-Eier langsam um eins zu erhöhen horizontal durch das Spritesheet bewegt , das sich horizontal durch das Spritesheet bewegt. Lass es uns testen Ich habe den Boss zehnmal geschlagen. Nett. Es spielt die Animation ab. Sie können sehen, dass der Chef immer noch da ist. Es zeigt nur einen leeren Sprite-Frame. Eigentlich muss ich nicht null Leben zeichnen. Wir zeichnen nur Leben, wenn es mehr als null Leben gibt. Jetzt vernichten wir den Boss. Wir zeichnen ihm nicht mehr Leben und Projektile kollidieren nicht mehr mit ihm, aber der unsichtbare Boss ist immer noch auf dem Bildschirm bewegen, müssen wir ihn mit der Eigenschaft Zum Löschen markiert entfernen , die wir hier in Zeile 171 definiert haben Wenn wir horizontale Frames aktualisieren, ist Frame X mehr als Max Frame. Wir haben den maximalen Frame hier auf 975 auf 11 gesetzt. Zu diesem Zeitpunkt wissen wir, dass der Boss zerstört wurde und dass alle Explosionsbilder angezeigt wurden Wir wissen, dass es zum jetzigen Zeitpunkt sicher ist, das Boss-Objekt vollständig aus dem Spiel zu entfernen . Ich setze die Eigenschaft „Zum Löschen markiert“ auf „true“. Ich werde auch die Spielpunktzahl um diese maximale Anzahl an Leben erhöhen , die der ursprünglichen Anzahl von Leben entspricht. Der Boss, in diesem Fall zehn Bosse mit zehn Leben, gibt zehn Punktepunkte. Wenn wir besiegt sind, setzen wir „ Zum Löschen markiert“ hier auf „Wahr“. Und ich gehe zur Haupt-Render-Methode und filtere das Boss-Array heraus. Diese Codezeile wird für jeden Animationsframe ausgeführt, was eine ziemliche Verschwendung ist. Um ehrlich zu sein, könnten wir es einfach woanders platzieren, zum Beispiel in die New-Wave-Methode, und alle Boss-Objekte nur regelmäßig reinigen, wir müssen es nicht für jeden Animationsframe überprüfen. Wir haben bereits behandelt, wie Filtermethode funktioniert und wie sie alle Objekte aus dem Array herausfiltert , deren Eigenschaft zum Löschen markiert auf true gesetzt ist. Mal sehen, ob es von Consolo funktioniert. In diesem Boss-Array haben wir einen Boss. In diesem Array verbrauchen wir all seine Leben. Die Explosionsanimation wird abgespielt. Jetzt kann ich in der Konsole sehen , dass das Boss-Array leer ist. Großartig, ich lösche das Consolo. 20. Boss vs. Spieler-Kollision: Jetzt müssen wir entscheiden, was passiert, wenn der Boss ganz unten ist und mit dem Spieler kollidiert Ich überprüfe, ob die benutzerdefinierte Kollisionsmethode zwischen diesem Boss-Objekt und dem Spielerobjekt gleichzeitig den Wert true zurückgibt Führe diesen Code nur aus, wenn der Boss mehr als null Leben hat. Weil es ein Szenario geben könnte, in dem Spieler mit Explosionsanimationsframes kollidiert Was in Ordnung wäre, weil der Boss bereits zerstört wurde In diesem Fall wollen wir keine Kollision registrieren. Drinnen habe ich das Spiel auf True umgestellt. Wir müssen den Boss vernichten, bevor er den Spieler berührt. Andernfalls durchdringen seine Klauen direkt unsere Schilde. Der Boss ist sehr gefährlich. Gleichzeitig kann ich die Leben des Bosses auf Null setzen, um die Explosionsanimationssequenz auszulösen , wenn ich möchte. Nehmen wir an, unsere Schilde werden auch den Boss zerstören. Ich weiß es nicht. Denk dir deine eigene Geschichte aus. Lass es uns testen. Ich werde das beschleunigen. Wenn der Boss den Spieler berührt, ist das Spiel vorbei und der Boss explodiert Das hat perfekt funktioniert. Eine weitere schlechte Bedingung wäre, wenn der Boss den ganzen Weg hierher kommt und den unteren Bildschirmrand berührt. Wenn die vertikale Position des Bosses zuzüglich seiner Höhe größer ist als die Höhe des Spiels. bedeutet, dass der untere Rand des Endgegners den unteren Rand des sichtbaren Spielbereichs berührt. Wir haben das Spiel auf „True“ gesetzt. Wenn ich den Boss auf diese Weise zerstöre, möchte ich, dass das Spiel weitergeht und ich möchte, dass regelmäßig Wellen von Feinden kommen. Boss wurde hier zerstört. Wenn Game Over falsch ist, rufen wir unsere benutzerdefinierte New-Wave-Methode auf. Lass es uns testen. Ich zerstöre den Boss, wir bekommen eine Welle von Feinden. Ich zerstöre alle Feinde in der Welle, ich bekomme eine weitere größere Welle. Das ist schon ein ziemlich guter Game-Loop. Ist es nicht erledigt, wenn du es bis hierher verfolgst? Jedes Mal, wenn eine neue Welle kommt, erhöhen wir den Wert für die Wellenzahl und zeigen ihn an. Hier rufen wir eine neue Wellenmethode auf, die wir hier in Zeile 393 definiert haben . Jedes Mal ist die Anzahl der Wellen ohne Rest durch zwei teilbar, zum Beispiel vier Wellen, 02468 usw. Wir werden einen neuen Boss herausfordern, andernfalls werden wir eine regelmäßige, immer größere Welle von Feinden erschaffen immer größere Welle Ich muss hier vorsichtig mit den Klammern sein. Ich sage, dieses Boss-Array, schieb den neuen Boss. Ich übergebe das, um auf das Hauptspielobjekt zu zeigen , in dem wir uns gerade befinden. Ich möchte die Anzahl der Wellen hier nicht erhöhen, ich habe sie gelöscht. Stattdessen erhöhen wir die Wellenzahl hier innerhalb der New-Wave-Methode. Auf diese Weise erhöhen sowohl Boss- als auch feindliche Wellen den Wert der Wellenzahl. Lass es uns jetzt testen. Wir starten die erste Welle mit einem Boss, wie wir ihn in der Neustart-Methode definiert haben. Beim ersten Laden besiegen wir den Boss. Wellenzähler wird auf zwei erhöht, was durch zwei teilbar ist, wobei der Rest Null Dieser Block läuft und wir bekommen wieder einen Boss. Wir haben Wave is Three besiegt. Das ist falsch, sonst läuft die Zeile 396 und wir bekommen eine Welle von Feinden. Vier ist teilbar durch zwei mit einem Rest von Null. Jetzt haben wir einen Boss. Sie können ändern, wie oft Sie einen Boss haben möchten , indem Sie diesen Wert hier anpassen. Wenn du möchtest, dass es sich bei der ersten Welle bei Welle Null um Endgegner oder Feinde handelt, definieren wir hier die Methode für den internen Neustart. Wenn die Explosionsanimation abgespielt wird, möchte ich nicht mehr, dass sie von den Rändern abprallt , weil das seltsam aussieht Wenn die Explosionswiedergabe beginnt, lassen Sie sie einfach vom Bildschirm ablaufen, wenn sie sich in der Nähe der Kanten Eine weitere Bedingung hier, die besagt, dass , wenn dieser Punkt mehr als Null lebt , der reguläre linke und rechte Punkt nur dann in Bewegung springen , wenn der Boss die endgültige Explosion nicht animiert Jetzt nähern wir uns dem Rand und wenn ich auf den Boss schieße, prallt die Explosion nicht mehr Sie fließt einfach und geht weiter in die Richtung, in die sich der Boss bereits bewegt hat Ich finde, das sieht besser aus. Ich merke, dass, wenn wir eine Bosswelle besiegen, kein Bonus-Spieler am Leben bleibt, der sie repariert. Ich schneide diese Codezeile aus und füge sie hier ein. Mit einer New-Wave-Methode besiege ich einen Boss. Ja, ich kriege ein Bonus-Leben. Gut. Jedes Mal, wenn wir einen neuen Boss kreieren, möchte ich, dass er, sagen wir, fünf weitere Leben hat. Um das Spiel nach und nach immer kniffliger zu machen. Ich werde den Wert von Bossleben hier auf dem Hauptspielobjekt speichern hier auf dem Hauptspielobjekt Wir werden mit zehn Leben beginnen. Wenn wir neu starten, müssen wir auch zu zehn zurückkehren. Ich übergebe Boss Lives als zweites Argument an den Boss-Klassenkonstruktor für den Boss in der ersten Welle und dann für jeden anderen aufeinanderfolgenden Boss, den wir erstellen, werde ich sicherstellen, dass hier in Zeile 161 ein Wert erwartet wird Und wir verwenden es als Wert für dieses Lebens-Eigentum, wenn wir einen Boss besiegen und er zerstört wurde , bevor wir die nächste Welle ausrufen Wir erhöhen die Anzahl der Bossleben auf dem Hauptspielobjekt um, sagen wir, fünf, wodurch der nächste Boss ein bisschen kalter wird. Dieser Boss hat zehn Leben, ich zerstöre ihn. Der nächste Boss hat 15 Leben, die funktioniert haben. Wir haben Wellen von Feinden, die im Laufe des Spiels immer größer werden. Und wir haben Bosse mit immer mehr Gesundheit. Vielen Dank an alle , die die Videos kommentiert und mir bei der Gestaltung dieses Spiels geholfen haben Wenn ihr weitere Ideen habt und wollt, dass ich weitere Funktionen hinzufüge, lasst es mich in den Kommentaren wissen Wenn euch etwas wirklich Gutes einfällt, werde ich es machen und es als Bonusstunde veröffentlichen. Mehr Feindtypen, Superwaffen, größere Superbosse , die tatsächlich Projektile auf uns zurückschießen Was soll ich als Nächstes tun? Lass es mich wissen. Es macht mir mehr Spaß, wenn wir es zusammen entwerfen. Beim Spielen merke ich, dass der Boss schon einige Leben verliert, bevor er auf dem Bildschirm überhaupt vollständig sichtbar ist . Das liegt den Projektilen, die nahe am oberen Rand fliegen Nehmen wir an, ich möchte, dass es erst Kollision zwischen Boss und Projektilen kommt, nachdem der Boss bis zu seiner Startposition geschwommen zu seiner Startposition geschwommen Und es ist auf dem Bildschirm vollständig sichtbar, wenn die vertikale Position des Bosses mehr oder gleich Null ist Okay, ich bin damit zufrieden. Was denkst du? Wir sehen uns im nächsten Teil. 21. Zusätzliche Funktionen: Super-Waffen: Da wir unser Spiel mit Schwärmen gefährlicher Feinde gefüllt haben, brauchen wir einen besseren Weg, mit ihnen umzugehen Zeit für Superwaffen. Lass mich dir etwas zeigen. Ich habe genug Plutonium gesammelt, um unseren Reaktor aufzurüsten. Ich kann seine Energie in eine mächtige Lichtsäule bündeln und sie als Waffe einsetzen Klingt so, als ob es sehr heiß werden kann. Sehr schnell. Ja, unsere Sonnenschutzschilde werden uns eine erneuerbare Energiequelle bieten, aber achten Sie darauf, die Kanone nicht zu überhitzen Das Sicherheitsschloss deaktiviert das Ventil. Wenn wir die angegebene maximale Sicherheitstemperatur überschreiten, verwalten wir unsere Energie und vermeiden Überhitzung. Klingt einfach genug Der Laser kann mehrere Gegner gleichzeitig durchbrennen . Er kann die gesamte feindliche Welle innerhalb von Sekunden zerstören , wenn er richtig eingestellt ist. Fröhliches Jagen 22. 2 Laserkurse: In diesem Kurs zur Entwicklung von Java-Script-Spielen fügen wir Ressourcenmanagement mit einem wiederaufladbaren Energieriegel hinzu. Und wir werden zwei weitere Waffen einsetzen, einen Langstreckenlaser und einen Superlaser. Diese neuen Waffen werden sich grundlegend von unseren Basisangriffen unterscheiden, da sie alle Gegner in einer Kolonne vor ihnen treffen, da sie alle Gegner in einer Kolonne vor ihnen treffen, auch die ganz hinten. Es kann sehr effektiv sein, besonders später im Spiel, wenn wir immer größeren feindlichen Wellen gegenüberstehen. Wir werden die übergeordnete Laserklasse haben, die die wichtigsten Eigenschaften und Methoden der beiden Laserwaffen definiert . Ich werde sie auf zwei Unterklassen ausweiten, kleine Laser und große Laser Ich mache das, weil jeder Laser eine andere Größe hat und unterschiedlichen Schaden anrichtet Diese spezifischen Dinge werden für jede Unterklasse separat definiert Konstruktor benötigt Zugriff auf das Hauptspielobjekt , damit wir Zugriff auf alle wichtigen Methoden und Eigenschaften haben, die wir zuvor der Spielklasse zugewiesen Aus der Laserklasse heraus zeichnet und aktualisiert die Rendermethode die Laserwaffe Es wird Kontext als Argument benötigen. Wir werden den Konstruktor auf beide Unterklassen erweitern. Lassen Sie uns einfach zuerst alles mit einer kleinen Laser-Subklasse machen , um es einfach zu halten Wenn wir eine Instanz von Small Laser erstellen, möchten wir den gesamten Code innerhalb des Konstruktors der übergeordneten Klasse ausführen innerhalb des Konstruktors der übergeordneten Klasse Wir wissen, dass die übergeordnete Klasse auch als Superklasse bezeichnet werden kann Wir nennen den Superklassenkonstruktor so. Und wir wissen, dass es einen Verweis auf das Spielobjekt erwartet. Ich gebe es hier so weiter. Es wird hier erwartet. Und es wird in dieses Punktspiel-Eigentum umgewandelt. Erst nachdem wir den Code im Superclass-Konstruktor ausgeführt haben, können wir diesen Punkt hier verwenden und Eigenschaften definieren , die nur für kleine Laserunterklassen spezifisch Das machen wir in einer Minute. Ich möchte es auch verlängern, ich möchte es diesmal nicht überschreiben. Ich möchte, dass der Code aus Zeile fünf tatsächlich ausgeführt wird. Dieser Codeblock enthält Logik, die für die Unterklassen Small Laser und Big Laser gemeinsam genutzt Unterklassen Small Laser und Big Laser Danach werde ich ein wenig Code hinzufügen, der nur für die Unterklasse Small Laser spezifisch ist Hier. Die Syntax ist ein bisschen anders. Es ist so. Hier rufe ich Superclass Constructor Hier rufe ich eine Methode auf, die in der Superklasse, in der Elternklasse, sitzt der Elternklasse, sitzt Ich hoffe es macht Sinn. Constructor ist eine spezielle Methode. Deshalb ist die Syntax hier anders. In diesen beiden Fällen, obwohl beide nur eine Methode für die übergeordnete Klasse aufrufen, gibt es einen Grund, warum ich die Render-Methode für die Unterklasse verwende, aber darauf kommen wir später zurück Laser benötigt die Breiten- und Höheneigenschaften x und y , damit Javascript genau weiß, wo er gezeichnet werden muss Außerdem werden wir die Kollisionserkennung zwischen dem Laserstrahl und den Feinden und Bossen überprüfen dem Laserstrahl und den Feinden und Bossen Wir wissen, dass alle Objekte, die an der Kollisionserkennung beteiligt sind , die Eigenschaften XY-Breite und Höhe benötigen X, Y und Höhe können in der übergeordneten Klasse bleiben, aber jeder Lasertyp hat eine andere Breite. Ich muss es hier der Unterklasse zuordnen. Wir wollen, dass der große Superlaser viel breiter ist als der kleine Laser, der kleine Versuchen wir es mit fünf Pixeln. Das Spritesheet des Spielers wird animiert. Mit diesen Waffen haben wir in einer Minute die gleiche Breite wie das Raumschiff Sprite Somit bewegt sich die horizontale Koordinate des Lasers mit dem Spieler Wenn sich der Spieler nach links und rechts bewegt. Anfangs habe ich es auf Null gesetzt. vertikale Y-Koordinate auf beiden Lasern ist immer Null, da ich möchte, dass das Rechteck des Laserstrahls am oberen Bildschirmrand beginnt. Die Höhe des Lasers wird ebenfalls immer dieselbe sein. Ich möchte, dass der Laserstrahl von hier nach irgendwo in der Nähe kommt . Ich schätze, ab Koordinate Null ergibt die Höhe des Laserstrahls diese Punkthöhe minus, sagen wir , 50 Pixel, um diesen Bereich abzudecken. Wir wissen, dass der gesamte Code im Konstruktor nur einmal ausgeführt wird Zu dem Zeitpunkt, an dem wir eine Instanz der Klasse erstellen, wird die Verwendung der neuen Schlüsselwort-Render-Methode Verwendung der neuen Schlüsselwort-Render-Methode ganz anders sein Der Code in der Render-Methode wird für jeden Animationsframe immer wieder ausgeführt. müssen wir berücksichtigen , wenn wir entscheiden, wo die Logik platziert werden soll. Wenn der Laser aktiv ist, möchte ich, dass seine horizontale Koordinate ständig aktualisiert wird, damit sie der Position des Spielers entspricht. Fangen wir einfach damit an und schauen, was wir bekommen. Jetzt werde ich den Laserstrahl als einfaches Rechteck zeichnen. Ich werde ihm zwei Farben geben. Ich werde diesen Code-Block sicher verpacken und integrierten Leinwandmethoden wiederherstellen, um sicherzustellen , dass sich diese Farben im Füllstil nur auf den Laserstrahl auswirken und nicht auf die anderen auf der Leinwand gezeichneten Formen. Ich sagte, es ist die integrierte Methode „ Füllstil zu Gold“. Wir zeichnen den Laser. Ich übergebe es einfach x, y, w von hier und in der Höhe. Wenn das alles keinen Sinn ergibt, hinterlasse einen Kommentar und wir können darüber sprechen. Ich hoffe, es ist alles klar. Wir haben die Basisversion des kleinen Lasers. Lass uns es zeichnen, damit wir es im Spiel sehen können und wir es optimieren und anpassen Ich werde hier eine Instanz einer kleinen Laserklasse erstellen. Es wird zu dem Zeitpunkt erstellt, an dem wir eine Instanz der Spielerklasse mit dem neuen Schlüsselwort erstellen . Ich hätte auch einen kleinen Laser auf die Hauptspielklasse setzen können, aber ich denke, das ist hier sinnvoller, da es so eng mit mehreren Eigenschaften des Spielers verknüpft sein wird so eng mit mehreren Eigenschaften des Spielers verknüpft sein . Hier überprüfen wir, ob die Taste gedrückt ist und wir tauschen die Spielerframes Ich werde einen LF-Block erstellen. Ich muss dabei sehr vorsichtig mit Klammern sein. Eine einzige falsche Klammer kann den gesamten Spielcode beschädigen. Wenn eine Taste gedrückt wird, führen wir den Basisangriff durch. Wenn zwei gedrückt wird, lösen wir einen kleinen Laser aus. Die Player-Tabelle wurde bereits von vorhin dafür vorbereitet Ich muss Frame X nur so auf zwei setzen. Wir befinden uns in einer Zeichenmethode, die für jeden Animationsframe immer wieder ausgeführt wird. Solange Taste zwei gedrückt ist, zeigen wir Bild X zwei an und wir rufen Rendern auf einem kleinen Laser auf, um es zu zeichnen und zu aktualisieren. Sobald wir die Taste loslassen, wird das Bild x auf Null gesetzt und wir hören auf, kleine Laser zu rendern. Wenn wir Nummer eins drücken, führen wir den Basisangriff aus. Wenn wir Nummer zwei drücken, zeichnen wir einen kleinen Laser. Er bewegt sich mit dem Spieler. Ich möchte es in die Mitte des Spielers verschieben. Ich passe den Code in Zeile neun an. Position des Lasers ist die Position des Spielers plus die Hälfte der Breite des Spielers. Jetzt ist es fast zentriert. Um es vollständig zu zentrieren, muss ich es um die Hälfte der Laserbreite verschieben. Jetzt ist es genau in der Mitte. Wie Sie sehen können, bewegen sich die Arme des Raumschiffs beim Abfeuern des kleinen Lasers nach innen, bewegen sich die Arme des Raumschiffs beim Abfeuern des kleinen Lasers nach innen um den dünnen Laserstrahl auf einen kleinen Bereich zu fokussieren Und die kleine Schnauze springt ein bisschen nach hinten. Ich werde die Laserbreite auf 50 Pixel einstellen, nur damit wir es besser sehen können Und ich gebe ihm zusätzliche Farbe weil die Mitte des Lasers so heiß ist, ich möchte, dass er reinweiß erscheint. Ich kopiere diesen Codeblock und setze ihn auf Weiß. Das sind, sagen wir, 60% der Breite des Laserstrahls, sodass wir 40% haben , um den weißen Strahl in der Mitte des Goldstrahls zu zentrieren. Wir müssen ihn um 20% der Breite zur linken Kante verschieben und ihn genau in die Mitte schieben. Jetzt ist die Mitte des Laserstrahls weiß und heiß. Ich denke, wenn man ihm zwei Farben gibt, sieht es viel besser aus. Da wir relative Werte verwendet haben, wird der weiße Strahl über dem goldenen Strahl zentriert. Unabhängig von der Laserbreite zehn Pixel immer noch zu breit. Ich möchte, dass die Breite des Strahls der Mündung unserer Laserpistole entspricht Fünf Pixel scheinen gut zu sein. 23. Laserschäden: Jede Laserunterklasse hat auch einen Schadenswert. Dadurch wird festgelegt, wie viel Schaden sie den Feinden pro Tick zufügen wird Lasst uns diesen Schaden tatsächlich umsetzen und Feinde mit dem Laser treffen. Im Moment interagiert es immer noch überhaupt nicht mit ihnen. Ich kann die folgende Logik an mehreren verschiedenen Stellen in meiner Codebasis platzieren . Ich habe beschlossen, es in die Render-Methode der Laserklasse aufzunehmen, weil ich den Code gerne organisiert und einfach zu navigieren habe. Es macht Sinn, weil ich die Kollision zwischen Laser und Feinden nur überprüfen möchte zwischen Laser und Feinden , wenn wir den Laser tatsächlich zeichnen. Wenn du dich erinnerst, haben wir ein Array, das alle derzeit aktiven feindlichen Wellen in unserem Spiel enthält . In diesem Array gibt es immer nur eine aktive Welle. Aber du kannst das Spiel schwieriger machen und die neue Welle starten, bevor die vorherige Welle vollständig besiegt ist. Zum Beispiel enthält jedes Wellenobjekt innerhalb eines Wellenarrays eine Reihe von Feinden. Wie wir hier in Zeile 280 definiert haben , enthält dieses Array alle derzeit aktiven Feinde in dieser Welle. Um alle aktiven Gegner zu durchqueren, werde ich das Wellen-Array durchgehen. Für jedes Objekt im Wellenarray wird es nur eines geben. In unserem Fall greifen wir auf sein Feinde-Array rufen auch für jedes gegnerische Objekt in diesem Array unsere wiederverwendbare Check-Collision-Methode auf für jedes gegnerische Objekt in diesem Array , die wir geschrieben haben bevor wir sie an Feind und Laserobjekt übergeben. Wenn Feind und Laser kollidieren, rufe ich die Treffermethode auf Feind auf und gebe diesen Schaden des Lasers Wir haben es hier in Zeile 32 definiert. Bevor ich es teste, werde ich auch alle Bosse innerhalb des Boss-Arrays durchgehen Auch hier gibt es normalerweise nur einen aktiven Boss, aber wenn du willst, kannst du mehrere Bosse gleichzeitig pushen mehrere Bosse gleichzeitig Oder du kannst den nächsten Boss pushen, wenn der vorherige nur noch wenige Trefferpunkte übrig hat Überlassen Sie die Spieldesign-Details bei Ihnen. Ich gebe dir alle Tools, gestalte dein eigenes Spiel für jeden Boss im Boss-Array. Wir nennen es auch Check-Collision-Methode zwischen diesem Boss und diesem Laserobjekt. Wenn sie kollidieren, rufen wir die Hit-Methode auf den Endgegner auf und geben sie als Argument „ Schaden des Lasers Wir haben unsere Render-Methode , die immer wieder ausgeführt wird solange wir die zweite Taste auf unserer Tastatur Wenn ich es teste, werden Sie feststellen, dass der Laser sehr leistungsstark ist. Es macht Spaß, alle Gegner leicht vernichten zu können, aber es nimmt dem Spiel die ganze Mühe weg. Wir wollen dem Spieler lustige und mächtige Waffen an die Hand geben. Aber wir wollen sie nicht so überwältigen. Der Grund, warum er so stark ist, ist, dass er Gegner mit 0,3 Leben pro Animationsframe trifft 0,3 Leben pro Animationsframe Und wir lassen das mit 60 Bildern pro Sekunde oder noch schneller laufen . Ich möchte, dass der Laserschaden langsamer abläuft und ich möchte sicherstellen, dass der Schaden für Benutzer auf alten Computern und für Benutzer auf neuen Spielecomputern mit Bildschirmen mit hoher Bildwiederholfrequenz gleich oder ähnlich und für Benutzer auf neuen Spielecomputern mit ist. Wir haben bereits eine Variable namens Sprite Update, die wir nur für einen Animationsframe alle 150 Millisekunden auf true setzen nur für einen Animationsframe alle 150 Millisekunden auf true Wir verwenden sie, um zu verlangsamen, wie schnell wir durch die Animationsframes in den Spritesheets Hier können wir dieselbe Variable verwenden, um zu verlangsamen, wie schnell der Laser tickt, wenn er Feinde durchbrennt Nur wenn dieses Punktspiel-Sprite-Update wahr ist, nur einmal alle 150 Millisekunden Wir wollen Kollisionen kontrollieren und Gegner und den Boss mit unseren Langstreckenlasern tatsächlich treffen Gegner und den Boss mit unseren Langstreckenlasern Jetzt können wir sehen, dass der Laser etwas weniger Leistung hat. Ich denke, das ist so viel besser. Wir können das jederzeit optimieren, indem wir diesen Punktschadenswert für kleine Laserunterklassen anpassen diesen Punktschadenswert für kleine Laserunterklassen Bisher galt unser Standardangriff für ein Leben pro Aber jetzt haben wir Laser, die Schaden anrichten können, das ist nicht gerade einer. Wir können in einem Szenario wie diesem enden, wenn der Boss weniger als ein Leben hat, er sollte bereits 0,8 Leben haben. Zählt nicht. In diesem Spiel will ich, dass der Boss explodiert, sobald seine Lebenspunkte unter eins fallen Ich gehe runter in die Boss-Klasse und muss mir diesen Codeblock ansehen Da steht, wenn das Leben mehr als Null ist, was bedeutet, dass eine was bedeutet, dass eine Gesundheit von 0,1 immer noch als lebendig gilt. Ich werde es hier, hier, hier und hier an all diesen Orten ändern . Ich ersetze das Häkchen überall durch diesen Punkt, Leben sind mehr oder gleich eins. Stellen Sie sicher, dass Sie es an all diesen Stellen ändern. Hier. Auch hier. Das tut mir leid. Versuchen wir nun, den Boss mit einem kleinen Laser zu behandeln. Ich werde langsam seine Tellurhülle durchbrennen. Und beachte, dass der Boss explodiert, sobald wir weniger als ein Leben Perfekt. Wir können den Schaden des Lasers hier korrigieren. Gut, es funktioniert immer noch gut. Ich habe es wieder auf 0,3 gesetzt Ich denke, das ist der richtige Wert für den kleinen Laser. Der große Laser wird viel leistungsfähiger sein. Natürlich wollen wir nicht, dass die Leben des Bosses so viele Dezimalstellen anzeigen. Ich gehe hier runter, um Methoden für beide Klassen zu zeichnen. Hier zeichnen wir beide Leben. Ich wickle es in Mattboden, um es abzurunden und entferne die kleinen Zahlen , die nach dem Komma kommen. Lass es uns noch einmal testen. Der Boss sollte sofort anfangen zu explodieren Sobald seine Lebenspunkte unter eins fallen und wir niemals null Leben sehen sollten Ja, wir machen große Fortschritte. Okay, Superlaser. Ich komme her und kopiere den gesamten Code darin. Ich füge es ein. Großer Laser, die Breite der Unterklasse wird 15 Pixel betragen. Der Schaden beträgt 0,7 pro Tick. Mehr als doppelt so viel Schaden wie der kleine Laser an der Spielerklasse. Wir erstellen ein Beispiel für einen großen Laser wie diesen. Dann können wir hier einen weiteren Lf-Block hinzufügen. Wenn die Taste, die gedrückt wurde, drei ist, ist der Spieler-Frame drei. gibt uns das endgültige Bild, in dem sich die Arme öffnen und Platz für den riesigen Laserstrahl schaffen. Und auch, um sicherzustellen, dass die Spitzen nicht durch die Hitze schmelzen. große Schnauze mit Laserlinien im Inneren springt heraus , um unsere ultimative Waffe, einen Superlaser, abzufeuern Wir zeichnen und aktualisieren den Laser, indem wir seine Rendermethode kleiner Laser und großer Laser nennen seine Rendermethode kleiner Laser und großer Laser Ihr könnt sehen, dass er mehr als den doppelten Schaden verursacht. Es ist eigentlich zu klein. Ich möchte, dass der Laserstrahl die Kanone vollständig füllt, vielleicht 25. Schau dir an, wie der Spieler animiert , damit sie zu den Waffen passen. Basisangriff, kleiner Laser und großer Laser. Was glauben Sie, können wir Feinde leicht vernichten? Jetzt sind die neuen Laserwaffen besonders effektiv gegen Gruppen von Feinden, da sie die gesamte Feindeskolonne auf einmal beschädigen . Ich möchte den Spieler und den Laserstrahl hinter den Feinden platzieren . Aber vor dem Spieltext schneide ich ihn aus und füge ihn hier ein, nachdem wir Projektile gezogen haben, aber bevor wir den Boss zeichnen, ist das, was zuerst gezeichnet wird, dahinter In Fällen wie diesem, wenn wir alles auf ein einzelnes Leinwandelement zeichnen , versuchen Sie, das Spiel ein bisschen zu spielen, und lassen Sie mich wissen, was Sie denken Hast du irgendwelche anderen Waffenideen, etwas ganz anderes als das? Was ist zum Beispiel, wenn wir einen Schwarm kleiner Roboter aussenden? Oder wie wäre es mit einer Elektrizitätswaffe? 24. Ressourcenmanagement: Das Spiel ist trivial und einfach geworden. Jetzt, wo wir Laser ohne Grenzen verwenden und es eigentlich keinen Grund gibt , den kleinen Laser zu verwenden wenn wir Superlaser ohne Unterbrechung verwenden können Es wäre viel besser, wenn der Spieler einen Energiepool hätte , den er verwalten müsste Bevor wir das tun, möchte ich sichergehen, dass der Boss nur getroffen wird , wenn er vollständig sichtbar ist. Ich möchte nicht, dass die Anzahl der Lebenspunkte bereits reduziert ist, wenn er auf dem Bildschirm schwebt Wir werden die Kollision zwischen dem Boss und den Lasern nur überprüfen , wenn die vertikale Position des Bosses mehr oder gleich Null ist Sobald der Boss den Spielbereich vollständig betreten hat, teste ich es. Ja, das funktioniert. Fügen wir einen Mechanismus für das Ressourcenmanagement hinzu. Der Spieler wird einen Energieriegel haben. Startenergie beträgt beispielsweise 50, die maximale Energie beträgt 100. Wenn wir nicht auf unser Energieniveau achten, können wir unseren Laser auch überhitzen , wodurch er für kurze Zeit deaktiviert wird Ich nenne diese Eigenschaft zum Beispiel Cool Down Wir werden den Energieleisten hier unten in der Methode Draw Status Text anzeigen. Auch hier verwenden wir vier Schleifen, die für jeden Energiepunkt einmal ausgeführt werden. Für jeden Energiepunkt zeichnen wir ein Rechteck. Wir werden sie im Abstand von zwei Pixeln vertikal platzieren, ich schätze, 50 Pixel von oben entfernt. Jedes Energierechteck wird nur zwei Pixel breit und 15 Pixel hoch sein. Da wir sie nebeneinander zeichnen , bilden sie einen Balken. Dieser Wert ist der Abstand zwischen den Balken, und dies ist die Breite jedes Balkens. Wenn sie denselben Wert haben, sehen wir eine durchgehende Form. Wenn die Breite kleiner als der Abstand ist, sehen wir einzelne Rechtecke Sie können mit diesen Werten spielen und es wird Sinn machen, wenn Sie sehen, wie sie auf verschiedene Werte reagieren, die Sie ihr geben Wenn ich hier 15 plus mache, das der vertikale Rand vom linken Rand. Ich möchte es wahrscheinlich 130 Pixel vom oberen linken Rand entfernt haben. Der Abstand von 20 entspricht zwei Pixeln, und die Breite jedes Energierechtecks beträgt ebenfalls zwei Pixel. Wir erhalten einen durchgehenden Balken. Wenn wir jetzt die Laser abfeuern, wird uns das Energie kosten Hier oben sage ich, dass dieser Spieler Energie abzüglich dieses Schadens verursacht Das bedeutet, dass kleine Laser jetzt billiger sind als große Laser Manchmal ist ein kleiner Laser die bessere Option Wenn ich jetzt Laser verwende, kann man sehen, dass dadurch tatsächlich Energie verbraucht wird Die Geschwindigkeit, mit der die Energie verbraucht wird , entspricht kleiner Laser kostet 0,3 Energie pro Dicke, ein großer Laser kostet 0,7 Ich möchte, dass sich die Energie langsam wieder auflädt. Wenn die aktuelle Energie unter der maximalen Energie liegt, erhöhen Sie die Energie pro Animationsframe um 0,05 Ich teste es, die Energie wird wieder aufgeladen Wenn ich es aufgebraucht habe, wird es automatisch aufgeladen. Nochmals, gut, wir müssen verhindern, dass die Energie in Minuswerte fällt Der Spieler muss die Energie verwalten und auf den Energieriegel achten. Wenn sie es so lange stehen lassen , dass die Energie bei E unter fällt, wird der Cooldown ausgelöst. Wir haben die Kanone überhitzt und jetzt müssen wir warten. Es ist eine kleine Strafe dafür, dass man nicht aufgepasst hat. Wenn wir optimal spielen wollen, verbrauchen wir niemals Energie unter eins Wir lösen nie den Cooldown aus, den ich sonst hier machen kann, weil die Energie im selben Animationsframe niemals unter eins und mehr als die maximale Energiezeit von 0,2 liegen kann eins und mehr als die maximale Energiezeit im selben Animationsframe niemals unter eins und mehr als die maximale Energiezeit von 0,2 Das spart uns ein bisschen Leistung. Wenn die Energie unter eins fällt, wird eine Abkühlung ausgelöst und die Energie wird wieder aufgefüllt, sodass die maximale Energie-Abklingzeit wieder auf 20% sinkt Die Laserkanone hatte genug Zeit, um sich abzukühlen, und wir können unsere Laser benutzen Auch hier möchte ich dem Spieler visuell klar machen, wann wir in einen Cooldown eingetreten sind, wenn wir unsere Kanonen hier unten, wo wir den Energieleisten ziehen, überhitzt haben, werden wir seine Farbe ändern Ich werde es in einen Safe packen und restaurieren, um sicherzustellen, dass diese Füllfarben keine Auswirkungen auf andere Formen haben, die wir auf Leinwand zeichnen Wir sind ziemlich weit in dieser Klasse. Anstelle einer standardmäßigen F L-Anweisung werde ich hier die ternäre Operatorsyntax als einfache einzelne Zeile verwenden hier die ternäre Operatorsyntax als einfache einzelne Zeile Wenn L, wenn die Abklingzeit des Spielers stimmt. Fragezeichen, setze den Füllstil auf Rot L. Doppelpunkt setzt den Füllstil auf Gold, einfach. Lass es uns testen. Energieriegel sind Gold. Standardmäßig bedeutet das, dass alles in Ordnung ist und wir unsere Laser benutzen können wenn ich meine Laser benutze, ohne auf den Energieleisten zu achten , und ich lasse ihn unter eins fallen, lösen wir aus, dass der Abkühlungsbalken rot wird, was bedeutet, dass unsere Laser überhitzt sind und wir für eine Weile nur den Basisangriff verwenden können Wenn wir mehr als 20% der maximalen Energie erreichen. Jetzt färbt sich der Balken wieder goldfarben und wir wissen, dass wir die Laser wieder verwenden können. Ich habe das Gefühl, dass wir immer noch unter Null Energie fallen. Ich kann das wirklich sauber machen, indem ich das mache. Hier ist die übergeordnete Laserklasse und ihre Render-Methode. Wir rufen diese Render-Methode niemals direkt auf. Wir rufen sie immer von einer der Unterklassen hier aus auf. Stellen wir sicher, dass wir die Laser nur rendern , wenn die Spielerenergie größer als eins ist Gleichzeitig zeichnen wir kleine Laser und aktualisieren sie, wenn die Abklingzeit nur falsch ist . Ich kopiere das und mache dasselbe für den großen Laser. Nun zu dem Grund, warum ich Rendern für Unterklassen verwende. Bisher sind diese Methoden identisch und wir wissen, dass Dinge, wenn sie gemeinsam genutzt werden, in der übergeordneten Klasse verbleiben können Unterklassen enthalten nur Eigenschaften und Methoden, die für diese Unterklasse spezifisch Wenn ich meine Energie aufgebraucht habe und versuche, die Laser abzuschießen, das Spielerschiff immer noch animiert sich das Spielerschiff immer noch, als ob wir den Laser abfeuern Aber da wir uns im Niedrigenergie-Modus befinden und uns im Abkühlmodus befinden, kommt der Laserstrahl nicht heraus Es könnte gut für dein Spiel sein. Ich bin mir nicht sicher, was du bevorzugst, aber in meinem Spiel möchte ich nur, dass das Spritesheet der Spieler so animiert wird, wenn wir tatsächlich mit den Lasern schießen anstatt hier das Spielerbild auszutauschen Hier lösche ich diese Zeilen. Von hier aus werden wir tatsächlich das Spritesheet der Spieler austauschen. Das bedeutet, dass der Spieler diese Waffenangriffe nur animiert , wenn wir sie tatsächlich rendern Diese Laserstrahlen. kleiner Laser ist Bild x zwei, ein großer Laser ist Bild x drei. So wie es vorher war. Jetzt, wenn ich im Cooldown bin, kommt kein Laser raus und das Spritesheet der Spieler animiert den Waffenangriff nicht 25. Eaglemorph-Feind-Kurs: Wir beginnen mit dem Import von Tabellenkalkulationen in das Projekt. Platzieren Sie beide Bilder in Ihrem Projektordner, und wir verweisen hier auf das Bild mit einer Quelle. Eaglemorphngid wird Eaglemorph sein. Wir werden auch ihre stacheligen, schleimigen, mutierten Projektile benötigen, die als Tabelle mit acht mutierten Bildern geliefert werden. Ich nenne sie Enemy Projectile. Wie üblich werde ich sie beim ersten Laden des Spiels mit CSS verstecken beim ersten Laden des Spiels mit CSS Anstatt den ersten Boss zu drängen. Wir werden mit der feindlichen Welle beginnen. Denn heute werden wir dieser Welle einen neuen Feindtyp hinzufügen . Ich kopiere diesen Codeblock, mit dem wir die Feindklasse Rhino Morph erstellt Ich benenne ihn in Eagle Morph um und richte seine Bildeigenschaft auf die neue Tabelle aus, die wir gerade in das Projekt aufgenommen Ich habe diesem Feind eine detailliertere Explosionsanimation mit einigen zusätzlichen Max Frame wird acht Jahre alt sein. Wir zählen ab dem Bild Null auf der linken Seite der Wellenklasse, in der wir jede feindliche Welle erzeugen , indem wir Feinde in einem Raster organisieren. Ich füge noch einen L-if-Anweisungsblock hinzu. Ich muss hier sehr vorsichtig mit den Klammern sein. Wenn ich das mache, möchte ich, dass das Meiste aus Eagle-Morphs gemacht wird , weil wir sie implementieren und testen werden Nehmen wir an, 80% werden Adler-Morphe sein, 10% werden Nashorn-Morphe sein, die restlichen 10% werden Käfer-Morphe die restlichen 10% werden Wir bekommen bereits Adler-Morphs, und weil wir den gesamten Codeblock, den wir zuvor für Rhino-Morphs verwendet haben, kopiert und wiederverwendet haben wiederverwendet Sie können sehen, dass es jedes Mal, wenn ich es treffe, ein Körpersegment verliert Wenn das letzte Segment weg ist, wird eine Explosionsanimation abgespielt. Bisher funktioniert es genauso wie Rhino Morph. Es ist ein Feind mit mehreren Leben. Und die Tabelle gibt den aktuellen Schadensstatus wieder. Wir wollen, dass es uns die fehlenden mutierten Körpersegmente als Projektile zurückspuckt die fehlenden mutierten Körpersegmente als Projektile zurückspuckt , die der Spieler zerstören oder vermeiden muss Ich fange damit an, den Adler ein bisschen zum Zucken zu bringen. Jedes Mal, wenn es getroffen wird, wird es gut aussehen und in einer Minute Sinn machen Wenn wir ihm Projektile geben, sieht es so aus, als würde er sich vorwärts bewegen und das Projektil in Richtung des Spielers spucken Es ist nur ein kleines Zucken. Es gefällt mir so Alles gut. Ich werde es versuchen. Wir können diese Methode entweder nach dem Zufallsprinzip ausführen, sodass die Feinde schießen, und die Projektile in zufälligen Abständen auf uns spucken Aber was ich heute machen werde, weil ich es mag, dass das Spiel auf Spieleraktionen reagiert und interaktiv ist Die Schussmethode wird ausgeführt, wenn dieser Feindtyp von einer Spielerwaffe getroffen wird. Wenn wir ihn treffen, versucht er uns zurückzuschlagen. Er reagiert darauf, getroffen zu werden. Feindliche Projektile werden ein Objektpool sein. Da wir bereits wissen, wie man diese einfache Codestruktur von Anfang dieses Kurses verwaltet , werde ich diese gesamte Projektilklasse kopieren Wir verwenden es, um einen Pool von Spielerprojektilen zu erstellen, den Basisangriffen unseres Raumschiffs Ich habe den gesamten Codeblock kopiert und ihn hier zwischen meine Eagle Morph - und Boss-Brille eingefügt meine Eagle Morph Ich werde es in Feindliches Projektil mit 50 Pixeln Höhe und 35 Pixeln umbenennen Projektil mit 50 Pixeln Höhe und 35 Pixeln Feindliche Projektile fliegen von Feinden auf den Spieler zu Die positive Richtung auf der Geschwindigkeit der vertikalen Y-Achse ist ein zufälliger Wert, 2-5 Pixel pro Animationsframe Dies ist ein Mitglied des Objektpools. Wir werden einen wiederverwendbaren Objektpool mit, sagen wir, 15 feindlichen Projektilen erstellen Objektpool mit, sagen wir, 15 feindlichen Projektilen Weil es effizienter ist, Objekte wiederzuverwenden , als ständig neue zu erstellen und die alten zu verwerfen Jedes Mitglied des Objektpools muss über eine Eigenschaft verfügen , die angibt, ob es frei ist und aus dem Objektpool abgerufen werden kann , oder ob das Objekt gerade ausgelastet ist Aktiv im Spiel. Wir zeichnen und aktualisieren gegnerisches Projektil nur , wenn es benutzt wird Wenn es frei ist, ist Eigentum dazu bestimmt, herunterzufallen. Jedes Mitglied des Objektpools benötigt außerdem eine Methode, die ausgeführt wird, wenn wir sie aus dem Pool nehmen und sie im Spiel aktivieren. In diesem Fall nennen wir diese Methode Start und übergeben ihr X- und Y-Koordinaten des Feindes , der dieses Projektil abschießen wird Und wir setzen sein freies Eigentum auf Stürze. Da es aktiv wird, ist es nicht mehr kostenlos und kann verwendet werden. Es benötigt auch eine Neustartmethode , mit der es deaktiviert wird. Es kehrt zurück in den Objektpool, indem es seine Eigenschaft free auf true setzt, was bedeutet, dass es nicht mehr gezeichnet und aktualisiert wird. Das ist unsere feindliche Projektilklasse, ein Mitglied des Objektpools Wir werden diesen Objektpool von der Hauptspielklasse aus verwalten. Er wird genauso funktionieren wie der Projektilpool für Spieler, den wir zu Beginn dieses Projekts erstellt haben den wir zu Beginn dieses Projekts erstellt Ich habe diese drei Codezeilen kopiert. Ich muss vorsichtig mit Kamelhäuschen sein. Hier werden wir ein Array haben , das alle Objekte aufnehmen wird. Ich nenne es „Pool feindlicher Projektile“. Die Anzahl der feindlichen Projektile wird ebenfalls 15 betragen. Wir werden dieselben 15 Objekte immer wieder erstellen und wiederverwenden, um die Leistung zu verbessern und die Speicherverwaltung unseres Java-Script-Projekts Sobald das Spielobjekt erstellt ist, lösen wir automatisch die Methode „Gegnerische Projektile erstellen“ aus. Lass uns diese Methode schreiben Hier haben wir nun zwei Methoden, mit Spielerprojektilen umzugehen Kopiere beide und ich füge sie hier ein. Wie gesagt, wir werden eine Methode haben, die einen Objektpool für feindliche Projektile Erstellung feindlicher Projektile Wir haben die Anzahl der gegnerischen Projektile auf 215 festgelegt, sodass der Loop 15 Mal läuft Jedes Mal, wenn er läuft, nimmt er Reihe feindlicher Projektile und schiebt ein neues gegnerisches Projektilobjekt in das Array, wobei die Klasse verwendet wird ein neues gegnerisches Projektilobjekt , die wir Außerdem benötigen wir eine Methode, die bei jedem Aufruf ein freies gegnerisches Projektilobjekt aus dem Pool holt, sodass der Feind es abschießen kann Methode „Gegnerisches Projektil abrufen“ wird der Pool gegnerischer Projektile durchsucht , wir gerade mit 15 Objekten gefüllt Sobald sie das erste Objekt findet , bei dem drei Eigenschaften auf „true“ gesetzt sind, sobald sie das erste inaktive, verfügbare gegnerische Projektilobjekt findet , beendet sie die Suche und das Objekt einfach an uns zurück Ordnung, wir haben die gesamte Logik im Griff, jetzt können wir den Objektpool feindlicher Projektile verwenden , um auf den Spieler zurückzuschießen. Lass es uns versuchen 26. Feindliche Projektile: Ich steige in meine Eagle Morph Feindklasse auf. Hier haben wir eine benutzerdefinierte Schießmethode erstellt. Wenn ihr euch an das Innere erinnert, werden wir ein neues gegnerisches Projektilobjekt aus dem Pool der gegnerischen Projektile ziehen aus dem Pool der gegnerischen Projektile Mit unserer benutzerdefinierten Methode zum Abrufen feindlicher Projektile überprüfen wir, ob wir tatsächlich ein kostenloses Projektil erhalten haben, da es sein kann , dass alle 15 Projektile benutzt wurden und nicht verfügbar sind Aber wenn wir es geschafft haben, eines aus dem Pool zu bekommen, rufen wir seine Startmethode Wir wissen, dass die Startmethode X- und Y-Koordinaten benötigt, um dem Projektil mitzuteilen , von wo auf der Leinwand die Animation beginnen soll Wir übergeben ihr die X- und Y-Koordinaten des Eagle-Morph-Feindes, der gerade dieses Projektil abgeschossen hat Jetzt muss ich hier runter zur Haupt-Render-Methode gehen. Genauso wie wir alle Spielerprojektile zeichnen und aktualisieren. Ich kopiere den Punkteblock und renne für jede Methode, für jedes Objekt in feindlichen Projektilen Pull Array, Vorsicht mit dem Großbuchstaben P. Für jedes feindliche Projektil im Array rufe ich die Methoden Update und Draw auf und wir übergeben es im Kontext Lass es uns versuchen. Ich schieße und treffe den Feind. Es schießt noch nicht auf uns zurück. Okay, ich gehe hier hoch zur feindlichen Projektilklasse. Hier wollen wir, dass die Projektile in positiver Richtung nach unten animiert Auf der vertikalen Y-Achse. Dieses Y-Plus entspricht dieser Geschwindigkeit. Sie schießen immer noch nicht. Ich ändere den Füllstil auf Rot. Projektile fliegen nach unten. Dieser Check muss mehr als, sagen wir, 200 Pixel Okay, lassen Sie uns diese Zeile vorerst auskommentieren. Consolo hilft uns immer. Unser Consolo-Projektil haben wir aus feindlichen Projektilen gezogen Wenn ich schieße und den Feind treffe, kann ich sehen, dass dieser Codeblock überhaupt nicht läuft Es gibt kein Consolo. Okay, jetzt verstehe ich es Wenn wir den Feind mit einem Spielerprojektil treffen, wollen wir seine Schussmethode so nennen Jetzt wird es reagieren. Das ist es. Wenn ich den Feind treffe, wird er auf uns zurückschießen. Jetzt funktioniert es perfekt. Wir wollen, dass das Projektil der Mitte des Feindes startet, nicht in der oberen linken Ecke Ich überhole es X des Feindes plus die Hälfte des Feindes mit dem Feind plus die Hälfte der feindlichen Höhe Jetzt kommen sie aus der Mitte. Das ist besser. Wir wollen keine roten Rechtecke zeichnen Wir möchten mutierte feindliche Projektile aus unserem benutzerdefinierten Tabellenbild zeichnen aus unserem benutzerdefinierten Tabellenbild Wir übergeben ihm das Bild, das wir zeichnen möchten, und die X- und Y-Koordinaten, wo es gezeichnet werden soll Dadurch wird das gesamte Bild in seiner Originalgröße gezeichnet Wir müssen diese Punktbildeigenschaft erstellen und haben auf das Bild gezeigt, auf das wir im Index-HTML verwiesen haben. Vorhin habe ich ihm eine Vorstellung von einem feindlichen Projektil gegeben. Wir zeichnen das gesamte Spritesheet. Wenn ich nun Breite und Höhe so übergebe, wird das gesamte Spritesheet auf einen Bereich von einem Frame zusammengedrückt einen Bereich von einem Frame zusammengedrückt Wie immer müssen wir ihm auch Quelle X, Quelle Y, Quellbreite und Quellhöhe übergeben , um einen Zuschnitt im Bereich zu definieren Frame X definiert den horizontalen Zuschnitt an der Position Er ist Null, Eins, Zwei oder Drei. Bild Y wird der vertikale Zuschnitt im Bereich vorgenommen und der Wert ist Null oder Eins. an die Methode draw image übergebene Quell-X-Argument lautet Frame X multipliziert mit einer Framebreite von 50 Pixeln. Quelle Y entspricht Frame Y mal Framehöhe von 35 Pixeln. Wir übergeben es mit Höhe als Quellbreite und Quellhöhe. Jetzt ist jedes Projektil eines der zufällig mutierten Alien-Babys Ich schätze, jeder Ego-Morph hat insgesamt vier Körpersegmente. Jedes Mal, wenn wir ihn mit dem Basisangriff treffen, verliert er ein Segment und schießt ein Projektil auf uns zurück Wenn wir es mit dem Laser treffen, mehr als vier Projektile freigesetzt, weil der Laser schneller tickt und diese Schussmethode häufiger auslöst Wir wollen sie auf maximal vier Projektile pro Feind beschränken maximal vier Projektile pro Ich nenne es zum Beispiel Schüsse. Es zählt einfach jedes Mal, wenn wir schießen, wie viele Schüsse dieser Adler-Morph abgefeuert Wir erhöhen diesen Wert bei Schüssen um eins , wenn der Feind durch Spielerangriffe Schaden erleidet Wenn dieser Punktschuss weniger als vier ist, schießt er, er schießt viermal, wenn dieser Punktschuss 012,3 Jetzt feuern Laser alle vier Projektile sehr schnell ab, aber der Feind kann keine Projektile mehr auf uns zurückschießen . Danach, mit dem Basisangriff, ist es ein Treffer, ein er schießt viermal, wenn dieser Punktschuss 012,3 Jetzt feuern Laser alle vier Projektile sehr schnell ab, aber der Feind kann keine Projektile mehr auf uns zurückschießen . Danach, mit dem Basisangriff, ist es ein Treffer, ein gegnerisches Projektil. Gibt es Rückendeckung? Mit dieser Implementierung werde ich sie etwas später gründlich testen. Im Moment haben wir 15 feindliche Projektile im Pool. Irgendwann werden sie mir ausgehen. Ich muss sicherstellen, dass sie zurückgesetzt werden und wieder in den Pool zurückkehren , wenn sie vom Bildschirm verschwinden. Wenn die vertikale Koordinate des Projektils größer als die Höhe des Spielfeldes ist, setze es zurück und lege es zurück in den Pool verfügbarer Objekte Dazu benötigen wir Zugriff auf dieses Spiel. Wir übergeben es als Argument und konvertieren es in eine Klasseneigenschaft. Dies ist nur eine Referenz, die auf die Stelle im Speicher verweist , an der sich das Hauptspielobjekt befindet. Es wird keine Kopie des Spielobjekts erstellt. Wenn wir ein gegnerisches Projektil erzeugen, müssen wir das Spiel als Argument weitergeben Wir befinden uns im Hauptspielobjekt. Hier übergebe ich ihm dieses Schlüsselwort, um es zu testen. Ich werde nur zwei feindliche Projektile im Pool haben. Ich möchte überprüfen, ob sie zurückgesetzt wurden. Sollte ich nur zwei freilassen und gegnerische Projektilpool ist leer Sobald sie den Bildschirm verlassen, sie zurückgesetzt und können wieder verwendet werden Das funktioniert. Ich habe es auf 15 zurückgesetzt. 27. Projektil-Interaktionen: Gegenwärtig Gegenwärtig interagieren feindliche Projektile nicht mit dem Spieler Lassen Sie uns die Kollision zwischen feindlichen Projektilen und dem Spieler überprüfen feindlichen Projektilen und dem Spieler Wenn unsere benutzerdefinierte Methode zur Überprüfung der Kollision zwischen diesem gegnerischen Projektil verwendet wird, ist der Spieler richtig Denkt daran, dass dieser Codeblock nur für aktive gegnerische Projektile läuft , was ideal ist Wenn ein aktives gegnerisches Projektil mit dem Spieler kollidiert, setzen wir das Projektil zurück und bringen es zurück Außerdem werden wir die Lebenspunkte der Spieler um eins reduzieren. Wir prüfen, ob es weniger als ein Spielerleben gibt. Wenn ja, setzen wir Game Over auf True. Wenn ich schieße, schießt der Feind zurück. Wir werden 123 getroffen. Das Spiel ist vorbei. Das funktioniert gut. Ich möchte, dass der Spieler die feindlichen Projektile abschießen kann , aber nur mit dem Basisangriff Mutierte gegnerische Projektile sind klein und haben große Augen Sie können massiven Laserstrahlen leicht ausweichen. Wir können sie nur mit dem Basisangriff treffen. Das ist nur meine Wahl. Ich versuche nur, dem Gameplay etwas Komplexität zu verleihen und dem Spieler mehr Gründe zu geben, den Basisangriff zu verwenden. Wir haben die Methode zur Aktualisierung der gegnerischen Projektilklasse im Blick. Dieser Code wird nur ausgeführt, wenn das gegnerische Projektil aktiv ist. für jedes Objekt in der Pull-Reihe der Spielerprojektile Überprüfe für jedes Objekt in der Pull-Reihe der Spielerprojektile die Kollision zwischen diesem gegnerischen Projektil und dem Projektil Außerdem führen wir diesen Code nur aus, wenn das Projektil des Spielers aktiv ist, wenn sein freies Eigentum falsch ist Wenn ein aktives gegnerisches Projektil und das Projektil eines aktiven Spielers kollidieren, nehmen wir das Projektil des Spielers und Bringen Sie es zurück in den Pool. Wir werden eine benutzerdefinierte Treffermethode für das gegnerische Projektil aufrufen und ihm einen Schaden von eins Wenn das gegnerische Projektil weniger als eins Lebenspunkte hat, rufen wir seine Reset-Methode auf und bringen es ebenfalls zurück in den Pool jedes gegnerische Projektil In meinem Spiel hat jedes gegnerische Projektil tatsächlich fünf Leben. Sie sind ziemlich hart, wenn sie getroffen werden Wir reduzieren die Anzahl der Leben durch Schaden. Wir werden auch ihre Geschwindigkeit auf, sagen wir, 60% der aktuellen Geschwindigkeit reduzieren . Wir haben die Möglichkeit, sie vollständig zu zerstören oder sie einfach zu verlangsamen und ihnen auf diese Weise auszuweichen. wiederum gibt uns mehr strategische Optionen, mehr Auswahl und mehr Kontrolle. Der Spieler kann entscheiden, diese robusten Projektile zu zerstören oder sie einfach zu verlangsamen und sie flach laufen zu lassen . Ich spiele das Spiel und ich kann sehen , dass neue Projektile sehr langsam werden und sie nur ein Leben haben Ich spiele ein bisschen mehr und versuche, die Projektile mit meinem Basisangriff zu kontrollieren Ja, das wird jetzt klarer, da feindliche Projektile ein Objektpool sind Wenn wir sie wieder in den Pool zurückbringen, müssen wir ihre Lebenspunkte wieder auf fünf zurücksetzen Ich kann ihre Bilder X und Y auch nach dem Zufallsprinzip auswählen, um ihnen ein anderes Bild als die Tabelle zu geben ihnen ein anderes Bild , eine andere Da wir ihre Geschwindigkeit ändern, indem wir sie treffen, müssen wir diese Geschwindigkeit auch auf den ursprünglichen Wertebereich zurücksetzen diese Geschwindigkeit auch auf den ursprünglichen Wertebereich Hier unten kümmern wir uns um die Verteilung nach Gegnertypen. 30% der Gegner in meinem Spiel werden Adler-Morphs sein, 30% werden Nashorn-Morphs sein und die letzten 40% werden Käfer-Morphs sein Ich würfle hier zweimal nach dem Zufallsprinzip. Das wird nicht funktionieren. Ich muss den Code umgestalten. Ich hoffe, dass Sie sich inzwischen mit dieser Codebasis wohl genug fühlen Sie können es selbst optimieren und die Regeln anpassen. In meinem Spiel kann jeder Eagle-Morph-Feindtyp maximal vier Projektile abfeuern Diese Projektile sind robust und haben fünf Leben, sodass wir ihnen ausweichen oder sie verlangsamen können Aber nur mit dem Basisangriff werden sie fortgeschrittene Laserstrahl-Angriffe ignorieren Ein Adler, der sich mit dem Laser verändert, lässt alle vier gegnerischen Projektile schneller abfeuern Bei einem normalen Angriff wird pro Treffer ein gegnerisches Projektil abgefeuert. Es macht mir wirklich Spaß, verschiedene Gegnertypen wie diesen zu entwerfen , zu zeichnen und zu codieren Ich könnte zehn weitere Folgen machen verschiedene außerirdische Arten zum Einsatz kommen. Du bist der Experte. Jetzt gebe ich dir eine weitere Tabelle mit Squid Jetzt, wenn du willst, kannst du es als deinen eigenen einzigartigen Feind einsetzen Wie wird dein Quid Morph funktionieren? Lass es mich in den Kommentaren wissen. 28. Squidmorph-Feind-Kurs: Nun, da wir alles haben, möchte ich Ihnen zeigen, wie einfach und schnell es ist, unserer bestehenden Codebasis weitere Feindtypen hinzuzufügen unserer bestehenden Codebasis weitere Feindtypen hinzuzufügen Die Tabelle, die ich Ihnen gebe, ist ziemlich flexibel. Du kannst diesem Feind verschiedene Eigenschaften und Verhaltensweisen geben . Ich gebe dir eine gegnerische Version in normaler Größe und auch eine Tabelle in beiden Größen. Lassen Sie uns jetzt beide implementieren, um unser Spiel noch abwechslungsreicher und unterhaltsamer zu gestalten. Du kannst alle Kunst-Assets im Abschnitt Ressourcen unten herunterladen . Ich habe es in meinen Projektordner gelegt und verweise hier im Index-HTML-Bild mit einem Quell-Squid-Morph darauf im Index-HTML-Bild mit einem Quell-Squid-Morph Ng und ID werden Squid Morph sein. Wir verstecken dieses Bild mit CSS, weil wir es nur mit Java-Skript auf Leinwand zeichnen wollen Squid Morph ist ein weicher, flexibler Space-Bug. Er kann viele unserer Projektile absorbieren, aber wir haben festgestellt, dass er sich jedes Mal, wenn er eines davon absorbiert, ein wenig aufbläst Füttern wir es einfach mit Projektilen, bis es platzt. Ich habe diese Tabelle erstellt, um einen aufblasbaren Feindtyp zu erstellen Wenn wir ihm, sagen wir, zehn Leben geben, wird er jedes Mal, wenn wir ihn treffen, nur ein bisschen größer Aber diese Tabelle funktioniert auch, wenn Sie Ihren Tintenfisch-Morph anders gestalten möchten Wenn du ihm nur zwei Leben gibst, wenn du ihn mit dem dritten Projektil triffst Die restlichen Frames werden einfach in einer schnellen Sequenz abgespielt Diese Tabelle wird auch dafür funktionieren, ich hoffe, es macht Sinn, was ich meine Aber lass mich dir auch den Code zeigen. Im Grunde haben wir einen Feind, Sie nach Belieben modifizieren können. Die Tabelle, die ich dir gebe, funktioniert, wenn der Feind 1-10 Leben hat Wenn Sie sich erinnern, haben wir hier in Zeile 161 unsere übergeordnete Feindklasse, die Methoden und Eigenschaften enthält, die von allen verschiedenen Feindtypen gemeinsam genutzt Dann erweitern wir diese Klasse um Eigenschaften und Methoden, Verhaltensweisen, die für einzelne Feindtypen spezifisch sind. Weil wir wollen, dass jeder Feindtyp ein anderes Bild, andere Anzahl von Leben usw. hat. Beetle Morph ist unser einfacher Grundfeind. Rhino Morph ist ein gepanzerter Rücken mit einem dicken Exoskelett. Eagle Morph opfert seine eigenen Körpersegmente und spuckt sie auf uns Jetzt fügen wir Squid Morph hinzu, einen aufblasbaren Feind, der unsere Projektile auffrisst, bis er platzt unsere An dieser Stelle ist es sehr einfach, weitere Feindtypen hinzuzufügen, da wir bereits so viel von der Logik aus den vorherigen Lektionen übernommen haben Logik aus den vorherigen Lektionen übernommen Ich kann einfach den Rhinomorph-Codeblock kopieren. Ich benenne ihn in Squid Morph um. Und wir haben hier auf das Squid Morph Sprite Sheet gezeigt. Und das war's, es wird einfach so funktionieren. Hier unten auf der Linie 405 haben wir unseren Wave-Kurs. Erzeugt feindliche Wellen und organisiert sie mit dieser benutzerdefinierten Erstellungsmethode, die wir zuvor geschrieben haben, in einer gitterartigen Formation Erstellungsmethode, die wir zuvor geschrieben haben Ich rufe hier und hier thotrandom an. Was nicht der beste Weg ist, weil ich möchte, dass der Code leicht lesbar ist Ich möchte, dass er sehr einfach zu lesen ist. Welcher Prozentsatz der Welle besteht aus welchem Feindtyp? Ich würfle nur einmal pro Gegner im Feld nach dem Zufallsprinzip. Ausgehend von diesem einen zufälligen Wert entscheiden wir, welcher Feindtyp hinzugefügt wird Ich nenne diese Variable zum Beispiel Zufallszahl und sie wird ein Zufallswert von 0-1 sein Wenn die Zufallszahl also kleiner als 0,3 ist , ist der Feind Eagle Morph Ungefähr in 30% der Fälle werden 30% jeder Welle aus Adler-Morphen L bestehen . Wenn dieselbe Zufallszahl kleiner als 0,6 ist und diese Zufallszahl zwischen 0,3 und 0,6 liegt , sind weitere 30% der Welle Nashorn-Morphen L, was bedeutet, dass, wenn die Zufallszahl zwischen 0,6 und eins liegt, die letzten 40% jeder Welle aus Käfer-Morphen bestehen. Ich hoffe, es macht Sinn. Wir haben tatsächlich vier Feindtypen. Ich werde noch einen hinzufügen. Wenn ich hier blockiere, muss ich sehr vorsichtig mit Klammern sein. Wenn ich das mache, ist es leicht, meinen Code mit einer einzigen falsch gesetzten Klammer zu beschädigen meinen Code mit einer einzigen falsch gesetzten Klammer Wir testen gerade Tintenfisch-Morphs. Ich werde sie hier hinzufügen. Ich kann hier zum Beispiel Eagle Morph platzieren. Es spielt noch keine Rolle, weil ich möchte, dass das Beste aus der Welle, eigentlich der gesamten Welle jetzt aus Tintenfisch-Morphen besteht, weil wir sie testen müssen Wie Sie sehen können, funktionieren sie bereits. Und alles, was wir bisher getan haben, ist den Codeblock für Rhino-Morphs wiederzuverwenden Wenn ich das Spiel spiele, sehe ich, dass wir ein paar Dinge anpassen müssen Die Logik, die wir hier haben , ist, dass wir, wenn wir einen Feind treffen, seine Tabelle horizontal um ein Bild austauschen Wenn der Feind weniger als ein Leben hat. Wie hier in Zeile 186 definiert, vergrößern wir Frame X weiter um die Explosionsanimation abzuspielen. Sobald Bild X größer als das maximale Bild ist, wissen wir, dass wir den Feind löschen können, da die gesamte Explosionsanimation abgespielt wurde. Um die Explosionsanimation tatsächlich zu sehen, schaue ich in meiner Squid Morph-Tabelle Und ich sehe, dass ich die maximale Framerate auf, sagen wir, 16 erhöhen muss maximale Framerate auf, sagen wir, 16 Um sicherzustellen, dass all diese Frames angezeigt werden, bevor der Feind aus dem Spiel gelöscht wird. Jetzt schieße ich es ab. Ich vernichte alle vier Leben. Es spielt den Rest der Tabelle ab und zeigt, wie der Feind sich aufbläst und platzt Ich habe die Tabelle so entworfen, dass sie funktioniert , wenn der Feind ein Leben oder neun Leben hat Zum Beispiel springen wir einfach Bild für Bild, wenn wir darauf treffen. Immer wenn wir die Lebenspunkte aufgebraucht haben, spielt es einfach die Frames ab , die noch übrig sind Wenn ich das mache, merke ich, dass wir einen Bug haben Etwas schiebt Frame Gs zurück , als er bereits den Explosionsframe zeigte. Das liegt daran, dass wir hier und auch hier Frame-Eier bewegen. wir sicher, dass sich diese beiden Codezeilen gegenseitig ausschließen. Und sie werden nicht gleichzeitig in derselben Animationsschleife ausgeführt . Codeblock wird nur ausgeführt, wenn der Feind weniger als ein Leben hat. Hier stelle ich sicher, dass diese Linie nur läuft, wenn dieses Leben mehr oder gleich eins ist. Das sollte den Bug beheben. Wenn du zu Zeile 243 gehst, gibst du deinem Tintenfisch-Morph zwei oder fünf oder neun Leben Sie können es testen und sehen, dass die Tabelle in all diesen Fällen funktioniert Ich habe auch eine Bo-Tabelle mit Tintenfisch vorbereitet. Moorphi hat sie zum bestehenden Bo-Sprite hinzugefügt. Du kannst diese neue Version im Abschnitt Ressourcen unten herunterladen . Sie heißt Boss Eight, weil es acht verschiedene Boss-Kreaturen geben wird Ich bringe sie in das Projekt ein. Hier ist Boss acht, Hund, und ID ist Boss acht. Wir haben acht verschiedene Boss-Grafiken in dieser Tabelle. Acht verschiedene Reihen. Ich verstecke es mit CSS. Ich gehe in die Klasse meines Chefs und verweise auf das Bild hier in Zeile 347 Jetzt kann ich Frame Y auf einen zufälligen Wert setzen, 0-7 So, ich habe es auskommentiert, nur um die Tabelle zu testen Ich werde Frame Y auf Null setzen Hier unten möchte ich, dass die Wellen mit einem Boss beginnen, um das Testen zu vereinfachen I Sat Frame X20, Frame Y Zero ist diese Version des Mantis-Morphs Zeile eins ist dieser Frame Y, zwei geben uns diese Zeile. Bild Y drei ist dieser Frame Y vier wird uns den neuen Tintenfisch-Morph geben Sie können sehen, wie er reagiert, wenn er von einer Sattelanimation seiner beiden vorderen Tentakel getroffen wird einer Sattelanimation seiner beiden vorderen Tentakel Genau wie die anderen Bosse von zuvor. Wenn wir seine Lebensdauer aufgebraucht haben, spielt es einfach den Rest der Tabelle in einer schönen Explosionsanimation mit niedriger Bildrate Bild sechs wird uns diese seltsame Weltraumkreatur geben. Beachten Sie, dass sie alle jedes Mal ein wenig animiert werden, wenn wir sie mit unserem Basisangriff treffen . Frame Y 7 gibt uns diesen Weltraum-Tintenfisch Wir wissen, dass alles funktioniert. Also werde ich Frame Y auf einen zufälligen Wert von 0-7 setzen und uns zufällig einen dieser acht Bosse hier unten geben zufällig einen dieser acht Bosse hier unten Ich möchte, dass mein Spiel mit einer feindlichen Welle beginnt. Ich möchte, dass jede Welle zu 25% aus Tintenfisch-Morphen und zu 25% aus Nashorn-Morphen besteht. Ego-Morph ist eigentlich sehr Wir machen nur 20% der Welle aus, und die letzten 30% werden aus Beatle-Morphen bestehen, dem einfachen Grundfeind. Wenn du dieselben Einstellungen verwendest, die ich für dieses Spiel verwendet habe, versuche es so lange wie möglich zu spielen und teile mir deinen Punktestand in den Es kann im Laufe des Spiels ziemlich schwierig und anstrengend werden Spiels ziemlich schwierig und anstrengend Und sowohl die Gesundheit als auch die Größe der Wellen nehmen weiter zu. Nun, wenn du es geschafft hast, so weit zu kommen, war das ein großes Projekt und wir haben so viele verschiedene Tricks und Techniken gelernt . Wenn du Spaß beim Programmieren mit mir hattest, kannst du dir meine anderen Kurse ansehen. Wir sehen uns später. 29. Feind-Klasse mit Lobstermorph: Analyse, erfolgreicher Feindkontakt bestätigt. Es scheint, dass unsere Laser bei dieser Spezies eine Zellmitose auslösen dieser Spezies eine Zellmitose Ganz eigenartig, was heißt das? Wenn wir sie erschießen, teilen sie sich in mehrere kleinere Lebewesen Ah, also müssen wir sie zuerst aufteilen und dann die Klone ins Visier nehmen Ja, das scheint die wirksamste Strategie zu sein. Lasst uns teilen und herrschen. Ich habe andere Projekte mit Kunstobjekten gemacht , die mit diesem Spiel kompatibel sind. Lassen Sie mich Ihnen zeigen, wie Sie ein Spritesheet aus diesem kompletten Spiel nehmen und es hier verwenden können. Es ist sehr einfach Sie können die Tabelle im Abschnitt Ressourcen unten herunterladen . Ich habe das Bild in meinen Projektordner gelegt und verweise hier darauf. Die Quelle ist Hummer. Moorphong ID wird Lobster Morph sein. Wir werden das Bild hier mit CSS verstecken , wir haben bereits die gesamte Logik eingerichtet Ich kann einfach diesen Codeblock kopieren, den wir für Squidmorph verwendet haben. Max Frame wird 14 Leben sein, acht, solange es mehr als eins gibt Jedes Mal, wenn wir den Gegner treffen, vergrößern wir den Frame in der Tabelle um eins, langsam voranschreitet Dieser Gegner teilt sich in mehrere kleinere Klone mehrere kleinere Klone Jetzt müssen wir nur noch entscheiden, welcher Teil der gesamten Feinde aus diesem neuen Feindtyp bestehen soll Ich erstelle einen neuen L-if-Block. Auch hier bin ich sehr vorsichtig den Klammern, da einzelne falsche Klammer meinen Code beschädigen kann. Sie können Ihre eigenen Werte wählen. Hier haben wir fünf verschiedene Feindtypen. Ich denke, es ist angemessen , jeden Feindtyp in etwa 20% der Welle, in der wir es zu etwa 20% der Welle, in der tun haben, mit Zufallszahlen zu versehen. Manche Wellen werden mehr Hummer-Morphen haben als andere. Ich teste es, es mutiert. Jedes Mal, wenn ich es treffe, teilt es sich in Klone auf, Ich habe ein anderes Spiel gemacht, das sich mehr auf diesen speziellen Weltraumfehler konzentriert Wenn du interessiert bist, sehe ich dich in der anderen Klasse, in der wir ein planetarisches Verteidigungsspiel entwickeln. Wenn es mehrere Feindtypen wie diesen gibt, ist es interessanter, das Spiel zu erkunden. Und wir fügen hier die Feinde hinzu, damit der Spieler ein wenig nachdenken und Strategien entwickeln Je schwieriger das Spiel wird, desto mehr müssen wir Ziele priorisieren, verschiedene Waffentypen einsetzen und uns gut positionieren Und das alles erreichen wir , indem wir eine Vielzahl von Gegnertypen mit unterschiedlichen Eigenschaften erschaffen . Das hat Spaß gemacht. 30. Projekt 2: JavaScript Planet Defense: Jedes Spiel besteht aus ein paar Grundbausteinen. Sobald Sie diese Techniken verstanden haben, können Sie kreativ werden, diese Elemente auf unterschiedliche Weise kombinieren und auf einfache Weise viele einzigartige Spiele erstellen. In diesem Kurs werden wir viele davon behandeln. Von Tastatur- und Maussteuerungen bis hin zu leistungsfreundlichen Tipps und Tricks für Sprite-Animationen Und das Hauptaugenmerk liegt diesmal auf dem Entwurfsmuster für Objektpools Wir werden es für Feinde und für Spielerprojektile verwenden. Weil es ein enormer Leistungsvorteil sein kann, unsere Objekte wiederzuverwenden, anstatt sie immer wieder zu erstellen und zu verwerfen zu verwerfen Heute entwickeln wir ein animiertes 2-D-Spiel komplett von Grund auf neu, ohne Frameworks und ohne Bibliotheken, indem wir nur einfaches Java-Skript und HTML-Five-Canvas-Analyse und HTML-Five-Canvas-Analyse Erfolgreicher Feindkontakt bestätigt. Offenbar lösen unsere Laser bei dieser Spezies eine ganz besondere Zellmitose Was bedeutet das? Wenn wir sie erschießen, teilen sie sich in mehrere kleinere Lebewesen Ah, also müssen wir sie zuerst aufteilen und dann haben wir es auf die Klone abgesehen Ja, das scheint die wirksamste Strategie zu sein. Lasst uns teilen und herrschen. Das Verhör dreht sich um Wir kontrollieren buchstäblich ein Raumschiff und müssen den Planeten vor Asteroiden und allen Arten von Außerirdischen und Weltraumwanzen schützen Asteroiden und allen Arten von Außerirdischen und von Dieses Projekt ist völlig eigenständig und unabhängig, aber ich habe die Grafikelemente so entworfen, dass sie mit dem Space Invaders-Spiel kompatibel sind mit dem Space Invaders-Spiel kompatibel Und am Ende werde ich Ihnen zeigen, wie Sie Spritesheets aus diesem Spiel nehmen und sie mit nur ein paar zusätzlichen Codezeilen in dieses Projekt implementieren mit nur ein paar zusätzlichen Codezeilen in dieses Projekt Dieser Kurs richtet sich an kreative Programmierer, die etwas über die Grundlagen der Entwicklung von Zwei-D-Spielen lernen möchten, sowie an Javascript-Anfänger, die ihre objektorientierten Programmierkenntnisse an einem unterhaltsamen visuellen und interaktiven Projekt üben ihre objektorientierten Programmierkenntnisse an einem unterhaltsamen visuellen und Gehen wir wie gewohnt vor, wir beginnen mit einer normalen generischen leeren Webseite wir ein CSS-Stylesheet verknüpfen, erstellen ein HTML-Five-Canvas-Element mit der ID Canvas HTML-Five-Canvas-Element mit der ID Und wir verknüpfen eine JS-Skriptdatei im CSS-Stil. Ich gebe der Leinwand einen Rahmen, lege ein PNG-Hintergrundbild in meinen Projektordner und verwende es als Hintergrund für meine Leinwand. Sie können alle Projektgrafiken im Abschnitt „Ressourcen“ unten herunterladen . Ich möchte, dass sich die Leinwand sowohl vertikal als auch horizontal in der Mitte der Seite befindet. Also verwende ich die Technik der absoluten Positionierung. Zum Beispiel wird alles andere mit Javascript gemacht. In der JS-Skriptdatei werden wir Bilder und andere Ressourcen in diesem Projekt verwenden. Wir müssen warten, bis alle Assets vollständig geladen und verfügbar sind, bevor wir Javascript-Code ausführen , der von ihnen abhängt. Aus diesem Grund werde ich meinen Code in eine Callback-Funktion auf einem Ladeereignis-Listener einfügen eine Callback-Funktion auf einem Ladeereignis-Listener Wenn das Ladeereignis ausgelöst wird und alle Ressourcen bereit sind, werden wir einen Javascript-Code ausführen Zuerst richte ich Javascript anhand seiner ID auf mein Canvas-Element. Ich habe den Kontext auf zwei D gesetzt. Wie üblich müssen wir immer diese Zeile machen. Wenn wir Canvas einrichten, machen wir ein Zwei-D-Spiel. Heute habe ich die Leinwand hier auf 800 mal 800 Pixel eingestellt, wodurch das gesamte Hintergrundbild sichtbar wird. 31. L2-Planeten- und Spielkurs: Ich kann meine Planetenklasse hier oben definieren, außerhalb des Event-Listeners Solange ich sicherstelle, dass ich diese Klasse nur innerhalb des Load-Event-Listeners instanziiere dass ich diese Klasse nur innerhalb des Load-Event-Listeners instanziiere Lassen Sie mich Ihnen später zeigen, dass alle Objekte in diesem Projekt XY-Koordinaten und Radius benötigen , weil alles ein Kreis sein wird und wir Kollisionen zwischen ihnen erkennen werden Kollisionen zwischen ihnen erkennen Draw-Methode verwendet den Kontext als Argument, um anzugeben , auf welcher Leinwand wir zeichnen möchten, und um unsere Klassen so eigenständig wie möglich zu halten unsere Klassen so eigenständig wie möglich Im Inneren zeichnen wir einen Kreis, der den Planeten darstellt. Wir übergeben die XY-Koordinaten des Mittelpunktradius des Kreises nach der Bogenmethode des Mittelpunktradius des Kreises nach der Startwinkel der Kreisform, der Pfad und Winkel, in dem die Methode Pythons zu einem vollen Kreis verlaufen soll Im Bogenmaß rufen wir die Füllmethode auf, um den Pfad mit der Standardfarbe Schwarz zu füllen den Pfad mit der Standardfarbe Im Listener für Ladeereignisse. Ich instanziiere die Klasse mit dem neuen Schlüsselwort. Jetzt kann ich diese Planetenvariable verwenden , die eine Instanz der Planetenklasse enthält Ich kann die Draw-Methode aufrufen, die wir in Zeile 7 definiert haben. Sie erwartet Kontext. Ich übergebe es CTX von Zeile 15. Wir haben unseren Planeten hier an den Koordinaten 200, 200 gezeichnet. Ich kann seinen Radius ändern, ich kann ihn stattdessen streicheln. Lassen Sie uns den Strichstil auf Weiß und vielleicht die Linienbreite auf zwei Pixel setzen. Wir können es verschieben und seine Größe ändern, indem wir diese Werte anpassen Das ist unsere Planetenklasse. Ich möchte eigentlich eine Hauptspielklasse haben, das Gehirn der Codebasis. werden wir alles zeichnen, aktualisieren Von hier aus werden wir alles zeichnen, aktualisieren und kontrollieren. Der Konstruktor erwartet Canvas als Argument. Im Inneren konvertieren wir es in eine Klasseneigenschaft. Aus dieser Eigenschaft werden wir Breite und Höhe extrahieren. Weil ich möchte, dass die Eigenschaften Breite und Höhe unseres Hauptspielobjekts der Breite und Höhe des eigentlichen Leinwandelements entsprechen auf dem wir das Spiel zeichnen und animieren werden Anstatt auf diese Weise eine Variable namens Planet zu erstellen und die Planetenklasse zu instanziieren , werde ich hier eine Eigenschaft namens diese erstellen Die Planet Game-Klasse wird auch eine Render-Methode haben. Diese Methode wird für jeden Animationsframe immer wieder aufgerufen . Beim Aktualisieren und Neuzeichnen unserer Formen und Objekte benötigen wir den Kontext als Argument. Ich nehme diese Planeteneigenschaft aus Zeile 19, die jetzt eine Instanz der Planetenklasse enthält Und ich rufe die zugehörige Draw-Methode ab Zeile 7 übergebe das Kontext-Argument so weiter. Jetzt nehmen wir diese Planetenklasse und instanziieren sie im Spielklassenkonstruktor Ich kann diese Zeilen löschen und stattdessen werde ich eine Instanz von Game Class erstellen Es erwartet Canvas als Argument. Ich übergebe die Leinwand von Zeile 27 bis dahin. Das Spiel hat nun Zugriff auf die Breiten - und Höheneigenschaften der Leinwand. Breite und Höhe des Spiels werden automatisch auf 800 mal 800 Pixel gesetzt. Ich kann diese Spielvariable aus Zeile 34 nehmen und daraus die Render-Methode aufrufen. Wir haben es in Zeile 21 definiert und wir können sehen, dass es Kontext als Argument erwartet. Ich übergebe es CTX von Zeile 28. Es ist wichtig, hier zu verstehen, dass der Code in einem Klassenkonstruktor einmal ausgeführt wird , wenn wir eine Instanz dieser Klasse mit dem neuen Schlüsselwort erstellen Wenn ich hier in Zeile 34 eine Instanz der Spielklasse erstelle, wird auch automatisch der gesamte Code Zeile für Zeile ausgeführt und eine Instanz der Planet-Klasse wird erstellt und als diese Planeteneigenschaft hier in Zeile 19 gespeichert als diese Planeteneigenschaft hier in Zeile 19 Ich mache es gerne so. Ich denke, es ist sauberer und einfacher zu navigieren. Wenn wir unseren Code so in Klassen und Objekten strukturieren so in Klassen und , nennt man das objektorientierte Programmierung. Das gesamte Projekt wird eine einfache objektorientierte Codebasis sein. Jetzt ist die Planet-Klasse mit der Spielklasse verbunden. Und ich kann immer noch die Eigenschaften des Planeten ändern , indem ich einen dieser Werte anpasse. dieselben Eigenschaften kann jetzt auch innerhalb des Spielobjekts über diese Planeteneigenschaft zugegriffen werden, was später wichtig werden wird. Was ist, wenn ich möchte, dass diese Verbindung in beide Richtungen erfolgt? Ich möchte auch , dass die Eigenschaften, die sich in der Spielklasse befinden , der Planetenklasse aus zugänglich sind. Tun Sie das, indem Sie eine Referenz , die auf das Hauptspielobjekt zeigt, wie folgt an den Planetenklassenkonstruktor übergeben das Hauptspielobjekt zeigt, wie folgt an den Planetenklassenkonstruktor Sie daran, dass Objekte in Java-Skript sogenannte Referenzdatentypen sind Auf diese Weise erstelle ich hier keine Kopie der Spielklasse. Ich erstelle nur eine Referenz, die auf einen Bereich im Speicher verweist , in dem sich das Hauptspielobjekt befindet. Wenn Eigenschaften, die sich auf dem Hauptspielobjekt befinden, ihre Werte aktualisieren, werden diese Änderungen sofort innerhalb der Planetenklasse sichtbar sein . Der Klassenkonstruktor erwartet hier einen Verweis auf das Spiel als Argument, wo ich den Planetenkonstruktor nenne, ich muss ihm diese Spielreferenz übergeben Da wir uns in dieser Spielklasse befinden , übergebe ich ihr dieses Schlüsselwort, das, wenn es hier verwendet wird, das gesamte Spielobjekt darstellt Jetzt haben wir eine nette und einfache bidirektionale Verbindung zwischen Planeten und Spielklassen. Wir können es testen, indem wir vom Inneren des Planeten aus auf die Breiteneigenschaft von Zeile 18 zugreifen . Weil ich möchte, dass sich der Planet mit Tams 0.5 genau in der Mitte des Spiels befindet. Falls dir das, was wir hier behandeln, zu schnell oder zu kompliziert ist, sieh dir die vorherige Klasse an, in der ich in einem anfängerfreundlichen Tempo viel langsamer gehe in einem anfängerfreundlichen Tempo viel langsamer Für den Rest dieses Projekts gehe ich davon aus, dass Sie die Grundlagen von Javascript und objektorientierter Programmierung bereits kennen Javascript und objektorientierter Programmierung Sie können das Planetenbild im Abschnitt Ressourcen unten herunterladen . Ich bringe es als IMG-Element in das Projekt ein. Hier im Index-HTML gebe ich ihm eine Planet-ID. Wir können das Bild hier sehen. Ich möchte es verstecken. Wir werden es zeichnen, aber mit Javascript auf Canavas ist das nicht so Das Projekt wird mehr Bilder enthalten. Ich werde sie alle in ein Diff mit einer Klasse von Hidden in CSS einfügen . Ich habe dieses versteckte Diff so eingestellt, dass keines angezeigt wird. Großartig, wir werden es hier als Bildeigenschaft der Planetenklasse in das Projekt aufnehmen. Jetzt kann ich die eingebaute Draw-Image-Methode aufrufen. Und ich übergebe ihr das Bild, das wir zeichnen wollen , und die X- und Y-Koordinaten, wobei X und Y eines Kreises den Mittelpunkt darstellen , um den der Kreis gezogen wird. Aber bei Rechtecken und Bildern auf der Leinwand definieren X- und Y-Koordinaten die obere linke Ecke Wenn ich das rechteckige Bild über dem kreisförmigen It-Feld ausrichten möchte, würde ich es normalerweise um das rechteckige Bild über dem kreisförmigen It-Feld ausrichten möchte, den Radius verschieben, der für alle anderen Objekte in diesem Spiel genau die Hälfte seiner Breite beträgt Aber für das Planetenbild habe ich es etwas größer gemacht , weil es von Wolken umgeben ist. Und ich möchte nicht wirklich, dass diese Wolken bei Kollisionen aktiv Feinde und Asteroiden werden nur dann mit dem Planeten kollidieren, wenn sie auf den eigentlichen Körper des Planeten treffen, nicht auf die Aus diesem Grund muss ich es etwas mehr ausgleichen. Sagen wir um 100 Pixel. Jetzt haben wir den kreisförmigen Planeten an der Box über dem rechteckigen Bild ausgerichtet . Wir behalten den weißen Kreis vorerst bei. Es wird den Kollisionsbereich des Planeten darstellen. 32. L3-Maus-Position: Wie bereits erwähnt, wird jeder Code innerhalb eines Klassenkonstruktors automatisch ausgeführt, wenn wir eine Instanz dieser Klasse mit dem neuen Schlüsselwort erstellen eine Instanz dieser Klasse mit dem neuen Schlüsselwort Ich nutze das bereits und erstelle automatisch eine Instanz von Planet Class zu dem Zeitpunkt, an dem ich eine Instanz von Game Class erstelle Zu dem Zeitpunkt, an dem wir ein Spiel mit dem neuen Schlüsselwort erstellen, möchte ich auch, dass einige Event-Listener angewendet werden Wir warten normalerweise auf das Mousemove-Ereignis Normalerweise würden wir mit Event-Listenern eine Standard-Callback-Funktion und etwas Code erstellen , der jedes Mal ausgeführt wird, wenn ein Mousemove-Ereignis ausgelöst wird jedes Mal ausgeführt wird, wenn ein Mousemove-Ereignis Die Herausforderung dabei besteht darin, dass ich den Event-Listener in einen Konstruktor Event-Listener in einen Konstruktor Wir befinden uns in der Spielklasse und rufen den Event-Listener vom Fensterobjekt aus mit einer regulären Wir würden den Zugriff auf diese Punkteigenschaften verlieren , die sich in der Spieleklasse befinden, um diesen Zugriff beizubehalten, um sicherzustellen, dass dieser Event-Listener sich daran erinnert, dass er innerhalb der Spielklasse lexikalisch definiert wurde , und um uns zu ermöglichen, diese Eigenschaften in der Spielklasse von einer Callback-Funktion auf diesem Mousemove-Event-Listener aus zu manipulieren diese Eigenschaften in der Spielklasse von einer Callback-Funktion auf diesem Mousemove-Event-Listener aus , wir Einfacher können wir hier die ES-Six-Pfeilfunktionen im Steuerbereich verwenden. Da eine der Besonderheiten von Pfeilfunktionen in Java-Skript darin besteht, dass sie diese Korrektur automatisch aus dem übergeordneten Bereich erben, wird das automatisch generierte Event-Objekt als Argument verwendet Ich gebe ihm einen Variablennamen. Da diese Funktion beispielsweise nur ein Argument hat, sind die Klammern nicht obligatorisch. Ich kann sie so löschen. jetzt das Mousemove-Ereignis ausgelöst wird, wird ein automatisch generiertes Ereignisobjekt Lassen Sie uns dieses Objekt mit einem Consolog versehen, damit wir es untersuchen können da wir den Event-Listener in den Konstruktor der . Der Event-Listener wird automatisch angewendet, wenn diese Codezeile ausgeführt wird, wenn eine Instanz von Game Class mit dem neuen Schlüsselwort erstellt wird mit dem neuen Schlüsselwort erstellt Jetzt bewege ich die Maus über die Leinwand und sehe diese Objekte jedes Mal, wenn ein Mausbewegungsereignis stattfindet Wenn ich das Mausereignisobjekt öffne, sehe ich, dass es alle Arten von Informationen über das Mausereignis enthält , das gerade passiert ist. Was ich benötige, sind die X- und Y-Koordinaten der Maus. Wir können diese Eigenschaften hier sehen. Ich möchte, dass diese Mauskoordinaten meiner gesamten Codebasis verfügbar sind, nicht nur im mousemove-Event-Listener Ich werde dieses Punkt-Maus-Objekt in der Hauptspielklasse erstellen der Hauptspielklasse ihm X - und Y-Eigenschaften geben Wir verwenden hier die ES-Sechs-Pfeil-Funktion, sodass wir diesen Punkt innerhalb der Callback-Funktion verwenden können Ich kann tatsächlich diesen Punkt, den Mauspunkt X aus Zeile 25 nehmen Mauspunkt X aus Zeile 25 Und ich kann ihn gleich x für die aktuelle horizontale Ausrichtung der Maus setzen x für die aktuelle horizontale Ausrichtung der Maus Ich mache dasselbe auch für die vertikale Y-Koordinate. Okay, wenn wir die Maus über die Leinwand bewegen, nehmen wir die X- und Y-Koordinaten aus dem Ereignis und weisen sie unserer benutzerdefinierten Punkt-Maus-Eigenschaft zu, um sie im gesamten Projekt verfügbar zu machen Wir können testen, wie genau diese Koordinaten sind, indem eine Linie von der Mitte des Planeten zur Maus ziehen des Planeten zur Maus Wir wollen eine einfache Linie ziehen. Also beginnen wir mit einem Bettelpfad. Methode Move Two definiert die X- und Y-Startkoordinaten der Linie Es wird die Mitte des Planeten sein. Die Methode Zeile zwei definiert die Endkoordinaten. Es wird die aktuelle Mausposition sein. Jetzt streichen wir diese Linie, um sie tatsächlich auf die Leinwand zu zeichnen. Die Linie reagiert nicht , weil wir die Render-Methode beim Laden der ersten Seite nur einmal aufrufen . Wir wollen sie für jeden Animationsframe immer wieder aufrufen , um eine Bewegung zu sehen. Lassen Sie uns eine einfache benutzerdefinierte Animationsschleifenfunktion erstellen. Ich rufe Animate an. Wir haben diesen Render-Call eingebaut. Dann rufen wir die integrierte Animationsframe-Methode für Anfragen , die sich auf dem Fensterobjekt befindet. Aber dieser Teil ist optional. Ich kann ihn löschen. Javascript weiß, wo diese Methode zu finden ist, ohne dass wir ihr sagen müssen , dass sie sich im Fensterobjekt befindet. Ich übergebe es animate , um die Schleife zu erstellen. Ich rufe es einmal beim Laden der ersten Seite so auf , um die erste Schleife tatsächlich zu starten ich jetzt die Maus bewege, läuft die Render-Methode und zeichnet eine Linie vom Planeten zur Maus, wie wir sie in den Zeilen 38.39 definiert haben . Es sieht so aus, weil wir alte Farbe sehen Wir sehen alle vorherigen Animationsframes. Wir wollen eigentlich nur den aktuellen Animationsframe sehen. Ich lösche den gesamten Inhalt der Leinwand zwischen jedem Frame, indem ich die integrierte Methode mit durchsichtigem Rechteck verwende. Wir löschen die gesamte Leinwand von den Koordinaten 00 bis zur Breite und Höhe des Leinwandbereichs. Jetzt animieren wir die Linie und können überprüfen, ob die Koordinaten, die wir vom Mausereignis erfassen , mit unseren Spielobjekten übereinstimmen Jetzt ist alles gut, aber sieh dir an, was passiert, wenn ich das Fenster vergrößere, weil die Leinwand nicht im Vollbildmodus angezeigt wird Diese X- und Y-Koordinaten berücksichtigen nicht den Leerraum zwischen der Webseite und dem Canvas-Element. Plötzlich sind die Koordinaten völlig falsch. Die Linie geht nicht mehr vom Planeten zur Maus. Wir können diese Mauskoordinaten ausgleichen, indem den weißen Bereich auf der Leinwand manuell oder noch einfacher berücksichtigen. Maus-Event-Objekt erledigt das bereits automatisch für uns. Es gibt uns die angepassten Koordinaten. Anstatt diese X- und Y-Eigenschaften zu verwenden, werden wir von hier aus Offset X und Offset Y verwenden. Ersetzen Sie sie hier. Jetzt verläuft die Linie genau von der Maus bis zur Mitte des Planeten. Wir wissen, dass wir die richtigen Mauskoordinaten erfassen. Perfekt. Die Eigenschaften Offset X und Offset Y sind nützlich für Projekte, bei denen Ihre Leinwand nicht im Vollbildmodus angezeigt wird. Ich lösche den Consolog. 33. L4-Player-Raumschiff: Zeit, die Spielerklasse zu erstellen. Ihre Aufgabe wird es sein, die Grafik, das Verhalten und die Steuerung des Spielers zu verwalten , das Verhalten und die Steuerung des Spielers Ich bin mir nicht sicher, ob ich das klargestellt habe, aber wir haben bereits eine vollständig animierte Codebasis Ich animiere und bewege den Planeten nicht, aber das kann ich schon Wenn ich mir das ansehen möchte, wissen wir, dass die Render-Methode für jeden Animationsframe immer wieder ausgeführt wird für jeden Animationsframe immer wieder Ich kann die horizontale Position des Planeten um ein Pixel pro Bild erhöhen und wir bekommen Bewegung. Diese Codebasis ist bereits vollständig bereit für alle Arten von Animationen. Ich bewege die Dinge einfach noch nicht. Ich kann den Planeten horizontal bewegen. Ich kann ihn vertikal nach unten und oben bewegen. Es sieht jetzt einfach statisch aus, weil ich noch keine Dinge verschiebe. Wir werden bald dort sein. So wie wir es mit der Planeten-Klasse gemacht haben. Ich benötige Zugriff auf Eigenschaften und Methoden, die zur Spielklasse gehören. Aus der Spielerklasse erstelle ich eine Eigenschaft, eine Referenz, die auf das Hauptspielobjekt zeigt. Wie bereits erwähnt, müssen alle aktiven Objekte in unserem Spiel X- und Y-Koordinaten und einen Radius haben. Damit wir diese Werte später für die Kollisionserkennung zwischen Kreisen verwenden können . Außerdem werden ab sofort alle Objekte in dieser Codebasis einen Radius von 40 Pixeln haben. Der Radius ist ein Halbkreis. Die rechteckigen Sprite-Frames werden 80 mal 80 Pixel groß sein. Kunstwerke aus diesem Projekt sind vollständig kompatibel mit dem Hauptspiel Space Invaders, das wir zuvor entwickelt haben sie gerne mitnehmen und auch in diesem Projekt verwenden Wenn Sie möchten, zeige ich Ihnen in einer Bonusstunde zu diesem Projekt auf unterhaltsame Weise , wie das geht. Ich bin mir sicher, dass einige von Ihnen es herausfinden können. Auch ohne meine Hilfe könnte es Spaß machen, den einzigartigen Feindtyp, den wir in diesem Projekt implementieren werden , auch dem vorherigen Projekt hinzuzufügen in diesem Projekt implementieren . Nur als Übung habe ich dafür gesorgt, dass alles kompatibel ist. Wie üblich gibt es für den Spieler eine Ziehmethode und wir zeichnen einen Kreis, der den Spieler repräsentiert. Immer wenn wir einen Kreis zeichnen, müssen wir mit dem Startpfad beginnen. Wir verwenden Methode, um den Pfad abzubilden. Wir zeichnen es tatsächlich mit der eingebauten Strich - oder Filmmethode auf Leinwand . Das ist die Spielerklasse. Wir werden einfach das Muster wiederholen, das wir mit dem Planeten gemacht haben. Ich instanziiere es als diese Player-Eigenschaft. Hier rufen wir die Player-Draw-Methode auf. Wir haben in Zeile 24 Inside Render so definiert, dass sich der Spieler bewegt. Ich benötige eine Aktualisierungsmethode wenn ich möchte, dass sich der Planet auf irgendeine Weise bewegt, zum Beispiel rotiert. Ich müsste ihm auch eine solche Aktualisierungsmethode geben. Lassen Sie uns vorerst einfach die horizontale Ausrichtung des Spielers um eine Auswahl erhöhen die horizontale Ausrichtung des . Siler-Animationsframe Nur ein Test, ich nenne die Aktualisierungsmethode, die wir gerade definiert haben. Von hier aus bewegt sich der Spieler, weil Rendervorgang aus der Animationsschleife in Zeile 75 immer wieder aufgerufen wird . Wir könnten auch die horizontale Position des Spielers an die Maus anhängen . Auf diese Weise möchte ich, dass sich der Spieler um den Planeten dreht und immer in die Richtung der Maus schaut. Dieser Teil wird für Anfänger etwas schwieriger zu implementieren sein , aber sobald wir ihn zum Laufen gebracht haben, wird der Rest des Spiels einfach sein. Zuerst werde ich das Spielerbild in das Projekt einbringen. Sie können es im Bereich Ressourcen herunterladen. Im Folgenden gebe ich der Spielerklasse diese Bildeigenschaft und habe auf das neue Bild hingewiesen , das wir gerade hinzugefügt haben. Ich nenne es die Built-in-Draw-Image-Methode. Und ich habe das Bild und die X- und Y-Koordinaten übergeben. Wenn wir nun den Spieler bewegen bewegen sich sowohl Kreis als auch Bild auf diese Weise zusammen. Ich möchte das rechteckige Bild so verschieben, dass es direkt über dem kreisförmigen Trefferfeld liegt. Ich versetze X und Y um die Hälfte der Spielerbreite und Höhe um den Radiuswert. Ich habe alle Grafik-Assets für dieses Projekt in bestimmten Größen vorbereitet , die wir im Spiel benötigen werden. Das erspart mir jetzt beim Programmieren eine Menge Arbeit, weil die Bilder sofort einsatzbereit sind und ich nichts mit Code skalieren oder in der Größe ändern muss, was ohnehin verschwenderisch wäre, was die Leistung angeht 34. L5-Math: Ich möchte den Spieler auf dem Planeten bewegen, je nachdem , wo sich die Maus gerade im Verhältnis zum Planeten befindet. Wir werden all diese Logik in einer wiederverwendbaren Methode schreiben. Ich nenne zum Beispiel rechnen Ich mache es so. Wir werden diese Methode später wiederverwenden können, damit Feinde sich dem Planeten zuwenden, wenn sie uns angreifen. Die Methode verwendet Objekt A und Objekt B als Argumente und berechnet eine Vektorrichtung zwischen diesen beiden Objekten. Nehmen wir an, wir haben zwei Objekte , die die X- und Y-Position haben. Zum Beispiel der Mittelpunkt des Planeten und der Mauszeiger. Diese beiden Objekte befinden sich irgendwo im Spielbereich. Wir wollen den Abstand zwischen diesen beiden Objekten auf der horizontalen X-Achse berechnen . Zuerst wird es die horizontale Position von Objekt A minus der horizontalen Position von Objekt B Dasselbe machen wir für die vertikale Achse. Jetzt haben wir zwei Seiten eines imaginären rechtwinkligen Dreiecks. Und wir wollen den Abstand zwischen diesen beiden Punkten berechnen , in diesem Fall zum Beispiel zwischen Planet und Maus Wir berechnen eine Entfernung zwischen zwei Punkten in einem Raum mit zwei D. Wir müssen den Hypotenus dieses imaginären rechtwinkligen Dreiecks berechnen dieses imaginären rechtwinkligen Dreiecks Der rechte Winkel wäre hier. Das ist der Hypotenus des rechtwinkligen Dreiecks. Die längste Seite gegenüber dem rechten Winkel. Dies ist ein perfekter Anwendungsfall für Formel des Satzes von Pythagoras, die im Javascript-Code aussehen würde Aber da wir Javascript verwenden, ist es noch einfacher, weil wir eine eingebaute Methode zur Hypotenose zur Verfügung haben eingebaute Wir müssen es nur auf dieser und dieser Seite übergeben, und es gibt uns die Länge des Hypotenus, was in diesem Fall der tatsächliche Abstand zwischen Objekt A und Objekt B ist . Wir haben hier als Argumente übergeben Jetzt brauche ich nur noch zwei sehr kleine Zahlen, die in eine bestimmte Richtung zeigen, vertikal und horizontal Und die Kombination dieser beiden Werte gibt uns einen Richtungsvektor zwischen Objekt A und Objekt B. Wir werden diesen verwenden, damit das Raumschiff des Spielers vom Mittelpunkt des Planeten weg vom Mittelpunkt des in Richtung Mauszeiger zeigt. Ich werde meine Vektoren auf einen Wertebereich zwischen minus eins und plus eins reduzieren, Ich werde meine Vektoren auf einen Wertebereich zwischen minus eins und plus eins reduzieren indem ich das Verhältnis zwischen x und Hypotenus überprüfe das Verhältnis zwischen x und Hypotenus Entfernung, das ist die horizontale Richtung weil die Entfernung zum Hypotenus immer größer als x ist. Weil die Maus links oder rechts vom Planeten sein kann . Der resultierende Wert liegt hier irgendwo zwischen minus eins und plus eins, aber er kann nie mehr als eins sein, weil Dx immer kleiner ist als Entfernung kleiner als Hypotenus Denken Sie daran, dass sich die Maus rechts vom Planeten befinden kann, aber zum Beispiel auch links Aus diesem Grund kann der resultierende Verhältniswert auch negativ sein Jedenfalls haben wir jetzt alles, was wir brauchen. Wir wissen, in welche Richtung wir ein Objekt bewegen müssen, wenn wir es zu einem anderen Objekt oder von einem anderen Objekt weg bewegen wollen . mx und meine Werte sagen uns, mx und meine Werte sagen uns dass bei horizontalem und vertikalem Drücken diese Methode calc Am ein Array von Werten zurückgibt , weil wir mehrere Werte benötigen Um nun die Spielertrefferbox zu positionieren und das Spielerbild dann korrekt zu drehen, müssen wir uns merken, in welcher Reihenfolge wir diese Werte zurückgeben Wir wissen, dass, wenn wir Calc Am aufrufen, ein Array zurückgegeben wird, ein Array zurückgegeben wird bei dem das Element mit dem Index Null die horizontale Richtung zwischen Objekt A und Objekt B ist zwischen Objekt A und Objekt B Wert mit dem Index Eins ist die vertikale Richtung Der vertikale Vektorindex zwei ist der horizontale Abstand zwischen a und B. Index drei ist D, Y, der vertikale Abstand zwischen Objekt A und Objekt B. Ich weiß, das war eine Menge Mathe. Entschuldigung, wir sind fast fertig. Wenn Sie diese Technik zum ersten Mal anwenden, sich darüber keine Sorgen. Verfolge einfach , was ich mache. Sie müssen es häufiger verwenden. Für andere Projekte wird es irgendwann für Sie sinnvoller sein. Wiederholung hilft dabei wirklich. Verstehen Sie zunächst, dass wir eine Methode geschrieben haben, uns einen Abstand zwischen zwei Punkten in einem Raum mit zwei D angibt Und wir werden diese Werte nun verwenden, um Objekte aufeinander zu richten und sie aufeinander zu oder voneinander weg zu drehen. Ich möchte den Spieler auf dem Planeten bewegen und dabei immer der Maus zugewandt sein. Ich kann das auf viele Arten umsetzen. Zum Beispiel kann ich dem Spieler diese Eigenschaft Punkt am für jeden Animationsframe geben . Ich werde diesem Punkt eine Eigenschaft die benutzerdefinierte Calc-Am-Methode zuweisen, die wir gerade geschrieben Wir werden sie dazu bringen, diese vier Werte zu berechnen und zu erzeugen und sie als Array zurückzugeben Diese Methode gibt uns den Abstand und die Richtung zwischen zwei Punkten. Also möchte ich diese Werte zwischen Maus und Planet überprüfen . Die relative Position zwischen Maus und Planet bestimmt , wohin der Spieler schaut. Ich konsultiere diesen Punkt am und wir erhalten das Array mit vier Werten, das die Lkm-Methode aus Performancegründen zurückgibt Es wird tatsächlich Sinn machen , nur diesen Punkt zu aktualisieren. Zielen Sie nur, wenn das Mausbewegungsereignis ausgelöst wird. Denn wenn sich die Maus nicht bewegt, berechnen wir immer wieder denselben Wert für jeden Animationsframe neu, was nicht notwendig ist Wie bereits erwähnt, gibt die Methode calc horizontale Richtung, die vertikale Richtung, die horizontale Entfernung und die vertikale Entfernung zurück vertikale Richtung, die horizontale Entfernung , in diesem Fall zwischen Maus Da das die beiden Werte sind, die wir ihr als Objekt A und Objekt B übergeben haben , komme ich hierher, also lösche ich das Consolo Jetzt kann ich diese Werte verwenden, um den Spieler in Richtung Maus zu drehen Die horizontale Position des Spielers beginnt immer in der Mitte des Spielfeldes, was in diesem Fall auch der horizontalen X-Position des Planeten entspricht, dem Mittelpunkt des Planetenkreises. Wir wollen nur diesen kleinen Richtungsvektorwert hinzufügen . Aber weil dieser Wert zwischen minus eins und plus eins sehr klein ist , würden wir die Bewegung kaum sehen. Ich multipliziere es mal, sagen wir 50. Perfekt. Das funktioniert bereits horizontal. Ich kann dasselbe für die vertikale Y-Position tun. Wir gehen vom Mittelpunkt des Planeten zur Maus. Und wir verbessern diesen Vektor erneut, indem wir ihn mit 50 multiplizieren Ja, das funktioniert. Die Zahl der wir hier den Vektor multiplizieren, beeinflusst nun die Entfernung zwischen dem Planeten und dem Raumschiff des Spielers Nehmen wir an, ich möchte, dass die Entfernung dem Radius des Planeten entspricht Wir bekommen eine interessante Bewegung. Wenn die vertikalen und horizontalen Werte nicht so übereinstimmen, wäre es zum Beispiel nützlich, wenn der Planet elliptisch wäre Jetzt bewegt sich der Mittelpunkt des Kreises des Spielers entlang des Radius des Planetenkreises Wenn ich ihn komplett vom Planeten entfernen will, müssen wir ihn um die Hälfte des Spielerkreises plus Spielerradius weiter verschieben . Auf diese Weise müssen wir Klammern verwenden, um sicherzustellen, dass die Addition zuerst erfolgt und dann der resultierende Wert mit dem Vektor multipliziert wird Sie können mit den Werten spielen, wenn Sie mitprogrammieren, und es wird deutlich, was passiert 35. L6-Canvas-Rotation: Wir brauchen diese Leitung nicht mehr. Außerdem wollen wir das Spielerbild so drehen , dass es immer zur Maus zeigt, weg von der Mitte des Planeten. Ich gebe dem Spieler eine Eigenschaft, die ich nenne, zum Beispiel diesen Winkel. Wenn Sie Canvas Rotate aufrufen, wird der Canvas-Status verändert. Alles, was nach dieser Drehung gezeichnet wurde, würde also gedreht werden. Wir wollen nur, dass sich das Bild des Raumschiffs dreht. Ich beschränke alle Änderungen am Canvas-Status , indem ich ihn zwischen den integrierten sicheren Methoden und Wiederherstellungsmethoden Wir speichern den Canvas-Status, wir nehmen hier alle Änderungen vor, dann rufen wir Restore auf und setzen alles zurück, was andere Formen, die danach auf der Leinwand gezeichnet wurden, bleiben davon unberührt Die Drehung der Leinwand ist etwas unintuitiv, wenn Sie sie noch nie zuvor gesehen haben Denken Sie grundsätzlich daran. Um etwas auf der Leinwand zu drehen, müssen Sie zuerst den Drehmittelpunkt verschieben, was den Koordinaten 00 auf der Leinwand entspricht Sie müssen das auf die Koordinaten der Mitte des Objekts übertragen , das Sie drehen möchten Dann nennst du den Wert drehen und übergibst den Winkel, um den du drehen möchtest, im Bogenmaß Dann zeichnen Sie das Objekt, das gedreht werden soll. Aber wir zeichnen dieses Objekt nicht an seinen X- und Y-Koordinaten, sondern an den Koordinaten 00. Weil die tatsächliche Position des Objekts jetzt in den Werten ausgedrückt wird , die wir zur Übersetzung übergeben haben. Ich denke, man kann mit Sicherheit sagen , dass die Rotation der Leinwand viel komplizierter ist als die CSS-Rotation. Aber es ist eigentlich einfach, wenn du es ein paar Mal machst. Lass es uns tatsächlich im Code machen. Ich möchte das Raumschiff des Spielers rotieren lassen. Ich übersetze den Rotationsmittelpunkt über X- und Z-Punkt-Y-Koordinaten des Spielers Der Mittelpunkt der Rotation entspricht ebenfalls den Koordinaten 00 auf der Leinwand. Jetzt übertreibe ich den Spieler zu weit , weil ich seine Position hier durch Translate ausdrücke und ihn noch weiter von dort aus durch Dx-Punkt-Y-Koordinaten verschiebe dort aus durch Dx-Punkt-Y-Koordinaten Das ist vielleicht nicht das beste Beispiel , um jemandem die Rotation beizubringen, weil wir haben hier das Bild des Spielerraumschiffs und auch den Kreis der Trefferbox Es ist also eine Menge los hier. Exposition des Kreises ist Null und die Belichtung des Bildes ist minus Radius Aufgrund des Unterschieds zwischen Rechtecken und Kreisen auf der Leinwand sind X und Y der Mittelpunkt eines Kreises, aber es ist die obere linke Ecke eines Rechtecks oder Bilds, wenn wir Formen auf die Leinwand zeichnen Wie dem auch sei, jetzt haben wir das Problem behoben und sind erfolgreich dabei Rotationsmittelpunkt über dem Spielerraumschiff zu übersetzen über Wenn ich jetzt drehe, dreht es sich von der Mitte des Spielerbilds aus, und das ist genau das, was ich in Canvas haben möchte. Bei der Rotate-Methode wird der Drehwinkel im Bogenmaß angegeben. Ich übergebe ihr diesen Punktwinkel von Zeile 25. Das ist eine Drehung mit der Methode 0 Radiant. Pi wird das Raumschiff auf diese Weise um 3,14 Radiant drehen, was ungefähr 180 Grad entspricht Halbkreismethode Pi mal zwei ergibt 6,28 Wir haben das Raumschiff rundum gedreht. Ein halber Pi entspricht 1,57 Radiant oder etwa 90 Grad, wir zeigen wir zeigen Jetzt hoffe ich, dass es Sinn macht. Der Winkel muss einen Wert von 0-6, 0,28 Radiant, 0-2 Pi haben. Dieser Winkelwert muss der Richtung zwischen dem Mauszeiger und dem Mittelpunkt des Planeten entsprechen , sodass der Spieler zur Maus zeigt Dafür haben wir eine weitere nützliche eingebaute Methode namens Methode A 102, die uns einen Winkel im Bogenmaß gegen den Uhrzeigersinn zwischen der positiven X-Achse und einer Linie gibt Bogenmaß gegen den Uhrzeigersinn zwischen , die von der Koordinate 00 auf einen bestimmten Punkt projiziert wird, auf bestimmte XY-Koordinaten Das Ergebnis als Wert ist eigentlich ein Wert zwischen minus Pi und plus Pi ist wichtig, hier zu beachten, dass wir es zuerst DY und dann x übergeben. Wir geben diese Argumente andersherum weiter. So funktioniert die eingebaute Methode. Wir berechnen diese Werte bereits in der Berechnungsmethode. Sie werden als Werte mit dem Index 2.3 in diesem Array zurückgegeben mit dem Index 2.3 in diesem Array Weil wir Indizes im Array ab 00123 zählen. Ich muss die Calm-Methode nicht noch einmal ausführen. Wir lassen es bereits auf Linie 38 laufen. Ich nehme einfach dieses Ziel, das das zurückgegebene Array enthält. Und ich nehme ein Element mit einem Index von zwei, Element mit einem Index von drei X- und D Y-Werten. Okay, wir können also sehen , dass es funktionieren kann, das ist nützlich für so viele Dinge in der kreativen Programmierung und Spieleentwicklung. Übrigens, ich muss das reparieren, hat mir nur eine Sekunde geraubt, bis ich das herausgefunden habe. Wie wäre es, wenn ich diese beiden Werte vertausche? Trigonometrie kann knifflig sein, okay? Das, wenn ich diese Werte mit minus eins multipliziere, richtig? Gib mir noch eine Sekunde, okay? Ich habe hier einen Fehler gemacht. Ich habe gesagt, dass Mathe 8102 zuerst DY und dann Dx braucht. Aber ja, ich gebe X und DY weiter, ich muss sie austauschen. Ja, das ist genau das , was wir brauchen. Perfekt. Das war eine Menge Mathe. Wenn du es bis hierher geschafft hast, wird der Rest des Projekts einfach sein und Spaß machen. Den schwierigsten Teil haben wir bereits erledigt. Jetzt haben wir unsere wiederverwendbare Calc M-Methode. Und wir können sie immer wieder verwenden, wenn wir einen Abstand zwischen zwei Objekten ermitteln und sie aufeinander richten oder sie voneinander entfernen müssen zwei Objekten ermitteln und sie aufeinander richten oder sie voneinander entfernen Ich werde dir zeigen , wie das geht, wenn wir etwas später Feinde einsetzen. 36. L7-Debug-Modus: Wir definieren hier Event-Listener. Lassen Sie uns einen weiteren Event-Listener implementieren. Diesmal für ein wichtiges Ereignis. Im Inneren konsologe ich das automatisch generierte Ereignisobjekt. Wenn ich jetzt auf Canvas klicke und anfange, auf der Tastatur zu tippen, sehen wir die Taste, die gedrückt wurde Ich kann sagen, wenn die Taste, die gedrückt wurde, ein kleines D ist, setzen Sie diese Back-Eigenschaft auf true. Wir werden das erstellen, hier debuggen. Wenn wir den Buchstaben D drücken, setzen wir den Punkt D auf das gegenüberliegende Ausrufezeichen, Ich tröste diese Punkt-D-Tasche, wenn ich D auf meiner Tastatur drücke. Jetzt schalte ich den Debug-Modus ein und aus. Wenn es wahr ist, wird es mit dem Buchstaben D auf falsch gesetzt. Wenn es falsch ist, wird es auf wahr gesetzt. Dieser Trick ist auch für viele andere Dinge nützlich. Es ist gut, sich diese Technik in der Zeichenmethode der Spielerklasse zu merken . Wir zeichnen das Spielerbild, das Raumschiff und wir zeichnen auch einen Kreis darum Dieser Kreis wird den Kollisionsbereich darstellen. Spieler treffen Box. Manchmal ist es nützlich, das zu sehen, aber ich möchte eine Möglichkeit haben, es ein- und auszublenden. Ich würde sagen, dass man den Kollisionsbereich nur dann um den Spieler herum zeichnen sollte , wenn der D-Bug wahr ist. Wenn ich jetzt immer wieder den Buchstaben D drücke, blende ich die Trefferbox ein und aus. Mit dem Planeten ist es genauso. Wir haben das Planetenbild und breiten Kollisionskreis um ihn herum. Lass uns es nur zeichnen, wenn das Spiel Debuggen wahr ist, also so Auch hier kann ich wiederholt den Buchstaben D auf meiner Tastatur drücken und die Kollisionselemente werden ein - und ausgeblendet Wir können alle Arten von hilfreichen Informationen in unserem Spiel ein- und ausblenden . Auf diese Weise können wir jetzt in unserem Projekt in den Debug-Modus wechseln, was uns bei der Entwicklung des Spiels helfen wird Gleichzeitig können wir diese Elemente einfach ausblenden, um zu sehen , wie das Endergebnis für den Benutzer aussehen wird, wenn die Trefferfelder und möglicherweise einige andere unterstützende Elemente ausgeblendet sind 37. L8-Objekt-Pool: Das sieht langsam ziemlich gut aus. Wir haben einen Planeten, einen Spieler , der sich um ihn dreht, und wir haben einen Debug-Modus implementiert Jetzt möchte ich, dass der Spieler Laserprojektile abschießen kann Laserprojektile abschießen kann Wir müssen sicherstellen, dass diese Projektile von der Spitze des Spielerraumschiffs in die richtige Richtung zum Mauszeiger fliegen von der Spitze des Spielerraumschiffs in die richtige Richtung zum Darüber hinaus werden wir die Leistung dieses Projekts optimieren, indem Projektile als wiederverwendbaren Objektpool implementieren Das bedeutet, dass wir, anstatt diese Projektilobjekte ständig zu erstellen und zu verwerfen, während der Spieler schießt, einen Pool von Objekten während der Spieler schießt, erstellen werden Und wir werden sie aus dem Pool ziehen, wenn wir sie brauchen, und wir werden den Objektpool zurückbringen, anstatt sie zu zerstören und Entwurfsmuster für Objektpools hilft uns dabei, die automatische Speicherzuweisung und die automatische Speicherbereinigung besser zu verwalten automatische Speicherzuweisung . Wir weisen den Speicher nur einmal wenn wir den ersten Objektpool erstellen, was leistungseffizienter ist Garbage-Collection ist ein automatisierter Prozess bei dem Java Script Speicher zurückgewinnt , indem Objekte entfernt werden , auf die im Code nicht mehr verwiesen wird Müllabfuhr erfolgt automatisch und manchmal zu ungünstigen Zeiten Zum Beispiel zu einer Zeit, in der wir eine große feindliche Welle animieren müssen Infolgedessen kann es zu einer niedrigeren Bildrate und zu Ruckeln der Animation Wir möchten, dass unser Spiel flüssig und schnell läuft und immer schnell auf Spielereingaben reagiert Deshalb werden wir den Müllsammelprozess komplett eliminieren , indem wir dieselben, sagen wir 20 Projektilobjekte, immer wieder verwenden , anstatt die alten zu verwerfen und neue zu erstellen. und Diese einfache Technik zur Leistungssteigerung kann viel für Ihre Projekte tun Lasst uns lernen, wie man es bei Projektilen einsetzt, und wenn wir es verstanden haben, werden wir es auch für Feinde einsetzen Klasse Projektil wird als Blaupause verwendet, um den anfänglichen Pool von, sagen wir , 20 wiederverwendbaren Projektilobjekten zu erstellen anfänglichen Pool von, sagen wir , 20 wiederverwendbaren Projektilobjekten sagen wir , 20 wiederverwendbaren Wie üblich benötigen wir einen Verweis auf die Hauptspielklasse, um Zugriff auf Eigenschaften und Methoden zu erhalten Zugriff auf Eigenschaften und Methoden zu , die sich auf dem Hauptspielobjekt Projektile werden an der Kollisionserkennung beteiligt sein, daher müssen wir sicherstellen, dass sie X - und Y-Koordinaten und Radiuseigenschaften haben - und Y-Koordinaten und Radiuseigenschaften Ich werde sie zuerst groß machen , damit wir sie deutlich sehen können Die Objekte, die von dieser Projektilklasse erzeugt werden, werden sogenannte Objektpoolmitglieder Wir müssen ihnen eine Eigenschaft geben , die angibt, ob das Objekt im Pool verfügbar ist oder ob es gerade verwendet wird Zunächst wird ein freies Eigentum festgelegt. Das wahre Projektilobjekt befindet sich inaktiv im Pool Es wartet darauf, benutzt zu werden. Es ist fertig und verfügbar, es ist kostenlos. Jedes Mitglied des Objektpools benötigt zwei Methoden, die Startmethode, die ausgeführt wird, wenn wir das Objekt aus dem Objektpool ziehen das Objekt aus dem Objektpool und es im Spiel aktivieren. An diesem Punkt stehen wir dem freien Eigentum gegenüber. Dieses Objekt ist nicht mehr kostenlos und verfügbar. Es wird im Spiel verwendet, gezeichnet und animiert. Wir benötigen auch eine Reset-Methode anstatt das Objekt zu löschen. Wenn wir damit fertig sind, rufen wir ihre Reset-Methode auf, die ihre Eigenschaft free wieder auf true setzt. Das bedeutet, dass das Objekt inaktiv wird wieder verwendet werden kann , wenn wir es benötigen. Wenn wir ein Projektil abfeuern, rufen wir dessen Startmethode auf, um es zu aktivieren Wenn das Projektil mit einem Feind kollidiert oder wenn es vom Bildschirm fliegt, rufen wir seine Reset-Methode auf, um es zu deaktivieren rufen wir seine Reset-Methode auf, um es zu Wir werden ihm eine Ziehmethode geben. Wir zeichnen das Objekt nur wenn seine freie Eigenschaft falsch ist, wenn es im Spiel aktiv ist. Wir wollen es nicht zeichnen, wenn es nur im Objekt sitzt und darauf wartet, benutzt zu werden. Bei der Aktualisierungsmethode wird das Projektil bewegt. Auch hier wollen wir den Aktualisierungscode für aktive Projektile nur ausführen , wenn dieser Wert innerhalb der Draw-Methode falsch ist. Wir zeichnen einen Kreis, der die R-Methode für den Projektil-Startpfad darstellt den Projektil-Startpfad darstellt Lassen Sie sich hier nicht von den Zeilenumbrüchen verwirren, es ist alles in einer Zeile, für mich wird nur in eine andere Zeile umgebrochen weil mein Code-Editor-Fenster zu schmal ist Weil ich dir den Code und das Projekt im Update Seite an Seite zeigen möchte . Wenn das Projektil aktiv ist, aktualisieren wir seine X- und Y-Position mit den Werten Geschwindigkeit X und Geschwindigkeit Y, die die Eigenschaft horizontale Geschwindigkeit erstellt haben und ich setze sie auf ein Pixel Ein Pixel pro Bild bedeutet, dass es sich von links nach rechts bewegt Geschwindigkeit Y wird ebenfalls ein Pixel betragen, was eine Bewegung von oben nach unten bedeutet. Diese beiden Geschwindigkeiten werden kombiniert , um die endgültige Richtung zu bestimmen. Die Kombination dieser beiden speziellen Geschwindigkeitswerte, 11, führt einer Bewegung in die untere rechte Richtung. Die Projektil-Klasse ist bereit. Sie ist ein Mitglied des Objektpools. Es hat eine kostenlose Eigenschaft sowie Start- und Reset-Methoden. Und der Code innerhalb von Draw and Update wird nur ausgeführt, wenn das Objekt aktiv ist, wenn seine freie Eigenschaft auf Fall gesetzt ist. Wir werden den Objektpool innerhalb der Spielklasse vom Haupt-Gehirn dieser Codebasis aus verwalten . Projektilpool wird ein Array sein, das alle aktiven und auch die inaktiven Projektilobjekte enthält aktiven und auch die inaktiven Projektilobjekte Die Anzahl der Projektile bestimmt wie viele Objekte wir erstellen wollen und wie viele Objekte wir im Pool zur Verfügung haben Wenn wir zu wenige davon haben, können uns diese ausgehen und wir haben keine zur Verfügung, wenn wir sie brauchen. Wenn wir zu viele erstellen, verschwenden wir möglicherweise Speicherplatz, wenn wir nie einen großen Teil des von uns erstellten Pools verwenden Wir werden die richtige Anzahl später finden, wenn das Spiel spielbar ist und wir können testen, wie schnell wir sie verwenden und wiederverwenden Sobald das Spiel erstellt ist, werden wir automatisch eine benutzerdefinierte Methode ausführen Ich nenne „Projektilpool erstellen“, wodurch das Projektilpool-Array mit fünf inaktiven Projektilobjekten gefüllt wird mit fünf inaktiven Projektilobjekten Ich werde diese Methode hier aufschreiben. Im Inneren erstelle ich eine Viererschleife , die fünfmal ausgeführt wird. Jedes Mal, wenn es läuft, nehmen wir Projektilpool und schieben neues Projektil hinein Ich kann mir vorstellen, dass es Spiel als Argument braucht. Ich übergebe ihm dieses Schlüsselwort. Die Hauptspielklasse wird unseren Projektilpool verwalten. Wir speichern fünf Objektpoolmitglieder im Projektilpool-Array und erstellen sie auf der ersten Seite. Sie werden geladen, sobald wir das Spielobjekt erstellt haben Denn wie bereits erwähnt, wird der gesamte Code im Klassenkonstruktor automatisch ausgeführt Wenn wir eine Instanz einer Klasse mit dem neuen Schlüsselwort erstellen , benötigen wir eine Methode, um diesen Projektilpool zu erstellen Und wir brauchen auch eine weitere Methode. Ich rufe zum Beispiel Get Projectile an. Wenn wir diese Methode aufrufen, durchläuft sie das Projektilpool-Array, das wir Während wir die Indizes in dem Array durchgehen , das wir zuvor erstellt haben Sobald wir das erste Projektilobjekt finden das erste Projektilobjekt Eigenschaft free auf true gesetzt ist, wissen wir, dass dieses Objekt derzeit inaktiv ist Es ist kostenlos und verfügbar. Wir nehmen es und geben es zurück. Methode Get projectile findet das erste freie Objekt im Pool und gibt es erneut zurück Bitte lassen Sie sich durch den Zeilenumbruch nicht verwirren, es ist alles in einer Zeile Es ist nur so, dass mein Browserfenster zu schmal ist. Wenn free den Wert true hat, gibt die Methode get projectile das Objekt zurück und beendet die Suche zunächst Alle Projektile sind kostenlos, sie sind inaktiv und können kostenlos verwendet werden Wenn wir die Startmethode ausführen, aktivieren wir sie und sie sind nicht mehr kostenlos Ich füge einen weiteren Event-Listener hinzu, diesmal für ein Mouse-Down-Event Wenn wir mit der linken Maustaste bei gedrückter Maustaste klicken , setzen wir Maus X und Y auf X und Y des Events und rufen die Schießmethode für die Spielerklasse auf diesen Spieler wird über diese Referenz zugegriffen . Hier ist diese Klasse. Geben wir ihm eine benutzerdefinierte Aufnahmemethode. Wenn wir eine temporäre Hilfsvariable erstellen sollten, setzen wir sie auf die benutzerdefinierte Get-Projektile-Methode, die wir gerade geschrieben Wir greifen über diese Spieleigenschaft hier darauf zu und es befindet sich auf dem Spielobjekt hier unten. Diese Methode durchläuft den Pool und gibt das erste freie Projektilobjekt zurück Nehmen wir an, wir zeichnen gerade alle Projektile. Sie sind derzeit alle aktiv und uns gehen die frei verfügbaren aus , die unseren Code beschädigen könnten Um das zu beheben, sage ich, wenn wir ein freies Projektil gefunden haben, wenn die Methode get projectile tatsächlich in der Lage war , eines zu finden , das nur kostenlos und verfügbar ist, dann nimm dieses kostenlose Projektil und rufe seine Startmethode auf, um es zu aktivieren Hier ändern sich die Werte einiger Eigenschaften des Projektilobjekts wir es animieren Wir müssen sie irgendwann auf die Standardwerte zurücksetzen. Wir können uns dafür entscheiden, dies zu tun, wenn wir es aktivieren, aber auch, wenn wir das Objekt deaktivieren. Ich werde alles innerhalb der Startmethode machen. Wenn wir das Objekt aktivieren, wird es einfacher sein, es in Kombination mit anderen Entscheidungen zu implementieren , die wir bereits mit dieser speziellen Codebasis getroffen haben. Startmethode erwartet X - und Y-Koordinaten als Argument, und wir setzen diese Werte auf tx und ty. Lassen Sie uns zunächst alle Projektile von Koordinaten aus starten. Sagen wir 100, 100. Ich hätte den Pool auch trösten sollen , nur um zu überprüfen, ob er nach der Ausführung dieser Methode tatsächlich mit fünf Objekten gefüllt war. Alles gut Ich inspiziere einen, um sicherzugehen , dass wir hier keine Fehler haben. Wenn Sie einen Wert sehen, der keine Zahl ist, könnte das bedeuten, dass wir bei der Berechnung dieser Werte irgendwo ein Problem haben . Hier ist alles gut. Ich lösche das Consolog in der Render-Methode Ich lösche diesen Startpfad. Ich nehme das gesamte Projektil-Pool-Array und rufe für jedes Projektilobjekt innerhalb des Arrays dessen Draw-Methode auf, übergebe ihr den Kontext und rufe auch Update Wenn ich klicke, passiert nichts. Ich folge dem Code , um meinen Fehler zu finden. Zuerst überprüfe ich, ob dieses Mousedown-Event tatsächlich läuft. Schnell. Consolog wird mir hier helfen. Ja, ich weiß, dass das Problem nicht hier ist. Der nächste Schritt wäre zu sehen, ob bei der Schussmethode das Icsolog-Projektil tatsächlich von innen ausgeführt wird Icsolog-Projektil tatsächlich von innen ausgeführt Wenn ich klicke, kann ich sehen, dass die Methode „Projektil abrufen korrekt aus dem Pool zurückgibt für mich Projektilobjekte korrekt aus dem Pool zurückgibt. Toll, das heißt, das Problem muss irgendwo in der Projektilklasse liegen Ich komme runter und sehe, dass ich eine Zeile vergessen habe. Die Arc-Methode rendert eigentlich keine Formen auf der Leinwand. Um es hier tatsächlich zu zeichnen, muss ich entweder Stroke oder Fail aufrufen. Wenn ich jetzt auf die Leinwand klicke, zeichnen wir Projektile Ich möchte ihnen eine andere Farbe geben, aber ich möchte, dass diese Farbe nur auf den Projektil-Zeichnungscode beschränkt ist und sich nicht auf andere Formen auswirkt, die wir auf der Leinwand zeichnen Später setze ich diesen Code zwischen den Methoden „ Sicher“ und „Wiederherstellen“ im Füllstil um, bis sich goldene Projektile nach unten rechts bewegen Aufgrund der Kombination dieser beiden Geschwindigkeiten ein Pixel nach rechts und ein Pixel nach unten pro führt ein Pixel nach rechts und ein Pixel nach unten pro Animationsframe dieser Bewegungsrichtung Die Start-Methode erwartet X- und Y-Koordinaten als Argumente. Ich möchte eigentlich, dass das Projektil von dem Spieler ausgeht , der es abschießt. Ich gebe es an diesen Doxy weiter, weil wir hier in der Spielerklasse sind, die 38. L9-Spieler-Projektile: Wenn ich weiter mit der Maus klicke, schießen wir fünf Projektile ab Und danach, anstatt ein Projektil zurückzugeben, Methode get projectile undefined zurück . Das liegt daran, dass uns die Objekte im Pool ausgehen und es keine frei verfügbaren Objekte mehr finden kann Es gibt kein Projektilobjekt mehr, bei dem drei Eigenschaften auf true gesetzt Da wir alle fünf aktiviert haben, werde ich dafür sorgen, dass Projektile automatisch zurückgesetzt werden, wenn sie sich vom Bildschirm entfernen Die horizontale Ausrichtung des Projektils ist kleiner als Null, wenn es größer als die Spielbreite ist, wenn die vertikale Position kleiner als Null ist oder wenn sie größer als die Spielhöhe ist Wenn eine dieser vier Bedingungen zutrifft, wissen wir, dass sich das Projektil bis zum Bildschirmrand bewegt bis zum Bildschirmrand Wir können also seine Reset-Methode aufrufen, wodurch die Eigenschaft free auf true gesetzt wird Sie wird deaktiviert. Das bedeutet auch, dass das Projektil aufgrund dieser Kontrollen nicht mehr gezeichnet und aktualisiert wird Jetzt kann ich schießen, wenn alle fünf Projektile aktiv sind Wir sind immer noch undefiniert Wir klicken weiter, aber sobald eines von ihnen vom Bildschirm verschwindet, wird es zurückgesetzt und wir können es wiederverwenden und wir schießen erneut. Tolle Arbeit. Wir haben einen funktionierenden Pool für wiederverwendbare Objekte. Jetzt möchte ich, dass die Projektile in die richtige Richtung fliegen Wir haben bereits eine benutzerdefinierte Calc Am-Methode, die zwei kreisförmige Objekte verwendet Sie gibt uns die horizontale und vertikale Richtung vor. Wir können es verwenden, um sie dazu zu bringen sich aufeinander zu oder voneinander weg zu bewegen. Wir berechnen diesen Wert bereits von früher und setzen ihn auf diese Dom-Eigenschaft in der Spielerklasse. Wir können denselben Wert verwenden, weil wir immer noch möchten, dass die Bewegungsrichtung des Projektils durch die relative Position zwischen dem Planeten und der Maus bestimmt wird die relative Position zwischen dem Planeten und der Maus Wir wollen, dass das Projektil von der Mitte des Planeten zur Maus fliegt der Mitte des Planeten zur Maus Zuerst übergebe ich der Startmethode Geschwindigkeit x von minus eins und Geschwindigkeit y von eins Wir stellen sicher, dass die Startmethode diese Werte erwartet , und setzt ihre Eigenschaften Geschwindigkeit x und Geschwindigkeit y auf diese Werte. Sie werden hier zu ihren Positionskoordinaten addiert , abzüglich eines Pixels pro Bild horizontal, und plus ein Pixel pro Bild vertikal ergibt diese Bewegungsrichtung. Minus zehn und plus eins geben uns das. Lassen Sie uns die Werte verwenden, die wir von der Calc-Methode erhalten. Wir wissen, dass der vertikale Vektor den Index Null im Array hat, der horizontale Vektor den Index eins Wir verwenden diese Werte, die das Verhältnis zwischen horizontaler Entfernung und hypotenöser Entfernung Jetzt können wir Projektile in jede Richtung abfeuern. Sie bewegen sich ziemlich langsam. Ich kann eine Eigenschaft für einen Geschwindigkeitsmodifikator erstellen. Solange ich beide mit demselben Modifikatorwert multipliziere , wird der Vektor einfach skaliert, wobei die richtige Richtung beibehalten Uns gehen die Projektile aus. Lass uns einen Puffer erstellen. Vielleicht möchte ich 30 im Pool erstellen , um sicherzugehen, dass uns nicht die Puste ausgeht. Es hängt davon ab, wie schnell sie Bildschirm verlassen und wie schnell sie zurückgesetzt werden. Wir können es testen und den Wert später anpassen. Die Projektile kommen aus der Mitte des Spielerkreises, was nicht ideal ist Ich möchte, dass sie von der Spitze des Raumschiffs kommen. Hier bei der Methode Spielerklasse innerhalb von Schüssen erweitern wir die X-Startkoordinaten erweitern wir die X-Startkoordinaten jedes Projektils horizontalen Vektorwert , skaliert auf den Radius des Spielers, der horizontal sortiert werden soll Ich verwende hier auch den vertikalen Vektor, alles befindet sich auf einer Linie Ich hoffe du kannst meinen Code lesen. Wir übergeben eine Menge Dinge an die Startmethode. Jetzt verwende ich hier den Radius, weil ich möchte, dass die Projektile direkt von der Spitze des Raumschiffs des Spielers kommen direkt von der Spitze des Raumschiffs des Spielers Wenn du hier so unterschiedliche Werte verwendest, sollte es sinnvoller sein, was hier genau passiert Wenn du eine Weile mit diesen Werten herumspielst, wenn ich hier diesen Punktradius verwende, kommt das Projektil genau an dem Punkt heraus , den ich will Wir schießen abwechselnd, wenn wir klicken. Ich kann auch sagen, ob die Taste, die losgelassen wurde, die Nummer eins auf der Tastatur ist oder ob es sich um eine beliebige Taste handelt. Ich nenne den Spieler auch Shot. Jetzt kann ich schießen, indem mit der Maus klicke oder eine Taste auf meiner Tastatur drücke. Jedes Mal, wenn wir schießen, benutze die Projektilmethode. Wir greifen von Linie hundertsieben aus in die Projektilreihe von Linie hundertsieben aus in die Projektilreihe Es wird uns ein kostenloses Projektilobjekt geben. Startmethode wird es an der Spitze des Spielerraumschiffs positioniert an der Spitze des Spielerraumschiffs Und die ruhige Methode gibt ihm horizontale und vertikale Geschwindigkeit, die der relativen Entfernung zum Zeitpunkt zwischen Planet und Maus entspricht die der relativen Entfernung zum Zeitpunkt zwischen , um das Projektil in die richtige Richtung zu schicken Projektil in die richtige Richtung Sobald sich Projektile außerhalb des Leinwandbereichs bewegen, setzen wir sie zurück und sie sind bereit, erneut verwendet zu werden, wann immer wir sie benötigen Wir haben einen Objektpool in unserer Codebasis implementiert. Ich lasse sie schneller bewegen und ich mache sie kleiner. Ja, das ist näher an dem, was ich mir eigentlich wünsche, dass sie im finalen Spiel aussehen. Ich kann diesen Consolog jetzt löschen. 39. L10 Feind-Pool: wir nun gesehen haben, wie ein Objektpool in unserer Codebasis implementiert wird, wollen wir dieses Wissen festigen, indem wir dieselbe Logik wiederholen , um einen wiederverwendbaren Pool feindlicher Objekte zu erstellen einen wiederverwendbaren Pool feindlicher Denken Sie daran, dass jedes Mitglied des Objektpools eine Eigenschaft benötigt , die angibt, ob es aktiv oder inaktiv ist Wir zeichnen und aktualisieren es nur, wenn es aktiv ist. Die Start-Methode aktiviert das Objekt. Sie legt Eigenschaften auf einige Werte fest, die das Objekt zu Beginn seines Lebenszyklus benötigt. In diesem Fall positionieren wir das Projektil in Abhängigkeit von der Spielerposition und geben ihm je nach Mausposition die Bewegungsrichtung je nach Mausposition die Bewegungsrichtung Die Reset-Methode ist hier. Um das Objekt zu deaktivieren, haben wir ein Array, das die Objekte enthält . Wir legen die Zahl fest. Und wir benötigen zwei Hilfsmethoden, eine, um den Pool zu erstellen und eine weitere, um einen kostenlosen Artikel aus dem Pool zu erhalten. Lassen Sie uns etwas ganz Ähnliches für Feinde implementieren , um dieses Wissen zu festigen und alles zu klären falls Sie sich immer noch nicht ganz sicher sind , wie einiges davon funktioniert Wiederholung hilft mir immer dabei, eine feindliche Klasse zu erstellen. Sie benötigt einen Verweis auf das Hauptspielobjekt. Es braucht X und Y und Radius. Lassen Sie uns zum Beispiel die Startkoordinaten für X und Y auf 100, 100 setzen . Vorerst werden wir rechteckige Rahmen aus dem Spritesheet ausschneiden , indem wir anhand der Breite und Höhe des Rahmens um das Spritesheet herumspringen und Höhe des Rahmens um das Spritesheet herumspringen Ich habe die Grafikelemente so erstellt, dass sie den Anforderungen entsprechen, die wir in Game Collision benötigen Kreise haben einen Radius von 40 Pixeln. Einzelne Sprite-Frames werden 80 mal 80 Pixel groß sein. Diese Framegröße entspricht auch dem Space Invaders-Spiel, das wir zuvor gemacht haben Die gegnerischen Spreizblätter sind voll kompatibel und können in diesem Spiel problemlos verwendet auch in diesem Spiel problemlos verwendet werden, wenn Sie es erweitern möchten Geschwindigkeit x und Geschwindigkeit Y bestimmen , in welche Richtung sich der Feind bewegen wird Da es sich um ein Mitglied des Objektpools handelt, benötigt es freies Eigentum. Anfangs haben wir es gesagt, die True-Start-Methode wird von Angesicht zu Angesicht freigegeben, um sie zu aktivieren. Die Reset-Methode wird auf true gesetzt, um sie zu deaktivieren. Die Draw-Methode erwartet wie gewohnt einen Kontext. Und der darin enthaltene Code wird nur für aktive Feinde ausgeführt. Die Update-Methode führt ihren Code auch nur für die aktiven aus. Lassen Sie uns zunächst die Feinde als einfache Kreise zeichnen. Die übliche Kombination aus Bettelpfad-Arc-Methode , ich streichle sie. Aktualisierungsmethode verschiebt ihre X- und Y-Koordinaten in Abhängigkeit von den Werten für Geschwindigkeit X und Geschwindigkeit Y. So wie wir es bei Projektilen gemacht haben. Wir brauchen ein Array, das alle aktiven und inaktiven feindlichen Objekte aufnehmen alle aktiven und inaktiven feindlichen Objekte Die Anzahl der Feinde im Feindpool wird 20 sein. Und wir werden diesen Pool automatisch erstellen und mit 20 feindlichen Objekten füllen. Zu dem Zeitpunkt, an dem wir eine Instanz der Spielklasse erstellen, genau wie bei Projektilen Mit „Gegnerpool erstellen“ werden 20 feindliche Objekte erstellt und in die gegnerische Pool-Anordnung geschoben Der Konstruktor der feindlichen Klasse erwartet das Spiel als Argument. Ich gebe ihm dieses Schlüsselwort, Get. Feind wird das feindliche Pool-Array durchqueren wir gerade mit 20 inaktiven Objekten gefüllt haben. Ich werde für jeden einzelnen das freie Eigentum überprüfen. Und sobald es das erste feindliche Objekt findet , bei dem drei Eigenschaften auf wahr gesetzt sind, gibt es dieses Objekt zurück und es hört auf zu gibt es dieses Objekt zurück und es hört auf suchen. Ich konsultiere Feindpool. Zu diesem Zeitpunkt sollte es hier mit 20 feindlichen Objekten gefüllt sein . Nachdem wir die Methode create aufgerufen haben, sehe ich sie ja. Ich öffne eine, nur um zu überprüfen. Hier scheint alles gut zu sein. Ich lösche den Consolo So wie wir Projektile für jeden Animationsframe zeichnen und aktualisieren , nehmen wir den gegnerischen Pool und rufen nach jedem Für jeden Feind nennen wir „Ziehen“ und „Aktualisieren“. Wir erinnern uns, dass diese Methoden nur ausgeführt werden, wenn das Objekt aktiv ist, wenn seine Eigenschaft free auf false gesetzt ist. Anfänglich haben alle feindlichen Objekte eine freie Eigenschaft, die auf true gesetzt ist. Durch Aufrufen von start wird das Objekt aktiviert. Ich möchte, dass das erste Objekt im feindlichen Pool-Array automatisch aktiviert wird. Sobald das Spiel beginnt, nehme ich ein Objekt mit einem Index von Null im gegnerischen Pool-Array und nenne es Startmethode. Ich kann es hier sehen. Lasst uns die ersten fünf Objekte aktivieren. Um das zu tun, müssen wir vorsichtig sein. Wenn wir nicht genug Objekte im Pool haben, würde das den Code kaputt machen, weil ich keine if-Anweisung mache. Überprüfe zuerst, ob die Zahl hier hoch genug ist. Ich zeichne alle fünf Gegner an den gleichen Koordinaten. Wenn ich sie bewege, bewegen sie sich alle, ich könnte die X - und Y-Startkoordinaten auf 00 setzen. Wenn die Startmethode ausgeführt wird, setzen wir X auf einen zufälligen Wert zwischen Null und Spielbreite vertikale Y-Position auf einen Zufallswert zwischen Null und Spielhöhe. Wenn ich jetzt die Seite aktualisiere, sind die fünf Gegner zufällig im sichtbaren Spielbereich verstreut . Wie immer fügen wir für jeden Animationsframe die Werte für Geschwindigkeit x und Geschwindigkeit Y zu ihren X- und Y-Koordinaten hinzu, um ihnen Richtung und Geschwindigkeit der Bewegung zu geben . Die Kombination dieser Werte bestimmt ihren Bewegungsvektor. Ich habe die Geschwindigkeiten auf 200 eingestellt. Wir werden ihre Geschwindigkeit einstellen , wenn die Startmethode ausgeführt wird. Ich möchte, dass sich der Feind direkt in die Mitte des Planeten bewegt . Sie greifen uns an. Wir brauchen einen Vektor zwischen zufälligen Startposition des Feindes und dem Planeten. Wir haben schon etwas Ähnliches gemacht. Ich erstelle eine temporäre Hilfsvariable namens am. Es wird unsere Utility-Methode ausführen, aber dieses Mal wollen wir Richtungsvektoren zwischen den Feinden. Mit diesem Schlüsselwort befinden wir uns als Objekt B in der Klasse des Feindes Wir übergeben es auf dem Planeten. Wenn Sie sich erinnern, gibt es ein Array mit vier Werten zurück. Das erste Element, das Element mit dem Index Null, ist die horizontale Richtung. Element mit einem Index von eins ist die vertikale Richtung. Es funktioniert nicht und es ist richtig. Das liegt daran, dass wir das Ziel berechnen müssen , nachdem wir die Feinde positioniert haben. Die Position des Feindes ist bei dieser Berechnung natürlich sehr wichtig, um die richtige Richtung zwischen dem Feind und dem Planeten zu ermitteln. Zuerst lege ich meine Position fest, dann rufen wir Ruhe. Jetzt bewegen sich die Feinde auf den Planeten zu. Perfekt. 40. L11-Kollisionserkennung: Ich möchte, dass die Feinde den Planeten so durchqueren. Ich will, dass sie damit kollidieren. Lassen Sie uns eine wiederverwendbare Check-Kollisionsmethode erstellen. Sie erwartet die Argumente Objekt A und Objekt BS. In diesem Projekt werden alle Objekte kreisförmige Kästchen haben . Um die Kollision zwischen zwei Kreisen, Kreis A und Kreis B, zu überprüfen, müssen wir den Abstand zwischen dem Mittelpunkt von Kreis A und dem Mittelpunkt von Kreis B auf der horizontalen Achse überprüfen. Das gibt uns diese Seite. Dann machen wir dasselbe auf der vertikalen Y-Achse , um diese Seite zu erhalten. Um nun den tatsächlichen Abstand zwischen diesen beiden Punkten in den Raum zu ermitteln, benötigen wir die Länge des Hypotenus, die wir mit der Formel des Ptagoras-Theorems oder der eingebauten Mathod-Hypotenus-Methode ermitteln können Ptagoras-Theorems oder der eingebauten Mathod-Hypotenus-Methode ermitteln zwischen diesen beiden Punkten in den Raum zu ermitteln, benötigen wir die Länge des Hypotenus, die wir mit der Formel des Ptagoras-Theorems oder der eingebauten Mathod-Hypotenus-Methode ermitteln können. Wir haben einen Abstand zwischen zwei Punkten, zwischen dem Mittelpunkt des kreisförmigen Objekts A und dem kreisförmigen Objekt A und B. Wir berechnen die Summe ihrer beiden Radien. Wenn der Abstand zwischen den Mittelpunkten größer ist als die Summe der Radien, wissen wir, dass die Kreise nicht kollidieren Es ist dasselbe, der Kreis berührt sich. Wenn die Entfernung kleiner ist als die Summe der Radien, wissen wir, dass die Kreise kollidieren Ich werde den Ausdruck einfach direkt so zurückgeben, sodass diese Methode einfach zu Wir nennen es, wir übergeben es Objekt A und Objekt B. Und diese Methode gibt direkt wahr oder falsch zurück , je nachdem ob diese beiden Objekte kollidieren oder nicht Mal sehen, ob es funktioniert, indem wir in die feindliche Klasse aufsteigen. Und innerhalb der Update-Methode überprüfen wir die Kollision zwischen Feind und Planet. Wenn es zu einer Kollision kommt, setzen wir den Gegner zurück, wodurch er deaktiviert wird und er wieder in den Pool verfügbarer Objekte zurückgesetzt Ich kopiere diesen Codeblock. Wir machen dasselbe für den Spieler. Wenn der Feind mit dem Raumschiff des Spielers kollidiert, setzen wir den Lass uns sehen. Ja, das funktioniert. 41. L12 Periodische Events: Wenn wir die Feinde zurücksetzen, sind sie weg. Aber jetzt brauche ich mehr Feinde, um weiter zu kommen. Im Moment aktivieren wir nur die ersten fünf Gegner aus dem Pool. Hier möchte ich regelmäßig neue Feinde aktivieren. Nehmen wir an, jede 1-Sekunden-Anforderungsanimationsframe-Methode generiert automatisch einen Zeitstempel Und sie übergibt diesen Zeitstempelwert als Argument an die Animate-Methode Stellen Sie sich vor, es gibt ihn hier so weiter. Auch wenn es unsichtbar hinter den Kulissen passiert, ist der Zeitstempelwert einfach die Anzahl der Millisekunden, die seit dem Aufruf des ersten Anforderungsanimationsframes in dieser Sequenz vergangen sind . Alles, was wir tun müssen, um auf diesen Zeitstempelwert zuzugreifen, ist, ihm einen Variablennamen zu diesen Zeitstempelwert zuzugreifen, ist, ihm Hier weiß das Java-Skript automatisch , dass wir den Zeitstempel wollten Wir müssen auch den Zeitstempel der vorherigen Animationsschleife verfolgen Zeitstempel der vorherigen Animationsschleife Ich nenne es das letzte Mal, wenn ich es hier außerhalb von Animate platziert habe. Anfangs habe ich es auf Null gesetzt. Dann verwenden wir diese Werte , um die Deltazeit zu berechnen. Deltazeit ist die Anzahl der Millisekunden, die unser Computer benötigt , um einen Animationsframe bereitzustellen Das ist der Unterschied zwischen dem Zeitstempel dieser Animationsschleife und dem Zeitstempel der vorherigen Wenn Sie einen schnellen Computer haben, kann Ihr Computer Frames schneller bereitstellen. Ihre Deltazeit wird geringer sein , da dieser schnelle Computer weniger Millisekunden benötigt , um jede Animationsframe-Anforderung zu generieren jede Animationsframe-Anforderung Animationsframe-Methode normalisiert auch automatisch Wenn sie dazu in der Lage ist, passt sie Animationsgeschwindigkeit an die Bildschirmaktualisierungsrate an, da es keinen Sinn macht, Animationsframes zu berechnen und zu zeichnen , die Ihr Bildschirm nicht anzeigen kann Nachdem wir die Deltazeit berechnet haben, setzen wir beim letzten Mal den aktuellen Zeitstempel ein. Damit er als alter Zeitstempel aus der vorherigen Animationsschleife verwendet werden kann . In der nächsten Animationsschleife haben die meisten von uns Bildschirme, die 60 Bilder pro Sekunde wiedergeben. 1.000 Millisekunden geteilt durch 60 sind ungefähr 16,6 Meine Deltazeit beträgt 16,6 Meine Animation läuft mit 60 Bildern pro Sekunde Wenn Sie einen Bildschirm mit hoher Bildwiederholfrequenz haben oder wenn Sie einen sehr alten, schwachen Computer haben, Ihre Deltazeit möglicherweise Deltazeit auf diese Weise verwenden , stellen wir sicher, dass wir das berücksichtigen Wir zählen die tatsächliche Zeit, die tatsächlichen Millisekunden Auf diese Weise können wir sicherstellen, dass das Timing in unserem Spiel das gleiche ist Auf schnellen und langsamen Maschinen lösche ich den Consolog Ich übergebe Delta-Zeit an die Render-Methode. Ich werde sicherstellen, dass die Render-Methode diesen Wert als Argument erwartet, und ich protokolliere die Delta-Time von hier aus. Ja, es funktioniert immer noch. Stellen Sie immer sicher, dass Sie Ihre Consologs löschen. Wenn Sie ein solches Consolo verlassen, kann dies Ihre Anwendung verlangsamen Diese Variablen beziehen sich auf Feinde. Ich werde zu Beginn des Spiels nur einen Feind hinzufügen. Ich benötige zwei Helfervariablen Feindtimer und Feindintervall. Der Feindtimer zählt die Delta-Zeit von Null bis 1.000. Dann fügen wir einen neuen Feind hinzu und wir zählen erneut. Wir wollen regelmäßig alle 1 Sekunde, alle 1.000 Millisekunden, ein feindliches Objekt aus dem Objektpool aktivieren alle 1 Sekunde, alle 1.000 Millisekunden, ein feindliches Objekt aus dem Objektpool Wenn der Timer des Feindes kleiner als das Feindintervall ist, fügen wir dem Timer für jeden Animationsframe immer wieder einen Wert für die Delta-Zeit hinzu, bis wir mehr als 1.000 haben. Und dann wird diese L-Anweisung ausgeführt, wenn Timer genug Delta-Zeit angesammelt hat Wir setzen den Timer auf Null zurück , damit er wieder zählen kann, und wir aktivieren einen neuen gegnerischen Helfer temporäre Variable namens Enemy entspricht unserer benutzerdefinierten Methode zum Abrufen von Feinden. Wenn Get Enemy in der Lage war, ein freies und verfügbares gegnerisches Objekt aus dem Pool zurückzugeben , rufen wir seine Startmethode auf, um es zu aktivieren. Toll, wir ziehen alle 1 Sekunde einen neuen Feind aus dem Objektpool. Ich möchte nicht wirklich, dass die Feinde zufällig irgendwo in der Mitte der Leinwand auftauchen . Lass uns etwas versuchen. Wenn ich die Methode random alleine so aufrufe, gibt sie einen Wert von 0-1 zurück, wenn dieser Wert kleiner als 0,5 ist. Ungefähr in 50% der Fälle sagten wir, dass Ungefähr in 50% der Fälle sagten wir horizontale Position des Feindes zwischen Null und der Spielbreite und die vertikale Position Null Der Feind wird irgendwo in diesem Bereich auftauchen. In den anderen 50% der Fälle setzen wir x auf Null. Y wird ein zufälliger Wert zwischen Null und Spielhöhe sein. Die resultierende Position wird zufällig irgendwo in diesem Bereich sein. Toll, jetzt können wir das auch für die anderen beiden Seiten machen. Wie macht man das? Zum Beispiel können wir einen sogenannten ternären Operator verwenden Der einzige Javascript-Operator mit drei Operanden. Wir verwenden ihn als einfache einzeilige FL-Anweisung. Es geht so. Eine Bedingung, um zu überprüfen, ob das Fragezeichen wahr Tun Sie dies als Doppelpunkt, tun Sie das. Wenn die Methode random kleiner als 0,5 ist, vertikale Position dieses Feindes Null L In anderen 50% der Fälle entspricht Punkt Y der Punkthöhe. Wenn dieser Codeblock ausgeführt wird, kommen Feinde von hier oder von hier. Ja, wir machen dasselbe in diesem Codeblock. Wenn die Methode random kleiner als 0,5 ist, ist dieser Punkt x Null, x ist die Spielbreite, während gleichzeitig die vertikale Position zufällig zwischen Null und Spielhöhe ist. Wenn dieser Codeblock ausgeführt wird, kommen Feinde von hier oder von hier. Jetzt haben wir Feinde, die zufällig aus allen vier Richtungen kommen. Wenn sie mit dem Spieler oder dem Planeten kollidieren, werden sie zurückgesetzt und verschwinden Ich möchte auch in der Lage sein, die Feinde zu erschießen. Ich möchte die Kollision zwischen Feind und Projektilen überprüfen. Ich nehme die gesamte Projektilzuganordnung für jedes Projektilobjekt Ich überprüfe zuerst, ob das Projektil aktiv ist, ob seine freie Eigenschaft auf False gesetzt ist Denkt daran, dass dieser Codeblock auch nur für aktive Gegner läuft Wir wollen nur die Kollision zwischen aktivem Feind und aktivem Projektil überprüfen aktivem Feind und aktivem Projektil Wenn das Projektil aktiv ist, überprüfen wir auch die Kollision zwischen diesem Feind und dem aktuellen Projektil, über das wir gerade fahren Wenn Projektil und Feind kollidieren, setzen wir das Projektil deaktiviert zurück und legen es wieder in den Objektpool zurück Wenn ich es teste, kann ich sehen, dass Projektile zerstört werden Großartig. Ich werde den Feind auch zurücksetzen, wenn die Kollision passiert. Nett, jetzt können wir die Feinde erschießen. Wir haben eine komplette Spielschleife und wir haben Projektile und Gegner als Objektpool implementiert , was etwas mehr Logik mit sich brachte Aber aus Performancegründen lohnt es sich. Der Code ist immer noch sauber und leicht zu lesen. Ich glaube, ich mag es nicht, wie die Feinde einfach auftauchen. Ich möchte, dass das außerhalb des Bildschirms passiert und der Feind einfach reibungslos und nahtlos in Sichtweite schwebt reibungslos und nahtlos in Sichtweite Ich werde die gegnerischen Startpositionen außerhalb des Bildschirms platzieren Ich berechne den Radius hier, hier, hier und hier. Jetzt fangen sie außerhalb des Bildschirms an und kommen viel besser ins Blickfeld. Ich kann die Gegner öfter zum Komma bringen. Wie wäre es mit einem neuen Feind jede halbe Sekunde. 42. L13 Asteroid-Feind: Der Planet wird wirklich angegriffen. Jetzt greifen uns weiße Kreise an. Lass uns ein paar Grafiken verwenden. Zuerst machen wir einen einfachen Feindtyp, einen Asteroiden. Wir können die Feindtabelle unten im Abschnitt Ressourcen herunterladen Feindtabelle unten im Abschnitt Ressourcen Geben Sie ihr eine Vorstellung von Asteroiden Dies ist die übergeordnete Feindklasse. Sie enthält alle Methoden und Eigenschaften, die allen Gegnertypen gemeinsam sind. Ich werde eine untergeordnete Klasse namens Asteroidenklasse erstellen. Asteroid verlängert den Einlauf. Jetzt ist Enemy eine übergeordnete Klasse, auch Superklasse genannt Asteroid ist eine untergeordnete Klasse, die auch als Unterklasse Asteroidenklasse bezeichnet wird Constructor wird hier das Spiel als Argument erwarten. Zuerst möchte ich den gesamten Code im Konstruktor ausführen. Innerhalb der übergeordneten feindlichen Klasse innerhalb ihrer Oberklasse. Ich möchte den ganzen Code hier ausführen. Ich gebe die Spielreferenz weiter. Wir wissen, dass es dort 98 auf der Leitung erwartet wird. untergeordnete Klasse Asteroid wird Eigenschaften und Methoden enthalten , die nur für diesen Feindtyp spezifisch sind Asteroid wird diese Punktbildeigenschaft haben, die auf die Asteroiden-Tabelle verweist die wir gerade importiert haben Gemeinsam genutzte Eigenschaften befinden sich in der übergeordneten Klasse. Spezifische Eigenschaften und Methoden befinden sich in der untergeordneten Klasse. Weil wir hier das Schlüsselwort extend verwenden. Javascript richtet hinter den Kulissen automatisch eine Prototypenkette ein, wenn wir die Draw- und Update-Methode für die Asteroidenklasse aufrufen Draw- und Update-Methode und Javascript sie hier nicht finden kann, es wird automatisch zur übergeordneten feindlichen Klasse weitergeleitet Es wird versuchen, diese Methoden dort zu finden , da alle Feindtypen Bilder haben werden Ich kann hier raufgehen und die Build-In-Draw-Image-Methode aufrufen. Ich übergebe ihm das Bild, das ich zeichnen möchte, und die X- und Y-Koordinaten. Wo wir es zeichnen sollen, erhalten wir eine Fehlermeldung, weil wir die übergeordnete feindliche Klasse instanziieren und das Java-Skript diese Bildeigenschaft darauf nicht sehen kann Ich muss stattdessen eine Instanz der untergeordneten Klasse Asteroid erstellen untergeordneten Klasse Asteroid Jetzt zeichnen wir die gesamte Tabelle. Ich werde die maximale Anzahl an Feinden auf zwei reduzieren , damit wir weniger beschäftigt und wir leichter sehen können, was passiert Wir können die Methode „Bild zeichnen“, optionale Breite und Höhe wie folgt übergeben . Jetzt wird das gesamte Bild gestreckt oder zusammengedrückt, um den vordefinierten Bereich auszufüllen In diesem Fall drücke ich die gesamte Asteroiden-Tabelle auf einen Bereich von einem Frame zusammen auf einen Bereich von einem Frame Wir können dem Bild auch neun Argumente übergeben, um die maximale Kontrolle über das Bild zu erhalten, das wir zeichnen möchten Wir übergeben ihm das Bild, das wir zeichnen möchten, Quelle X, Quelle Y , Quellbreite und Quellhöhe des Bereichs, den wir aus dem Quellbild ausschneiden wollen. Und wir übergeben ihm Ziel X, Ziel Y, Ziel mit und Zielhöhe um zu definieren, wo auf Ziel-Leinwand wir das ausgeschnittene Bild auf zwei Seiten platzieren möchten das ausgeschnittene Bild auf zwei Seiten platzieren Zuerst schneide ich diesen Frame aus der Koordinate 002 mit und der Höhe eines Frames Nochmals nett, wie wir wissen, werden Kreise und Rechtecke auf Leinwand unterschiedlich gezeichnet Um das rechteckige Asteroidenbild über seiner kreisförmigen Kopfbox auszurichten , muss ich das X-Argument für die horizontale Position um den Auf diese Weise versetze ich auch die vertikale Y-Koordinate um den Radius Als ob dieser Planet von Asteroiden angegriffen wird. Jetzt erhöhe ich die Größe des feindlichen Objekts und ziehe es auf 20 zurück Ich will alle, sagen wir, 1.700 Millisekunden einen neuen feindlichen Asteroiden sagen wir, 1.700 Ich möchte nur die Kollisionskreise auf Feinde zeichnen. Wenn der Debug-Modus aktiv ist, möchte ich sie ein- und ausblenden können , indem ich D auf der Tastatur drücke und den Debug-Modus insgesamt ein- 43. L14 Sprite-Animation: Wir können in der Tabelle navigieren und entscheiden, welchen Frame wir gerade ausschneiden, indem wir den Wert anpassen , den wir an die Methode draw image übergeben Hier kann das Quell-X-Argument so aussehen. Null-fache der Breite eines einzelnen Frames ergibt das Einmalige der Breite, wir erhalten diesen Frame. Dieser Rahmen ist fünfmal breit. Die Art, wie ich dieses Sprysheet strukturiere. horizontalen Navigation rund um das Spry-Sheet werden Explosionsanimationsframes abgespielt Das Argument „Quelle Y“ bewegt sich in der Tabelle nach oben und unten Null mal Höhe ist dieser Rahmen Das ist einmal Höhe, zweimal Höhe, dreimal Höhe. an die Methode draw image übergebene Quell-Y-Argument bestimmt , welchen dieser vier verfügbaren Asteroiden wir zeichnen Ich werde diesen Multiplikator in eine Variable setzen. Ich nenne Frame Y. Ich kann ihn hier platzieren, aber vielleicht hat jeder Gegnertyp eine andere Tabelle mit einer anderen Anzahl von Ich füge Frame Y hier der Asteroiden-Unterklasse hinzu. Ich möchte es randomisieren. Jedem Asteroidenobjekt wird nach dem Zufallsprinzip eine der vier Varianten des Asteroiden zugewiesen , die ich für Sie erstellt habe Ich muss es in Methodenebene einschließen, um es auf ganze Zahlen abzurunden , weil es keine Zeile 1,5 gibt. Frame Y ist beispielsweise zufällig Null oder eins, oder zwei oder drei, da Feinde ein Objektpool sind Wenn ich so spiele, verwende ich ständig die ersten ein oder zwei Objekte wieder und wieder, sie in den Pool zurück nehme sie wieder mit. Deshalb ändern sich die Bilder nicht Ich kann die Reihe zum Beispiel jedes Mal, wenn ein feindliches Objekt aktiviert wird, in der Startmethode nach dem Zufallsprinzip ordnen wenn ein feindliches Objekt aktiviert wird, in der Startmethode nach dem Im Moment ist das in Ordnung, obwohl es uns Probleme bereiten kann, wenn andere Feindtypen keine Force-Sprite-Rollen haben Ich kann die Feinde auch schwieriger machen , indem ich ihnen mehrere Leben gebe Ich gebe feindlichen Objekten eine benutzerdefinierte AT-Methode. Diese Methode verwendet den Schadenswert als Argument. Wenn diese Methode ausgeführt wird, reduzieren wir die Leben des Feindes um Schaden. Hier überprüfen wir die Kollision zwischen Feind und Projektilen. Anstatt den Feind sofort zurückzusetzen, nenne ich diese Treffermethode und gebe sie Das bedeutet, dass wir, wenn wir einen Feind mit einem Projektil treffen, dessen Leben um eins reduzieren , wenn der Feind weniger als eins Lebenspunkte Ich möchte Frame X weiter vergrößern , um die Explosionsanimation abzuspielen Wir müssen hier den Wert für Frame X verwenden er von 01234 aus zunimmt, schneiden wir verschiedene horizontale Sprite-Frames aus und spielen die Animation Ich könnte Frame X auf die übergeordnete Klasse setzen, aber lassen wir es hier in der untergeordneten Klasse neben Frame Y weitermachen. Wenn ich jetzt fünfmal auf der untergeordneten Klasse neben Frame Y weitermachen. einen Asteroiden treffe und sein Leben aufgebraucht habe, der Rest der Tabelle die Explosionsframes ab aber lassen wir es hier in der untergeordneten Klasse neben Frame Y weitermachen. Wenn ich jetzt fünfmal auf einen Asteroiden treffe und sein Leben aufgebraucht habe, spielt der Rest der Tabelle die Explosionsframes ab. Jetzt müssen wir auch den Asteroiden zurücksetzen, wenn alle seine horizontalen Animationsframes Ich gebe ihm eine maximale Frame-Eigenschaft und setze sie auf sieben Wir wissen, dass wir es, sobald alle sieben Frames angezeigt wurden , sicher zurücksetzen und wieder in den Pool zurückkehren können. Wenn Frame X mehr als Max Frame ist, rufen wir Reset auf, da Feinde Mitglieder des Objektpools sind Wir müssen auch diese Werte zurücksetzen. Wenn wir diese Objekte wiederverwenden, gebe ich ihr die Eigenschaft Max. Leben und sie merkt sich, wie gebe ich ihr die Eigenschaft Max. Leben viele Leben der Gegner ursprünglich hatte Leben der Gegner ursprünglich , bevor wir anfingen, ihm Schaden zuzufügen. Wenn ich jetzt auf die Asteroiden schieße, kommen sie nicht zurück, weil ihr Frame X auf einem hohen Wert hängen geblieben ist und sie immer wieder zurückgesetzt Wenn wir einen Asteroiden starten, müssen wir sicherstellen, dass Bild X wieder auf Null gesetzt ist Sein Leben muss auf maximale Lebenserwartung zurückgesetzt werden. Toll, jetzt funktioniert alles. Wenn der Debug-Modus aktiv ist, möchte ich die Anzahl der Leben als große Zahl anzeigen , die über jedem Asteroiden gezogen wird Wir werden es hier in diesem bedingten Codeblock tun , der nur ausgeführt wird, wenn das Debuggen aktiv Ich rufe eine eingebaute Fülltext-Methode , die mindestens drei Argumente benötigt den zu zeichnenden Text und die X- und Y-Koordinaten des Ankerpunkts, in Bezug auf den der Text gezeichnet wird Der Text, den ich zeichnen möchte, ist Eigentum des Feindes und wir wollen, dass sich diese Zahl bewegt, wenn sich der Feind bewegt Ich gebe ihr die XY-Koordinaten des Feindes. Hier unten, wo wir unsere Leinwand einrichten, werde ich die Eigenschaft der Leinwandschrift definieren. Stellen wir es auf 30 Pixel ein. Der Helvetica-Füllstil wird weiß sein. Ich erhöhe die Schriftgröße. Wie Sie sehen können, wenn wir den Textankerpunkt genau in der Mitte unseres kreisförmigen Asteroiden positionieren , wird der Text nach rechts oben gezeichnet Von dort aus befindet sich der Ankerpunkt am linken unteren Rand Eigenschaft „Textausrichtung“ definiert , wo sich der Text, den wir auf der Leinwand zeichnen im Verhältnis zu seinem Ankerpunkt auf der horizontalen X-Achse befindet . Ich setze es auf Text zentrieren. Die Grundlinien-Eigenschaft definiert die Beziehung zwischen Text und seinem Ankerpunkt. Vertikal setze ich es auf Mitte. Jetzt ist der Text über dem Asteroiden zentriert. Schön, wenn wir den Asteroiden, die Explosion, zerstören , werden Animationsframes zu schnell abgespielt. Wir verwenden Spritesheets mit einer begrenzten Anzahl von Frames, um uns bei der Leistung zu unterstützen Jetzt brauchen wir eine Möglichkeit, die Geschwindigkeit zu verringern , mit der die Spritesheets die Frames austauschen , damit die Benutzer die Details der Animationen wirklich verstehen können die Details der Animationen wirklich verstehen Wir werden dieselbe Technik verwenden, die wir hier mit Timer- und Interval-Hilfsvariablen verwendet haben mit Timer- und Interval-Hilfsvariablen Das Sprite-Update wird falsch sein. Es wird für diesen einen Animationsframe zu echten periodischen Leisten wechseln für diesen einen Animationsframe damit alle Spritesheets in unserem Spiel zum nächsten Frame springen Sprite-Timer zählt die Delta-Zeit immer wieder von Null bis zum Sprite-Intervallwert Jedes Mal, wenn das Intervall erreicht ist, löst es Sprite-Update aus und setzt auf Null zurück, sodass es Nehmen wir an, ich möchte alle 150 Millisekunden ein Sprite-Update auslösen . Dann schreiben wir die Logik, schreiben wir die Hier wiederholen wir dieselbe Logik, mit der wir Wenn der Sprite-Timer kleiner als das Sprite-Intervall ist, erhöhen Sie den Sprite-Timer immer weiter um die Delta-Zeit, die wir bereits von zuvor berechnet Es ist die Zeit in Millisekunden. Unser Computer benötigt, um einen anderen Animationsframe bereitzustellen, was bedeutet, dass der Sprite-Timer höher als das Intervall ist Während der Delta-Zeit wurden genügend Millisekunden gesammelt. Wir können den Sprite-Timer auf Null zurücksetzen, sodass er erneut zählen kann und Sprite Update auf True gesetzt Ich möchte, dass Sprite Update nur für diesen einen Animationsframe wahr ist nur Hier setze ich es wieder auf falsch. Dieser Codeblock bedeutet nun, dass das Sprite-Update für einen Frame auf true gesetzt ist Jedes Mal, wenn wir einen Sprite-Intervallwert festlegen, 250 Millisekunden erreicht Wenn ich ein Consolo-Sprite-Update durchführe, sehen wir, dass es falsch ist und Toll, jedes Mal, wenn es wahr ist, möchte ich Spritesheets animieren zu einem neuen Frame im Spritesheet springen Vergessen wir nicht, die Consologe zu löschen. Wir werden uns hier mit der Sprite-Animationslogik befassen, falls die Leben des Asteroiden weniger als eins sind Nur wenn das Sprite-Update wahr ist, nur zehn. Wir erhöhen Frame x um eins. Jetzt wird die Explosionsanimation langsamer abgespielt. Wir können diese Geschwindigkeit mithilfe der Sprite-Interval-Eigenschaft auf alles einstellen, was wir wollen Das Problem ist, dass ich jetzt, wo die Explosion langsamer abläuft, feststelle, dass die Projektile immer noch mit einem Asteroiden kollidieren, Ich möchte, dass die Projektile Explosionen ignorieren . Wir überprüfen Kollisionen zwischen Feind, Asteroid und Projektilen nur , wenn die Lebenspunkte des Feindes mehr oder gleich eins sind Wenn weniger als eins Leben übrig ist und eine Explosionsanimation abgespielt wird, fliegen Projektile durch das Ja, das ist viel besser. Ich kann Trefferfelder ein- und ausblenden, indem ich den Buchstaben D drücke, indem ich den D-Bug-Modus aktiviere und 44. L15 Lobstermorph-Feind: Ich möchte, dass die Asteroiden nur ein Leben haben. Es wird ein einfacher, einfacher Feindtyp sein. Unsere Laser sind für die Asteroidenabwehr kalibriert und wir können jeden einzelnen leicht mit einem einzigen Treffer zerstören Fügen wir einen anspruchsvolleren Klistiertyp hinzu. Sie können Lobster Morph Spreadsheet im Abschnitt Ressourcen unten herunterladen im Abschnitt Ressourcen unten Wie üblich habe ich es in meinen Projektordner gelegt und hier eingefügt, ihm eine Vorstellung von Lobster Morph zu geben Zum jetzigen Zeitpunkt ist es einfach, mehr Gegnertypen zu implementieren , da wir Großteil der Logik bereits implementiert haben Ich kopiere den gesamten Codeblock für Asteroid und benenne ihn um Ich verweise diese Punktbildeigenschaft auf die Hummer-Morph-Tabelle Ich kann den maximalen Frame auf 14 setzen. Wir wissen, dass es sicher ist, den Feind zurückzusetzen, wenn wir diesen Punkt erreichen . Ich habe die Tabellen mit einem bestimmten Plan im Hinterkopf erstellt. Ein Asteroid wird ein Leben haben, wenn wir ihn treffen. Im Rest der Tabelle wird die Explosionsanimation wiedergegeben Lobster Morph wird acht Leben haben. Jedes Mal, wenn wir ihn treffen, springen wir um ein Bild, bis wir alle acht Leben aufgebraucht Dann werden die restlichen Frames in einer Animationssequenz abgespielt Hier unten kommentiere ich die Klasse der Asteroiden aus und schiebe stattdessen den Gegnertyp Hummer-Morph in die gegnerische Pool-Reihe Okay, wir haben ein paar Aliens im Anmarsch. Ich spiele, um zu sehen, was wir bisher haben. Ich kann D drücken, um den Debug-Modus zu deaktivieren. Ich kann sehen, dass, wenn ich sie achtmal treffe und die Leben aufgebraucht habe, die gesamte Tabelle abgespielt wird Ich kann mir hier die Hit-Methode ansehen. Und wenn wir den Feind treffen und seine Leben um den Schadenswert reduzieren, kann ich Frame X so einstellen, dass es die maximale Anzahl an Leben minus der aktuellen Leben ist. Das wird schon funktionieren. Es geht jedes Mal in eine neue Phase, wenn es ein Leben verliert, und dann spielt sich die Explosion ab. Ich kann es hier anders machen. Ich kann sagen, ob die Leben eines Feindes mehr oder gleich eins sind , jedes Mal, wenn wir ihn treffen. Erhöhen wir Frame X um eins, erhalten wir dasselbe Verhalten. Gegner werden sofort zurückgesetzt, wenn sie mit dem Spieler oder dem Planeten kollidieren Wenn ein Feind mit dem Planeten kollidiert, setze ich seine Lebenspunkte sofort auf 20, wodurch die Tabelle abgespielt wird Ich möchte auch, dass die Feinde aufhören, sich an den Rändern des Planeten zu bewegen Ich habe Geschwindigkeit x20 und Geschwindigkeit y20 eingestellt. Eine Kollision mit dem Planeten passiert, wenn der Feind den Planeten trifft Jetzt hören sie auf und spielen ihre Animationssequenz ab. Ich finde, das sieht interessanter aus. Sie spielen die Sequenz ab, wenn der aktuelle Frame mehr als das maximale Frame ist, sie setzen sie zurück und kehren zum Objektpool zurück. Wenn der Feind mit dem Spieler kollidiert, setze ich die Lebenspunkte einfach auf Null Um es animieren zu lassen. Ich werde seine Bewegung nicht aufhalten Du kannst hier deine eigenen Entscheidungen treffen, gestalte das Spiel so, wie du es willst. Ich zeige dir nur, wie die Codebasis funktioniert. Wir haben einen Bug, wenn du einen Feind konsultierst. Jedes Mal, wenn wir einen aktivieren, werden Sie feststellen, dass einige von ihnen außerhalb des Bildschirms hängen bleiben überhaupt nicht von einer Seite des Bildschirms kommen. Weil ihre horizontale Position keine ist, keine Zahl. Weil ich hier den Punktradius des Spiels gesagt habe, muss ich stattdessen den Radius des Feindes verwenden. Das ist dir wahrscheinlich schon einmal aufgefallen. Wie dem auch sei, jetzt ist es behoben. Ich möchte die Gegner rotieren lassen, um sicherzustellen, dass sie dem Planeten zugewandt in ihrer Bewegungsrichtung dem Planeten zugewandt sind. Jedes Mal, wenn wir die Leinwand drehen, summiert sich das und wirkt sich auf alles aus, was auf der Leinwand gezeichnet wird. Ich möchte, dass jeder Feind um seinen individuellen Winkelwert gedreht wird. Ich werde alle Änderungen, die ich an den Zeichnungscodes der Gegner vornehme, einschränken , indem ich diesen Codeblock zwischen sicheren Methoden und Wiederherstellungsmethoden umschließe. Wir speichern den Canvas-Status hier alle Änderungen vor, die wir auf diese Zeichenaufrufe anwenden möchten . Dann kehren wir zu dem zurück, was vorher war , um sicherzustellen, dass die danach gezeichneten Formen auf der Leinwand unverändert bleiben Ich kann nicht einfach drehen, wenn ich um 0,1 Radiant drehe. Hier werdet ihr feststellen , dass bei Navas alles ein bisschen schief läuft, um irgendwas zu drehen Zuerst müssen wir den Mittelpunkt der Rotation verschieben, was der Koordinate 00 auf Navas über den Mittelpunkt des Objekts entspricht . Wir wollen rotieren Ich übersetze den Zx-Punkt Y des Feindes, den wir gerade zeichnen werden Jetzt, wo wir es übersetzen, müssen wir bedenken, dass wir Mittelpunkt der Rotation als auch den Punkt, der auf Leinwand als Koordinate 00 gilt, übersetzen als auch den Punkt, der auf Leinwand als Koordinate 00 gilt, Koordinate 00 gilt Wir übersetzen es über die Position des Feindes. Dann gehen wir weiter um tatsächlich den Feind zu zeichnen Wir haben die gegnerische Position effektiv verdoppelt und sie weiter nach rechts und an den unteren Bildschirmrand verschoben. Die Position des Feindes wird jetzt durch die Übersetzung definiert. Also müssen wir tatsächlich alles an der Koordinate 00 zeichnen. Wenn ich feindliche Kollisionskreise an der Koordinate 00 zeichne , bekommen wir das. Ich muss das auch für die Textkoordinaten tun , die feindliche Leben anzeigen. Für das eigentliche Bild, das über dem Kreis zentriert ist. Indem wir es um den Radius verschieben, machen wir hier und hier den Minusradius Um etwas auf der Leinwand zu drehen, übersetzen wir das Objekt, das wir drehen möchten Dann zeichnen wir das Objekt an der Koordinate 00, weil die Position des Objekts jetzt durch Translate definiert ist. Jetzt kann ich es frei drehen und es funktioniert, wenn ich um 0,1 Radiant, 0,5 Radiant Stattdessen verwende ich eine Eigenschaft für den distalen Winkel. Ich definiere es hier, 1 Radiant bedeutet, dass alle Gegner so gedreht werden, 2 Ich möchte, dass der Drehwinkel von der relativen Position zwischen dem Feind und dem Planeten abhängt der relativen Position zwischen dem Feind und dem Planeten Wir haben bereits die Lkm-Hilfsmethode geschrieben und verwenden sie, um die Geschwindigkeit des Feindes festzulegen Lkm-Methode gibt uns Richtungsvektoren sowie den vertikalen und horizontalen Abstand von DX und DY zwischen den beiden Objekten, die wir ihr als Argumente übergeben, die ich benötige Ich kann sehen, dass dies Elemente mit Indizes von 2,3 sind , die in diesem Array zurückgegeben Anfangs habe ich den Winkel 20 eingestellt. Das ist nicht wirklich wichtig, denn wenn die Startmethode ausgeführt wird, legen wir diesen Winkel auf der Grundlage der relativen Position zwischen diesem Feind und dem Planeten fest. wir schon gemacht , als wir das Spielerraumschiff vom Mittelpunkt des Planeten weggedreht Spielerraumschiff vom Mittelpunkt des Planeten weggedreht Hier ist es dasselbe. Wir verwenden die integrierte Methode 102, die uns einen Winkel zwischen einer positiven X-Achse und einer Linie ergibt, die auf einen bestimmten Punkt projiziert wird. Wir wissen, dass sie zuerst dy und dx als zweites Argument erwartet . Ich muss diese Argumente in dieser Reihenfolge übergeben. Feinde zeigen nicht , wohin ich sie haben möchte. Aber das spielt keine Rolle, weil wir wissen, dass der Winkel, den wir einnehmen, relativ zur Startposition des Feindes und des Planeten ist . Startmethode wird nur einmal ausgeführt, um den Feind aufzustellen, und ich kann den resultierenden Wert auf viele Arten anpassen. Ich könnte den ganzen Hummer oder die gesamte Tabelle in meinem Grafikeditor rotieren , und dann könnte ich das verwenden, aber ich könnte das auch einfach mit Code machen Ich sehe, dass ich den Feind einfach um 90 Grad drehen muss , also um einen Viertelkreis, um sicherzugehen, dass er dem Planeten zugewandt Ich füge hier den Wert der Methode Pi hinzu. Wenn ich die Methode Pi hinzufüge, drehe ich sie um 3,14 Radiant, um 180 Grad, einen Halbkreis Ich möchte nur um einen halben Pi, um 90 Grad, um einen Viertelkreis drehen um 90 Grad, um einen Viertelkreis Auch wenn Sie Methode 8102 noch nicht vollständig verstehen, denken Sie daran, wie man sie benutzt Geben Sie ihr die Punkte , die für die gewünschte Rotation relevant sind , und sehen Sie, welches Ergebnis Sie erzielen Dann können Sie diesen Winkel einfach weiter anpassen , wie wir es gerade getan haben. Vielleicht gibt es noch einen einfacheren Weg, dies zu tun, an den ich gerade nicht denke. Ich weiß, dass einige von Ihnen mir in den Kommentaren Ihre Tricks und Verbesserungen geben werden , und das weiß ich zu schätzen. Wie dem auch sei, feindliche Aliens schauen jetzt auf den Planeten zu. Jedes Mal, wenn wir sie treffen, verschieben wir die Tabelle in den nächsten Frame, wenn die Leben aufgebraucht sind Animation, Abspielen Das Animieren von Tabellenkalkulationen wie diesem ist sehr billig Leistung angeht, wird Ihr Spiel flüssig laufen. Wenn Sie mit einer Sprite-Animation mit niedriger Bildrate wie dieser zufrieden sind , denke ich, dass das Spiel lebendig aussieht Selbst mit diesen leistungsfreundlichen Sprite-Animationstechniken gefällt mir, wie es auf Spieleraktionen reagiert Dadurch fühlt sich das Spiel sehr interaktiv und reaktionsschnell an. 45. L16-Spiel-Text: Textmethode für den Status „Unentschieden“ werden Punktestand, Spielerleben und Nachrichten über das Ende des Spiels angezeigt. Es wird Kontext als Argument erwartet. Die Schrift wird eine Wirkung von 30 Pixeln haben. Sie können hier beispielsweise ganz einfach beliebige benutzerdefinierte Google-Schriftarten verwenden, wenn Sie möchten. Importieren Sie sie einfach in Index-HTML. Geben Sie Canvas diese Schriftfamilieneigenschaft im Stil CSS und verwenden Sie hier den Namen dieser Schriftfamilie. Anstatt Wirkung zu zeigen, wird es funktionieren. Ich habe Custom Fund schon oft in anderen Spielen verwendet . Lassen Sie uns einfach Standard-Impact-Fonds verwenden und uns auf andere Dinge konzentrieren , die ich als integrierte Fülltextmethode bezeichne. Der Text wird als Punktezahl angegeben, die Koordinaten lauten 2030. Ich möchte, dass der Text über dem Planeten gezeichnet wird , aber unter dem Spieler und den Projektilen und Feinden entsteht ein netter Ebeneneffekt, wenn sich der Text in der Mitte von Spielobjekten befindet Wenn ich „ Statustext zeichnen“ aufrufe und ihm den Kontext übergebe, kann ich sehen, dass der Punktetext mittig ausgerichtet ist und die Leben von Feinden jetzt in einer anderen Schrift dargestellt werden Durch diese Deklaration wurde der gesamte Canvas-Status erneut geändert. Ich verpacke es in Safe and Restore, sodass ich Statustextspezifische Einstellungen vornehmen kann , ohne andere Texte und Formen zu beeinflussen , die auf der Leinwand gezeichnet wurden. Ich sagte, Text nach links ausrichten. Ich erstelle diese Score-Eigenschaft. Ich gehe zur Hauptfeindklasse über, wenn der Feind Abspielen der Explosionsanimation beendet und wieder in den Objektpool zurückgesetzt wird. Ich erhöhe die Punktzahl um die maximale Anzahl an Lebenspunkten des Feindes, was bedeutet, dass ein einfacher Asteroid, Feind einen Punktepunkt gibt Hummer-Morph, der Feind gibt acht Punktepunkte. Es hat hier acht Leben, ich füge Punkte hinzu, um den Wert zu ermitteln. Jedes Mal, wenn ich einen Feind zerstöre und die Explosionsanimation zu Ende gespielt wird, erhalte ich acht Punkte Sie können sehen, dass wir jedes Mal, wenn ein Feind explodiert, acht Punkte erhalten Ich möchte dem Spieler nur dann Punktepunkte geben, wenn wir den Feind zerstören, indem wir ihn abschießen. Der Feind wird zerstört, weil er mit dem Spieler oder dem Planeten kollidiert Dafür wollen wir keine Punktepunkte vergeben. Ich gebe jedem Feind eine Eigenschaft, die als kollidiert bezeichnet wird. Anfangs habe ich sie auf „Falsch“ gesetzt. Wenn wir den Feind starten, müssen wir ihn immer wieder auf Falsch zurücksetzen. Wenn der Feind mit dem Planeten kollidiert, setzen wir Collided auf True Ebenfalls, wenn der Feind mit dem Spieler kollidiert. Wenn der Feind mit einem Projektil kollidiert, tun wir nichts, was bedeutet, dass kollidiert falsch bleibt Hier fügen wir nur Punkte hinzu, wenn der Wert Collided falsch ist. Wenn ich das Alien erschieße, kriege ich acht Punkte Wenn der Feind mit dem Spieler oder dem Planeten kollidiert, bekommen wir für diese Klasse keine Punkte Ich erstelle eine Eigenschaft namens Winning Score. Zu Testzwecken habe ich sie zunächst auf zehn gesetzt. Wir werden hier in der Render-Methode mit der Bedingung „Gewinnen oder Verlieren“ umgehen . Wenn die Punktzahl mehr oder gleich der Gewinnpunktzahl ist, ist das Spiel vorbei. Hier unten in der Textmethode für den Status des Unentschieden werden wir uns um Meldungen kümmern, dass das Spiel vorbei ist wahr ist. Ich möchte zwei große Nachrichten in der Mitte des Bildschirms haben. Wir setzen den Text auf eine Linie, um ihn zu zentrieren. Ich kann zum Beispiel zwei LED-Variablen mit den Namen Nachricht eins und Nachricht zwei erstellen . Ihnen wird ein anderer Text zugewiesen , wenn wir gewinnen und wenn wir verlieren. Wenn wir gewinnen, wenn die Punktzahl höher ist als die Gewinnpunktzahl, heißt es in der ersten Nachricht, dass Sie gewinnen. Nachricht zwei, Ihre Punktzahl lautet Wir zeigen den Punktewert plus ein Ausrufezeichen wie Jetzt werden wir sie zeichnen. Nachricht eins wird groß sein, 100 Pixel. Impact: Auch hier können Sie anstelle von Impact Ihre eigene benutzerdefinierte Schriftart verwenden . Filltext-Methode zeichnet eine Nachricht horizontal in der Mitte und 200 Pixel vertikal von oben Ein weiterer Fülltext für Nachricht zwei, wiederum horizontal in der Mitte, und sagen wir, 550 Pixel vertikal von oben entfernt Lasst uns versuchen, das Spiel zu gewinnen. Ich benötige zehn oder mehr Punkte. Toll, ich möchte, dass die zweite Nachricht kleiner ist. Wie wäre es nochmal mit 50 Pixeln? Ich schieße auf Feind eins, Feind zwei. Los geht's. Ja, das sieht besser aus. Ich möchte , dass die Gegner nicht mehr kommen wenn Game Over in einem Pool passiert, ein neues feindliches Objekt aus dem Pool, wenn Gameover falsch ist 46. L17 Spieler lebt: Nun, wie können wir dieses Spiel verlieren? Wir können dem Spieler zum Beispiel eine begrenzte Anzahl von Leben geben. Ich habe diese Leben hier auf dem Hauptspielobjekt platziert. Ich gehe hier runter. Oh, diese Restaurierung muss hier unten sein. Andernfalls ändern sich die Zahlen , mit denen wir die Leben von Feinden anzeigen. 250 Pixel wirken sich negativ aus, wenn die Meldung „ Game over“ angezeigt wird. Das ist jetzt behoben. Ich möchte Spielerleben unter dem Punktestand ziehen. Ich erstelle eine Vierer-Schleife. Es wird einmal für jedes Leben ausgeführt und es wird für jedes ein Rechteck gezeichnet. Der linke Rand wird 20 Pixel groß sein. horizontale Position bestimmt den Abstand zwischen den Rechtecken, er beträgt also 15 Pixel Der Abstand zwischen ihnen wird dadurch multipliziert , dass der Abstand im Tams-Index in der vertikalen Position mit den vier Schleifen vielleicht 60 Pixel vom oberen Rand entfernt Die Breite jedes Rechtecks sollte kleiner als der Leerraum sein. Bei zehn beträgt die Höhe beispielsweise 30 Pixel. Sie können diese Werte ändern , um zu sehen, was sie bewirken. Das ist der linke Rand, das ist der horizontale Abstand. Wenn die Breite jedes einzelnen Rechtecks gleich oder größer als der Abstand ist, werden sie zu einem einzigen Balken. Sie können solche Ladebalken bauen, wenn Sie möchten. Ich möchte, dass sie hier getrennt sind, also nehme ich zehn, schätze ich. Wenn ein Feind mit dem Planeten kollidiert, nehme ich diesem Spiel Leben und reduziere es um eins Außerdem teste ich es, wenn es zu einer Kollision mit dem Raumschiff des Spielers kommt Wenn wir kollidieren, kostet das sofort alle fünf Leben. Weil es ein Leben pro Animationsframe kostet. Wir möchten, dass dieser Codeblock nur für Feinde läuft , deren Leben mehr oder gleich einem ist. Wir geben diesen Codeblock ein und setzen die Lebenspunkte auf Null, wodurch verhindert wird, dass derselbe Codeblock ausgeführt wird. Auch das sollte das Problem beheben. Ich mache dasselbe für Spielerkollisionen. Ja, jetzt nimmt eine Kollision nur ein Leben weg. Perfekt. Hier im Block mit dem Zustandscode „Gewinnen und Verlieren “ sage ich, wenn die Punktzahl mehr oder gleich der Gewinnpunktzahl ist oder wenn die Lebenspunkte in beiden Fällen weniger als eins sind, setze das Spiel auf „Wahr“ , um zu verhindern, dass Feinde kommen. Und es wird die große Nachricht eins und Nachricht zwei anzeigen. Wenn wir gewinnen, senden wir Botschaften an diese anderen, was bedeutet, dass wir alle Leben verloren haben. In der ersten Nachricht heißt es, dass du verlierst. In der zweiten Nachricht heißt es, versuchen Sie es erneut. Ich teste es. Ich schieße auf Feinde, ich gewinne. Feinde kollidieren mit dem Planeten. Wir verlieren Leben, wir verlieren alle Leben und die Botschaft zu verlieren scheint großartig Wir haben eine komplette Spielschleife. Zunächst werde ich den D-Bug auf False setzen. Wir können es immer noch ein- und ausschalten, indem wir den Buchstaben D auf der Tastatur drücken Ich möchte alle Feindtypen gleichzeitig ins Spiel bringen gleichzeitig ins Spiel Hier, wo wir einen Feindpool erstellen, erstelle ich eine Zufallszahlenvariable. Ich setze sie auf einen Zufallswert 0-1, wenn die Zufallszahl größer als 0,25 ist In 25% der Fälle erzeugen wir einen Asteroiden L. In den anderen 75% der Fälle erstellen wir den Hummer-Moorph-Asteroiden der einfache Hummer-Morphs sind starke Gegner, die sich verändern und spalten, sodass es länger dauert, sie zu besiegen. Ich möchte öfter einen neuen Feind hinzufügen. Nehmen wir an, ich möchte alle 1 Sekunde Gegner schneller oder langsamer bewegen können. Wir haben dasselbe mit Spielerprojektilen gemacht. Ich erstelle eine Eigenschaft für einen Geschwindigkeitsmodifikator. Ich möchte sie verlangsamen. In diesem Fall ist der Geschwindigkeitsmodifikator eine Zufallszahl zwischen 0,1 und 0,6. Dann füge ich hier den Geschwindigkeitsmodifikator hinzu, dem wir die horizontale und vertikale Geschwindigkeit berechnen Ich skaliere den Vektor hier. Ich bevorzuge das Spiel so. Wir haben viel mehr Feinde auf dem Bildschirm, aber sie bewegen sich alle langsamer, sodass wir mehr Zeit haben, Strategien zu entwickeln Es liegt an dir, zu entscheiden, ob es im Spiel mehr um schnelle Reaktionen oder um Positionierung gehen soll mehr um schnelle Reaktionen oder um Positionierung Endstrategie: Sie können die Frequenz und Bewegungsgeschwindigkeit der Feinde anpassen , je nachdem, welche Art von Gameplay Sie bevorzugen Wie wäre es mit einem Feindintervall, 800 Millisekunden und 30 Leben, Ich werde diese Werte später anpassen. 47. L18 Beetlemorph-Enemy: Ich habe kürzlich ein weiteres Weltraumspiel entwickelt Klassiker Space Invaders inspiriert wurde Ich habe dafür gesorgt, dass die Tabellen aus diesem Spiel kompatibel sind Lassen Sie mich Ihnen hier zeigen, wie wir sie verwenden und einfach in diesem Projekt verwenden können . Wenn du diesem Kurs folgst, hast du bereits die Split-Blätter Falls nicht, kannst du sie im Abschnitt „Ressourcen“ unten herunterladen sie im Abschnitt „Ressourcen“ unten Ich importiere die Beetle Morph-Tabelle in das Projekt. ist extrem einfach, diesem Spiel weitere Feindtypen hinzuzufügen, da wir die gesamte Logik bereits geschrieben Ich kann diesen Codeblock einfach kopieren. Ich benenne es in Beetlemorph um. Ich passe diese Eigenschaft für das Punktbild überprüfe die Tabelle . Maximal drei Frames und eins Leben Dieser Feind ist leicht zu besiegen. Hier unten, wo ich einen Feindpool erstelle, kann ich sagen, ob Zufallszahl mehr als 0,5 ist. Füge Käfer Moorph Hier muss ich vorsichtig mit Klammern sein. Ich muss den Zustand hier ändern. Wenn die Zufallszahl kleiner als 0,25 ist, erzeugen wir einen Asteroiden. Wenn die Zahl kleiner als 0,5 ist, erzeugen wir Beetle Moorph. In den anderen 50% der Fälle erzeugen wir einen Hummer-Morph Das war einfach. Jetzt hat das Spiel noch mehr Abwechslung. 48. L19 Rhinomorph-Feind: Nachdem wir den Prozess verstanden haben, nehmen wir einen weiteren Feindtyp und Ich kopiere diesen Codeblock und erstelle feindlichen Typ Rhinomorph, den gepanzerten Feind Auch hier kannst du die Tabelle unten herunterladen, falls du sie nicht aus dem Space Invaders-Projekt hast Hier füge ich noch ein L hinzu, wenn die Zufallszahl kleiner als 0,75 ist Wir schieben Rhinomorph in feindliche Pool-Array. Der Code-Editor hat diesen Klassennamen anders eingefärbt , um mich daran zu erinnern, dass ich die Klasse immer noch nicht geschrieben habe Ich kopiere diesen Codeblock. Ich benenne es in Rhinomorph um. Ich weise dieses Punktbild auf die neue Tabelle. Max Frame wird sechs Jahre alt sein, er wird vier Leben haben. Jetzt hat unser Spiel vier verschiedene Feindtypen. Einfache Gegner mit einem Leben sind Asteroid und Beetle Morph, ein gepanzerter Feind, der sich in kleinere Klone aufteilt Als wir ihn trafen, hieß es Feindintervall auf 1.200 Millisekunden und das Spiel auf zehn Lebenspunkte , das Feindintervall auf 1.200 Millisekunden und das Spiel auf zehn Lebenspunkte, wenn ihr euch die erweiterte Space Invaders-Klasse da drüben angeschaut habt. Wir haben auch einen aufblasbaren Gegner namens Tintenfisch, der Spielergeschosse absorbiert, bis er platzt. Wir haben auch Eagle Moorph, einen Feind, der seine eigenen Körpersegmente opfert und sie als mutierte Projektile aus Schleim und Tentakeln auf uns zurückspuckt zurückspuckt . All diese Kunstressourcen sind voll kompatibel mit dieser Codebasis . Eine Herausforderung für dich besteht darin, sie anzunehmen , wenn du der erweiterten Klasse folgst und sie in diesem Spiel implementierst. Wenn du möchtest, kannst du auch den einzigartigen Feind aus dieser Klasse, den Lobsta Morph, nehmen den einzigartigen Feind aus dieser Klasse, und ihn im Space Invaders-Spiel implementieren Diese beiden Spiele sind vollständig kreuzkompatibel. Ich möchte die Punktzahl nur erhöhen, wenn Game Over falsch ist. So ist es einfach zu machen und wenn Sie meine Anleitung benötigen, implementiere ich Lobster Morph in das Spiel Space Invaders in der Wenn du dir das auf meinem YouTube-Kanal ansiehst, sind die beliebtesten und neuesten erweiterten Bonusklassen auf der Info-Seite verlinkt Ich hoffe, du hattest Spaß bei diesem Weltraumabenteuer. Das war ein großes Projekt. Gut gemacht. Wenn Sie so weit gehen, lassen Sie mich wissen, ob Sie etwas Wertvolles gefunden haben , und lernen Sie etwas Neues, indem Sie einen kurzen Kommentar hinterlassen Ich frage mich, welcher Teil dieses Projekts für Sie am interessantesten war. Kannst du einfach einen kurzen Kommentar hinterlassen und mir sagen, welcher von allen dein Lieblingsfeindtyp ist? Er kann entweder sein, wie er aussieht oder sein einzigartiges Verhalten und seine einzigartige Mechanik. Ich freue mich darauf zu sehen, was ihr denkt und welcher euer Favorit ist. Ich werde es später sehen. 49. Projekt 3: JavaScript-Mobilspiel: Hauptsitz. Lass uns ein Handyspiel bauen. Gehen Sie dem Gasriesen nicht näher, es bewegt sich etwas in der oberen Atmosphäre. Zuerst werden wir die gesamte Spiellogik schreiben und alle Hauptfunktionen implementieren. Du kannst diese Standardvorlage später für viele verschiedene berührungsbasierte Vollbild-Spiele wiederverwenden für viele verschiedene berührungsbasierte Vollbild-Spiele Und dann fügen wir Grafiken, Animationen und Sounds hinzu und machen daraus ein Cartoon-Space-Horror-Spiel Ich möchte, dass du Projekte baust, auf die du stolz sein kannst und die du deinen Freunden zeigen kannst. Dieses Spiel wird ein Spiel im Vollbildmodus sein. Touch-Ereignisse lassen sich auf jedem Gerät abspielen, von einem großen horizontalen Computerbildschirm bis hin zu einem kleinen vertikalen Mobiltelefon Sie können es also mit sich herumtragen, wenn Sie allen zeigen möchten, was Sie gelernt haben und was Sie mit Java Script machen können Unsere Anwesenheit löste einen Fressrausch aus. Sie schienen sich zu den Lebenszeichen unserer nicht mechanischen Besatzungsmitglieder hingezogen zu fühlen den Lebenszeichen unserer nicht mechanischen Besatzungsmitglieder Sofortiger Rückzug wies darauf hin, dass er in den überkritischen Flüssigkeitsschichten dieses fernen Gasriesen endemisch den überkritischen Flüssigkeitsschichten dieses fernen Gasriesen Diese Kreatur lebt unter extremem Druck. Sie können solide Hindernisse überwinden. Wir müssen sie treffen, wenn sie sich in ihrem festen Zustand befinden, um Ihre Angriffe sorgfältig zu planen. 50. Projekt 3-Setup: In Index-HTML erstelle ich eine einfache leere Webseite. Ich gebe ihr einen Titel, ich verlinke das CS-Stylesheet, ich erstelle ein fünftes HTML-Canvas-Element mit der ID eines Canvas-Elements Und ich verlinke meine Java-Script-Datei im CSS-Stil. Ich führe einen globalen Reset durch , um sicherzustellen, dass wir in verschiedenen Browsern ein konsistentes Verhalten erzielen . Indem ich die Standardstile entferne, gebe ich der Leinwand eine absolute Randposition. Ich zentriere es vertikal und horizontal in der Mitte der Seite. Sie können zwei Hintergrundbilder im Bereich Ressourcen herunterladen . Unten. Ich nehme den horizontalen Hintergrund und lege ihn als Hintergrund-URL fest. Hier ist die Hintergrundgröße das Titelbild, wodurch das Bild auf die kleinstmögliche Größe skaliert wird . Dadurch werden Breite und Höhe immer noch vollständig abgedeckt, sodass kein leerer Raum übrig bleibt. Ich erstelle einen Diff mit einer Klasse von Steuerelementen. Wir werden eine Vollbildtaste und eine Reset-Taste haben. Wir werden auch den Vollbildmodus steuern und mit der Tastatur zurücksetzen, aber wir werden diese Muster für mobile Geräte benötigen. Sie möchten, gebe ich diesen Schaltflächen Höhe, Wenn Sie möchten, gebe ich diesen Schaltflächen Höhe, schwarzen Hintergrund und weißen Text. Sie können auch eine benutzerdefinierte Schriftart verwenden. Zum Beispiel kann ich hier zu Google Fonts gehen. Ich suche nach einer Schrift namens Bangers. Ich wähle hier die Schriftart aus und sie gibt mir Links, die in den HTML-Index aufgenommen werden müssen Idealerweise hier oben, bevor ich Style-CSS wie dieses einfüge. Jetzt kann ich überall in meiner Codebasis Bangers verwenden. Zum Beispiel gebe ich hier für die Schrift auf den Buttons auch einen weißen Rand 51. Alles reaktionsschnell machen: Wir werden die gesamte Logik hier mit Java-Skript in die JS-Hauptdatei schreiben . Vielleicht werde ich das später in mehrere Dateien aufteilen. Hier können wir unsere Klassen definieren, aber wir stellen sicher, dass wir sie erst instanziieren, nachdem das Load-Event ausgelöst wurde Nachdem alle Ressourcen geladen und verfügbar sind. Alle Stylesheets, Skripte, HTML-Elemente, Bilder und so weiter. Sobald die Seite geladen wurde und wir wissen, dass das HTML-Cannavas-Element verfügbar ist, können wir unser Projekt einrichten Ich verweise mit Get Element by ID auf Java-Skript auf mein Canvas-Element , ich definiere CTX Dadurch wird eine Instanz der integrierten Canvas-zu-D-API gespeichert. Ich sagte Breite und Höhe der Leinwand, um das gesamte Browserfenster in Sty Sss abzudecken , ich sagte, die Hintergrundposition zur Mitte, um sicherzustellen, dass der Planet immer in der Mitte ist Bei jeder Bildschirmgröße positioniere ich die Bedienelemente wie folgt links unten Ich kann die Schaltflächen auch vertikal anordnen , indem ich eine Flexbox, transparenten Hintergrund und keinen Rand an den Steuerelementen verwende. Hier, der Rand der Schaltflächen beträgt fünf Pixel, ich kann dem Feld Schatten und Textschatten geben. Zum Beispiel 0,3 Sekunden beim Schweben, Aktivieren und Fokussieren Wir animieren Boxschatten und Textschatten, und auch die Spielklasse für den Button-Hintergrund wird das Haupt-Gehirn der Codebasis sein Es wird den Status unseres Projekts verwalten. Sie wird Cannavas als Argument erwarten. Im Inneren konvertieren wir es in eine Klasseneigenschaft und daraus extrahieren wir Breite und Höhe, weil ich sicherstellen möchte, dass Breite und Höhe des Spiels immer der Breite und Höhe von Cannavas entsprechen der Breite und Höhe von Wir geben ihm eine benutzerdefinierte Methode zur Größenänderung. Es wird Breite und Höhe als Argumente erwarten. Und wir werden die Größe des Canavas-Elements an diese neuen Werte anpassen. Außerdem werden die Eigenschaften Breite und Höhe von Zeile 8.9 auf diese neuen Werte festgelegt von Zeile 8.9 auf diese Ich erstelle eine Instanz meiner Spielklasse Expects Canvas. Also übergebe ich ihr die Canvas-Variable aus Zeile 20. Wenn wir eine Instanz einer Klasse erstellen, der gesamte Code in wird der gesamte Code in einem Klassenkonstruktor automatisch Zeile für Zeile ausgeführt Ich kann das nutzen und meinen Code, den ich ausführen möchte , hier einfügen An dem Punkt, an dem eine Instanz dieser Spielklasse mit dem neuen Schlüsselwort erstellt wird, kann ich hier sogar Event-Listener platzieren Wir werden auf das Resize-Event warten. Das einzige Problem ist, dass sich der Event-Listener auf dem Fensterobjekt befindet und ich ihn lexikalisch in meiner Wenn ich von diesem Callback aus auf dieses Schlüsselwort zugreifen möchte , bezieht es sich auf das Wenn ich versuche, die Größe meines Browserfensters zu ändern und die integrierten Größenänderungsereignisse ausgelöst werden, führt die Verwendung dieser Punktgrößenänderung hier zu Das Spielobjekt ist hier von innen nicht sichtbar. Wir benötigen dieses Schlüsselwort hier, um auf das Spielobjekt zu verweisen, damit wir auf Eigenschaften und Methoden zugreifen können, die zur Spielklasse gehören, einschließlich unserer benutzerdefinierten Methode zur Größenänderung Ich kann das tun, indem hier die sechs Pfeilfunktionen von ES anstelle einer regulären Callback-Funktion Eine Besonderheit von Pfeilfunktionen besteht darin, dass sie dies vom übergeordneten Bereich erben jetzt diesen Punkt verwenden , zeigt auf das Spielobjekt, das genau das ist, was wir brauchen Wenn nun das Ereignis zum Ändern der Größe ausgelöst wird, setzen wir die Breite und Höhe des Navas-Spiels auf 150 mal 150 Pixel Wenn ich das automatisch generierte Event-Objekt selbst als Konsole betrachte, kann ich es untersuchen Dieses Objekt gibt uns alle Arten von Informationen über das Ereignis, das passiert ist, in diesem Fall das Ereignis zur Größenänderung Zum Beispiel können wir hier in Target die Eigenschaften Breite und Innenhöhe finden, können wir hier in Target die Eigenschaften Breite und Innenhöhe finden die uns die neuen Abmessungen geben, auf die wir die Größe geändert haben. Ich kann diese Werte nehmen und die Größe meiner Canavas und meines Spiels an Wenn wir nun eine Instanz der Spielklasse in Zeile 29 erstellen, der gesamte Code im Konstruktor ausgeführt Der Event-Listener zur Größenänderung wird automatisch angewendet. Jetzt kann ich die Größe meines Browsers ändern und Canavas deckt auch den gesamten verfügbaren Browserbereich ab deckt auch den Wir können auch hier unten klicken, um mobile Geräte zu emulieren. Es ist nicht immer zu 100% genau , verglichen mit dem nativen Testen Ihres Codes auf diesem Gerät Aber es ist nah genug , um loszulegen. Das ist übrigens der Google Chrome-Browser. Es könnte für Sie anders aussehen, wenn Sie einen anderen Browser verwenden. Ich empfehle die Verwendung von Google Chrome für diesen Kurs. Ich kann jetzt verschiedene Geräte aus diesem Drop-down-Menü auswählen und mir ansehen, wie das Spiel aussehen wird. Manchmal funktioniert die Vorschau nicht richtig. Wenn wir zwischen Geräten wechseln, muss ich die Seite manuell aktualisieren Das ist nicht wirklich wichtig, denn in der realen Welt wird niemand zwischen iPhone und Samsung-Telefon wechseln , ohne die Seite neu zu laden Durch erneutes Laden der Seite werden die Positionierungsprobleme immer behoben . Wenn sie auftauchen, können Sie sehen, dass unser einfacher Prototyp bereits relativ gut funktioniert Lass uns weiter daran arbeiten. Ich kann auch eine Medienabfrage für kleine Bildschirmgrößen erstellen. Ich gehe davon aus, dass es sich um ein Handy handelt. Und in diesem Fall werde ich ein anderes Hintergrundbild verwenden , das vertikaler ist für vertikale mobile Bildschirme geeignet ist. Ich stelle Ihnen diese beiden hochauflösenden Bilder im Abschnitt Ressourcen unten zur Verfügung. Sie können dieses Bild auch zuschneiden und für andere Projekte verwenden, wenn Sie möchten. Jetzt haben wir eine sehr einfache reaktionsschnelle Spielwelt, die sowohl auf breiten horizontalen Bildschirmen als auch auf hohen vertikalen Bildschirmen funktioniert breiten horizontalen Bildschirmen als auch . Perfekt. 52. Feind-Klasse: Um die Navigation in dieser Codebasis zu vereinfachen, werde ich eine weitere Java-Script-Datei namens Enemy JS erstellen. Hier werden wir unsere Feindklasse haben und wir werden sie auf mehrere Feindtypen ausweiten. Ich könnte hier Javascript-Module verwenden, aber dann müsste ich dir zeigen , wie man einen lokalen Server betreibt. Ich werde das einfach halten und einfach MGS-Datei hier so einfügen Es muss vor dem Haupt-GS kommen. Wenn wir das auf diese Weise tun, die Reihenfolge, in der wir kann die Reihenfolge, in der wir einzelne Javascript-Dateien einfügen, wichtig sein. Ich kann die feindliche Klasse aus Maina-JS herausschneiden und sie hier in NMS einfügen. Der Feind erwartet als Argument eine Referenz, die auf das Hauptspielobjekt zeigt die auf das Hauptspielobjekt Im Inneren konvertieren wir es in eine Klasseneigenschaft. Durch diese Referenz können wir alle Eigenschaften und Methoden der Hauptspielklasse sehen und darauf zugreifen . Jeder Feind benötigt X- und Y-Koordinaten, damit das Java-Skript weiß, wo er auf der Leinwand gezeichnet Es benötigt Geschwindigkeit X und Geschwindigkeit Y. Und innerhalb der Höhe setze ich sie auf 50 50. Anfänglich zeichnet die benutzerdefinierte Zeichenmethode den Feind auf dem Bildschirm Das Spiel wird über eine Render-Methode verfügen, mit der alle Feinde sowie alle anderen Spielobjekte gezeichnet werden. Während wir unser Projekt aufbauen, ist es gut, es Schritt für Schritt zu testen, damit wir mögliche Fehler zuerst Render erwartet Kontext als Argument. Von dort aus rufen wir die eingebaute Fill Rectangle-Methode auf, um eine einfache rechteckige Form zu zeichnen. Ich nenne diese Render-Methode mit der Spielvariablen aus Zeile 28. Es erwartet Kontext. Ich übergebe es CTX von Zeile 24. Der Standard-Füllstil ist schwarz, wenn wir ihn nicht überschreiben Wir zeichnen an diesen X- und Y-Koordinaten ein schwarzes Rechteck . Mit dieser Breite und Höhe kann ich den Füllstil auf eine andere Farbe einstellen. Hier zum Beispiel grün. Wir entwickeln ein Spiel und die Dinge werden sich bewegen. Das erreichen wir, indem wir die Objekte immer wieder zeichnen, aktualisieren und neu zeichnen um eine Illusion von Bewegung zu erzeugen Benutzerdefinierte Methode, die ich zum Beispiel animate nenne. Ich werde Game Dot Render von innen aufrufen. Und ich rufe die eingebaute Animationsrahmenmethode die auf dem Fensterobjekt sitzt. Aber wir brauchen diesen Teil von Javascript nicht, wir werden automatisch wissen, wo wir diese Methode finden. Ich übergebe es animate und rufe es hier auf, um mit der Animation zu beginnen. Wir animieren bereits. Ich kann es beweisen, indem ich die Eigenschaft Position X erstelle sie hier als X-Position auf dem Rechteck verwende Ich erhöhe Position x für jeden Animationsframe um eins . Wir sehen alte Farbe. Ich kann auch zwischen jedem Animationsframe die Leinwand von der alten Farbe entfernen , indem ich die integrierte Methode für durchsichtige Rechtecke verwende . Jetzt haben wir ein sich bewegendes grünes Rechteck, das wir animieren. Ich kann dies löschen, wenn ich die Größe des Browserfensters ändere . Der Resize-Event-Listener löst unsere benutzerdefinierte Resize-Methode aus, die die Leinwand auf die neue Innenhöhe setzt Als Nebeneffekt wird beim Ändern der Größe der Leinwand der Canvas-Status auf die Standardeinstellung gesetzt Alle Eigenschaften werden zurückgesetzt, einschließlich des Füllstils, der auf die Standardblockfarbe zurückgesetzt wird auf die Standardblockfarbe zurückgesetzt Aus diesem Grund müssen wir diese Stile neu deklarieren, wenn wir die Größe ändern müssen wir diese Stile neu deklarieren, wenn wir die Eine Möglichkeit, dies zu lösen, besteht darin, sicherzustellen, dass wir innerhalb unserer benutzerdefinierten Größenänderungsmethode Zugriff auf diese CTX-Variable haben innerhalb unserer benutzerdefinierten Größenänderungsmethode Zugriff auf diese CTX-Variable , um sicherzustellen, dass der grüne Selbst nach der Größenänderung kann ich CTX aus dem Spielklassenkonstruktor der Zeile 24 als zweites Argument hier oben übergeben Spielklassenkonstruktor der Zeile 24 . Ich stelle sicher, dass es erwartet wird, ich konvertiere es in Jetzt kann ich von hier aus eingebaute Zeichenmethoden und Eigenschaften auf Leinwand aufrufen Zeichenmethoden und Eigenschaften auf Leinwand In der Spieleklasse wird diese Zeile überschrieben, wenn die Resize-Methode Stattdessen setze ich hier den Füllstil auf Grün. Nachdem wir die Größe geändert haben, gebe ich unserem Spiel eine benutzerdefinierte Startmethode, die nur einmal ausgeführt wird, wenn das Spiel gestartet oder neu gestartet wird Und es wird zuerst alles einrichten. Ich rufe einfach resize auf, um sicherzustellen, dass der grüne Füllstil gilt, wenn das Spiel geladen wird, das mit und Höhe als Argumenten erwartet Anfangs übergebe ich das Fenster in der Breite und das Fenster in der Höhe So wie wir es hier automatisch im Event-Listener anwenden Wir werden diesen Start auch automatisch auslösen, wenn eine Instanz des Spielobjekts im neuen Schlüsselwort hier erstellt wird im neuen Schlüsselwort hier erstellt Jetzt bleibt die Farbe, die wir dem Phil-Stil geben, erhalten , auch wenn wir die Größe des Browserfensters Perfekt. Jetzt kann ich diesen Dogamex von hier aus nehmen Zugriff über diese Eigenschaft nenne ich Phil Rechteck, das den Einlauf darstellt Wir passieren es xy mit der Höhe des feindlichen Objekts. Ich kann dem Eneme hier auch einen anderen Phil-Stil geben. Wir müssen ihm einige X- und Y-Koordinaten geben. Hier füge ich die gegnerische GS vor der Haupt-GS hinzu. feindliche Klasse sollte innerhalb der Spielklasse verfügbar sein . Hier erstelle ich eine Eigenschaft namens Punkt Feind Eins und setze sie auf eine Instanz unserer benutzerdefinierten Feindklasse. Es erwartet ein Spiel als Argument. Hier, hier sind wir. In dem Spielobjekt. Ich übergebe ihm dieses Schlüsselwort, über das das feindliche Objekt Zugriff auf alle Eigenschaften und Methoden der Spielklasse hat. Vom Rendern aus möchte ich die Draw-Methode aufrufen , die für die feindliche Klasse definiert ist. Ich sage dieser Feind eins aus Zeile sieben, Unentschieden aus Zeile 11. Hier, nett, wir zeichnen einen Feind. Und das bestätigt, dass wir erfolgreich eine wechselseitige Verbindung zwischen Spiel- und Feindklassen hergestellt haben. Gute Arbeit. Diese Rendermethode wird für jeden Animationsframe immer wieder aufgerufen. Hier zeichnen wir nur denselben roten Feind an derselben Position neu denselben roten Feind an derselben Position Wir können es aber auch verschieben, indem wir seine Update-Methode aufrufen Es existiert noch nicht. Ich definiere es hier. Für jeden Animationsframe möchte ich horizontale Position des Feindes um Geschwindigkeit x und die vertikale Position um Geschwindigkeit y erhöhen und die vertikale Position um Geschwindigkeit y . Ein Pixel auf der vertikalen Y-Achse führt zu einer Bewegung von oben nach unten. kann auch sagen, wenn sich der Feind ganz nach unten bewegt, seine vertikale Position wieder nach oben setzt, ich lasse ihn schneller bewegen. Ich kann die anfängliche horizontale Ausrichtung auch nach dem Zufallsprinzip einstellen. Ich randomisiere es auch jedes Mal, wenn wir es zurücksetzen. Ich kann mehrere solche Feinde erschaffen. Du musst diesen Teil nicht machen. Ich zeige Ihnen nur einen sehr ineffizienten Weg, wie das geht Es könnte vielleicht funktionieren, wenn wir nur ein oder zwei Objekte haben. Aber in diesem Spiel möchte ich Dutzende oder vielleicht sogar Hunderte von Feinden haben . Ich kann Geschwindigkeit Y zufällig einstellen, damit sie sich mit unterschiedlichen Geschwindigkeiten bewegen Wenn ich das beim ersten Los zufällig von einer anderen vertikalen Position aus beginne, kann ich sie bis hinter den oberen Rand zurücksetzen lassen Sie gleiten also von außen in den Leinwandbereich hinein. Wir wissen, dass alles funktioniert. Und wir haben eine feindliche Klasse, die mehrfach instanziiert werden kann , um einen Schwarm von Feinden zu erzeugen Ich lösche das alles, weil es eine bessere Möglichkeit gibt, diesen Code zu strukturieren 53. Designmuster für Objektpools: Ich werde jedes feindliche Objekt in ein wiederverwendbares Objektpool-Mitglied verwandeln. Das bedeutet, dass wir es aus dem Objektpool nehmen und es aktivieren, wenn wir es brauchen. Und wenn wir damit fertig sind, werden wir es einfach in den Objektpool zurückgeben, anstatt es zu löschen, und wir werden seine Eigenschaften zurücksetzen. Wiederverwendung unserer Objekte. Die Verwendung eines solchen Objektpool-Entwurfsmusters kann die Leistung unserer Codebasen erheblich verbessern Weil wir Probleme im Zusammenhang mit automatisierter Speicherzuweisung und Garbage-Collection-Prozessen vermeiden automatisierter Speicherzuweisung und Garbage-Collection-Prozessen , die automatisch ausgeführt werden Wenn wir viele Objekte in einer Codebasis erstellen und zerstören, werden wir unsere feindlichen Objekte immer wieder verwenden , um Speicherplatz zu sparen. Jedes Mitglied des Objektpools muss über eine Eigenschaft verfügen, die angibt, ob es derzeit aktiv oder inaktiv ist. Ich nenne es kostenlos. Wenn das Objekt frei ist, ist es inaktiv und wartet im Objektpool darauf, verwendet zu werden. Wenn free den Wert false hat, bedeutet das, dass das Objekt derzeit im Spiel aktiv ist. Es wird gezeichnet und animiert und ist nicht kostenlos. Es kann nicht aus dem Pool genommen werden. Jedes Mitglied des Objektpools benötigt zwei Hilfsmethoden. Startmethode, die ausgeführt wird. Wenn wir die Site aktivieren, setzen wir die Methode „False Reset“ frei , die ausgeführt wird, wenn wir mit dem Objekt fertig sind. Wenn zum Beispiel ein Feind zerstört wird, setzt die Reset-Methode die Eigenschaft free wieder auf true zurück, wodurch der Feind in einen Pool mit freien und verfügbaren Objekten zurückkehrt. Wir aktualisieren und zeichnen nur die aktiven Gegner. Wir werden die Methoden Update und Draw nur ausführen , wenn diese Eigenschaft ohne Punkte den Wert False hat. Enemy ist Mitglied eines Objektpools. Es hat eine Eigenschaft , die angibt, ob es kostenlos ist oder ob es gerade verwendet wird. Es gibt eine Methode, um es zu aktivieren und eine Methode, um es zu deaktivieren, aktualisieren und zu zeichnen. Methoden führen ihren Code nur für aktive Objekte aus. Wenn der Feind den Bildschirm verlässt, rufen wir die Reset-Methode auf, um den Feind zu deaktivieren und ihn mit einigen Eigenschaften des gegnerischen Objekts wieder in den Objektpool zurückzubringen ihn mit einigen Eigenschaften des gegnerischen Objekts wieder in den Objektpool . Zum Beispiel müssen die X- und Y-Koordinaten jedes Mal zurückgesetzt werden , wenn das gegnerische Objekt seinen Lebenszyklus durchläuft. Wir können sie an dem Punkt zurücksetzen dem wir das Objekt aus dem Pool nehmen. Oder an dem Punkt, an dem wir es wieder in den Pool zurückbringen. Ich werde mich dafür entscheiden, die Eigenschaften zurückzusetzen. Bei der Startmethode werden wir den Feind nach dem Zufallsprinzip horizontal anordnen und ihn vertikal direkt hinter dem oberen Rand der Leinwand platzieren Auf diese Weise verwandeln Sie ein normales Objekt in ein Objektpoolmitglied Der zweite Teil wäre die Verwaltung des Objektpools selbst. Wir werden das von unserer State Management-Klasse aus machen. Der Feindpool wird ein Array sein, das alle aktiven und inaktiven feindlichen Objekte enthält . Wir müssen wählen, wie viele Feinde wir erschaffen. Diese Zahl wird statisch sein. Sie muss hoch genug sein, damit sie uns nicht ausgeht, aber auch nicht zu hoch, da wir sonst Speicherplatz verschwenden würden Wir können es später anpassen. Wenn das Spiel spielbar ist, scheint 50 eine vernünftige Zahl zu sein. Die Spielklasse benötigt zwei Hilfsmethoden, um den Objektpool zu verwalten Eine Methode, um den Pool mit 50 feindlichen Objekten zu füllen. Wir werden eine For-Schleife laufen lassen. Jedes Mal, wenn es läuft, nimmt es eine feindliche Zuggruppe und drängt neue Gegner an. Da drin erwartet es Spiel als Argument. Ich gebe ihm dieses Schlüsselwort hier weiter. Das haben wir schon einmal gemacht. 54. Periodische Auslöser: Ich möchte Create Enemy Pool automatisch aufrufen. Wenn wir eine Instanz der Spielklasse erstellen , protokolliere ich diesen Feindpool, nachdem wir ihn erstellt haben. Ich muss diesen Punkt hier in der Konsole verwenden , um zu sehen, dass wir eine Reihe von 50 feindlichen Objekten haben. Ich öffne einen, um zu überprüfen, wir sehen keine undefinierten oder Nullwerte , die auf ein Problem hinweisen könnten Jetzt möchte ich regelmäßig einen neuen Feind aktivieren. Sagen wir alle 1 Sekunde, alle 1.000 Millisekunden. Wir verwenden einen Anforderungsanimationsframe , der automatisch Zeitstempel für uns generiert Lass uns sie benutzen. Ich werde eine Hilfsvariable erstellen, die ich beim letzten Mal aufgerufen habe. Hier werden wir den Zeitstempel der vorherigen Animationsschleifenanfrage speichern . Die Animationsframe-Methode generiert automatisch Zeitstempel. Alles, was ich tun muss, um auf sie zuzugreifen , ist ihnen einen Variablennamen zuzuweisen. Hier wird es wissen, dass wir diesen Zeitstempelwert wollen. Wenn wir das für jeden Animationsframe tun, möchte ich die Deltazeit berechnen, das ist die Differenz in Millisekunden zwischen dem Zeitstempel dieser Animationsschleife und dem dieser Animationsschleife und Zeitstempel der vorherigen Nachdem wir die Deltazeit berechnet haben, setzen wir letztes Mal auf den aktuellen Zeitstempel, sodass er als alter Zeitstempel verwendet werden kann als alter Zeitstempel verwendet werden In der nächsten Animationsschleife gibt uns die Delta-Zeit an, wie viele Millisekunden unser Computer gebraucht hat , um einen Animationsframe zu erzeugen Dann können wir diesen Deltazeitwert an das Rendern übergeben, wo wir eine einfache Logik schreiben, die die kleinen Millisekundenwerte akkumuliert , bis ein bestimmter Logik schreiben, die die kleinen Millisekundenwerte akkumuliert kleinen Millisekundenwerte bis Dann können wir eine Event-Anfrage auslösen . Animationsframe passt die Geschwindigkeit, mit der der nächste Animationsframe angezeigt wird, automatisch passt die Geschwindigkeit, an die Bildschirmaktualisierungsrate an, da es sinnlos wäre, Positionen für Frames zu berechnen , die Ihr Monitor oder Ihr Telefonbildschirm nicht anzeigen kann In den meisten Fällen wären das 60 Bilder pro Sekunde. Beim Rendern stelle ich sicher, dass Deltazeit erwartet wird. Ich tröste tausend Millisekunden geteilt durch 60 Bilder pro Sekunde, das sind ungefähr 16,6. Mein Computer benötigt ungefähr 16,6 Millisekunden, um jeden Animationsframe geteilt durch 60 Bilder pro Sekunde, das sind ungefähr 16,6. Mein Computer benötigt ungefähr zu berechnen 16,6 Millisekunden und neu zu zeichnen. Perfekt. Während der Code ausgeführt wird, generieren wir Zeitstempel, berechnen die Deltazeit und übergeben diese an die Render-Methode. Von dort aus können wir diesen Wert in unserer gesamten Codebasis verfügbar machen , um jede Art von Ereignis zu erzeugen , das sich regelmäßig wiederholt. Lassen Sie mich Ihnen zeigen, wie es einfach ist. Wir benötigen zwei Hilfsvariablen, Anime-Timer und Anime-Intervall. Der Animate-Timer sammelt die Deltazeit von 0 bis 1 Tausend. Dann aktiviert es einen Feind und fängt wieder an zu zählen Wir benötigen eine weitere Hilfsmethode, die, wenn sie aufgerufen wird, den Feindpool nach einem freien und verfügbaren feindlichen Objekt durchsucht einem freien und verfügbaren feindlichen Objekt und es uns gibt. Drinnen laufen wir eine For-Schleife über den feindlichen Pool. Sobald wir das erste Objekt im Pool finden, bei dem die Eigenschaft free auf true gesetzt ist, das erste inaktive Objekt, geben wir dieses Objekt zurück. Ich kann diese Logik , die alle 1.000 Millisekunden einen neuen Feind aktiviert , direkt in die Aber um den Code etwas sauberer zu halten, schreibe ich ihn in ein separates Feinde werden Delta-Time als Argument erwarten. Wenn der Timer des Feindes unter dem Feindintervall liegt, Feind-Timer bei Null beginnt und das Feindintervall 1.000 beträgt , solange der Timer unter dem Intervall liegt, erhöhen wir den Timer ständig um die Delta-Zeit, andernfalls bedeutet das, dass der Timer 1.000 oder mehr Millisekunden angesammelt hat 1.000 oder mehr Millisekunden angesammelt Wir setzen es wieder auf Null, damit es wieder zählen kann. Und wir rufen get enemy auf, um ein Objekt zu aktivieren, setzen es auf eine Variable und ich rufe die Startmethode für dieses inaktive feindliche Objekt auf , um es zu aktivieren. Es setzt die Objekteigenschaften auf Standardwerte zurück und markiert es als aktiv. Es besteht die Möglichkeit, dass wir keine inaktiven Gegner im Pool haben , wenn dieser Code ausgeführt wird , was unseren Code beschädigen würde. Ich möchte die Startmethode nur auslösen, wenn die Methode Get Enemy tatsächlich ein verfügbares inaktives feindliches Objekt finden konnte . Ich lösche dieses Consolo, ich rufe Handle Enemies von der Render-Methode aus Das heißt, es wird immer wieder für jeden Animationsframe aufgerufen , in dem ich den Feind konsolidieren werde. Hier sehe ich, dass ein Feind korrekt aktiviert wird. Alle 1 Sekunde lösche ich den Consolo. Jedes feindliche Objekt hat Aktualisierungs- und Zeichnungsmethoden. Diese Methoden werden nur ausgeführt , wenn das Objekt aktiv ist seine Eigenschaft free auf false gesetzt ist. Ich kann jetzt für jede Methode im Feindpool und für jedes gegnerische Objekt, sowohl aktive als auch inaktive, arbeiten. Wir nennen ihre Update - und Draw-Methoden. Ich lege mit und Höhe hier oben hin. Ich möchte, dass die anfängliche vertikale Position so minus Höhe ist. So implementieren Sie ein einfaches Objektpool-Entwurfsmuster mit Java-Skript. Wir aktivieren alle 1.000 Millisekunden ein neues feindliches Objekt Wir bringen sie zurück in den Objektpool, wenn sie Wiederverwendung unserer Objekte auf diese Weise, anstatt ständig neue zu erstellen und alte zu löschen, wird die Leistung unserer Codebasis verbessern Insbesondere in Projekten, in denen wir große Mengen von Objekten haben, kann ich sie sehr schnell ins Blickfeld bringen und dann mit ihrer normalen Geschwindigkeit wie folgt bewegen Sobald das gesamte Objekt vollständig sichtbar ist , wird es langsamer. Dieses kleine Detail wird noch besser aussehen, wenn wir Grafiken und animierte Tabellen hinzufügen Wir möchten, dass dieses Projekt vollständig reagiert. Wenn Benutzer auf Mobilgeräten spielen, können sie die Bildschirmgröße nicht ändern. Auf Computerbildschirmen oder Geräten kann es jedoch zu Situationen kommen, Auf Computerbildschirmen oder Geräten kann es jedoch zu Situationen in denen wir zwischen Quer - und Hochformat wechseln Oder wenn wir mitten im Spiel zum Vollbildmodus wechseln. Wo sich die Größe des Spielbereichs ändert. Um diesen unwahrscheinlichen, aber möglichen Szenarien Rechnung zu tragen, werde ich sicherstellen, dass sich alle aktiven Gegner immer im sichtbaren Spielbereich befinden. Wenn der rechte Rand des gegnerischen Rechtecks hinter dem rechten Rand des Spielbereichs liegt , verschiebe es. Ändert sich nun die Größe des Spielbereichs während des Spiels, werden die Gegner einfach in den sichtbaren Spielbereich geschoben. Dadurch wird sichergestellt, dass keine aktiven Gegner vom Bildschirm schweben , wo wir sie nicht treffen können. 55. Maussteuerungen: Wir werden Feinde mit der Maus und durch Berührungsereignisse treffen. Wir erstellen ein Mausobjekt des Kunden und geben ihm X-, Y- und gedrückte Eigenschaften. Dies wird uns helfen, die Mausposition in der gesamten Codebasis verfügbar zu machen , nicht nur in den Event-Listenern So wie wir es mit Re Size gemacht haben. Ich werde den Mouse-Down-Event-Listener hier im Spielklassenkonstruktor automatisch anwenden Mouse-Down-Event-Listener hier im Spielklassenkonstruktor Ich muss hier die ES-Sechs-Pfeil-Funktion verwenden , um sicherzustellen, dass dieses Schlüsselwort von innen heraus auf das Spielobjekt zeigt . Ich könnte Prevent Default aufrufen falls das Event etwas tut, das uns nicht gefällt. Es könnte relevanter sein. Später bei Touch-Events werden wir es testen und sehen, vorerst glaube ich nicht, dass ich es hier brauche. Von hier aus setze ich die X- und Y-Position des Mouse-Down-Events auf die X- und Y-Eigenschaften unseres benutzerdefinierten Mausobjekts, sie einfacher verfügbar zu machen. In der gesamten Codebasis werde ich das automatisch generierte Event-Objekt konsultieren Und wir greifen hier auf diese X- und Y-Eigenschaften zu. Wenn Ihre Leinwand nicht das gesamte Browserfenster abdeckt, müssten wir von hier aus Offset X und Offset Y verwenden. Stattdessen werde ich diese Werte heranziehen , um zu überprüfen, ob alles funktioniert Ich klicke herum und erhalte X und Y des Klicks. Nett 56. Kollisionserkennung: Ich werde eine wiederverwendbare Check-Kollisionsmethode erstellen. Wir werden sie verwenden, um Kollisionen zwischen Feind und Maus zu überprüfen , aber sie kann später auch für andere Funktionen verwendet werden benutzerdefinierte Methode zur Kollisionsprüfung verwendet Rechteck eins und Rechteck zwei als Argumente Es verwendet die üblichen vier Codezeilen, um zu prüfen, ob zwei rechteckige Objekte kollidieren Beide Objekte wurden an diese Methode übergeben. Beide Rechtecke müssen über die Eigenschaften X- und Y-Position sowie Breite und Höhe Damit das funktioniert, werte ich den Ausdruck direkt und gebe einfach true oder false zurück , wenn diese Methode aufgerufen wird Die übliche Formel für Kollisionserkennung zwischen zwei ausgerichteten, also nicht gedrehten Rechtecken Prüfen Sie, ob die linke Seite von Rechteck eins links von der rechten Seite von Rechteck zwei liegt Gleichzeitig prüfen wir, ob sich die rechte Seite von Rechteck eins rechts von der linken Seite von Rechteck zwei befindet. Wenn diese beiden Prüfungen zutreffen, wissen wir bereits, dass die Rechtecke horizontal kollidieren, aber sie können vertikal immer noch weit voneinander entfernt sein Wir benötigen zwei weitere Prüfungen gleichzeitig. Wir müssen prüfen, ob der obere Rand von Rechteck eins über dem unteren Rand von Rechteck zwei liegt. Gleichzeitig müssen wir auch prüfen, ob der untere Rand von Rechteck eins unter dem oberen Rand von Rechteck zwei liegt. Nur wenn alle vier dieser Prüfungen zutreffen, liegt eine Kollision vor. Selbst wenn eine dieser vier Prüfungen falsch ist, wissen wir, dass die Rechtecke weit voneinander entfernt sind und nicht kollidieren können Dieser gesamte Ausdruck gibt Falsch zurück. Jetzt müssen wir sicherstellen, dass jedes Objekt, das wir zur Kollisionsprüfung als Rechteck eins oder Rechteck zwei übergeben Kollisionsprüfung als Rechteck eins oder , die Eigenschaften x, y, width und height haben muss . Andernfalls würde die Kollisionsprüfung nicht funktionieren. Wir werden vier Kollisionen überprüfen. Hier sind wir in der Update-Methode für die feindliche Klasse. Wenn die Check-Collision-Methode, die zwischen diesem feindlichen Objekt und dem Mausobjekt definiert ist , den Wert true rufen wir Reset für den Feind auf und geben sie zurück in den Objektpool. Ich erhalte eine Fehlermeldung. Ich muss sicherstellen, dass das Mausobjekt die Eigenschaften Breite und Höhe hat . Es wird nur ein sehr kleines Rechteck sein, einmal ein Pixel. Das Problem ist, dass, wenn ich irgendwo klicke und die Maus wegbewege, das Mousedown-Ereignis mein benutzerdefiniertes Mausobjekt dort platziert hat und es immer noch mit Feinden kollidiert Wenn das Mousedown-Ereignis ausgelöst wird, setzen wir bei gedrückter Maustaste den Wert true Ich erstelle dort einen weiteren Event-Listener für das Mousedown-Ereignis Ich werde auch die X- und Y-Koordinaten auf die letzte bekannte Position aktualisieren Y-Koordinaten auf die letzte Und ich werde die gedrückte Maustaste auf False setzen. Jetzt kann ich eine weitere Bedingung hinzufügen. Wenn dieser Feind und die Mausobjekte kollidieren und nur, wenn die Maus gleichzeitig gedrückt wird, nur dann wird der Feind zurückgesetzt Jetzt kann ich auf die Gegner klicken und sie werden korrekt zurückgesetzt und kehren zum Objektpool Ich könnte hier andere Dinge tun. Wenn ich sie zum Beispiel anklicke, rutschen sie so runter. Wir werden das später für etwas anderes verwenden, mit unserem Alien, das sich phasenweise verändert Ich gehe zurück, um hier neu zu starten. Ich kann diesen Code bereinigen. Da wir die Werte von x und y innerhalb der Startmethode definieren , können wir die Eigenschaften hier einfach ohne Werte deklarieren . Weil wir wissen, dass die Startmethode sowieso ausgeführt werden muss , bevor das Objekt im Spiel gezeichnet wird. Es ist sauberer, denn wenn ich jetzt Änderungen an diesen Werten vornehmen möchte, mache ich das einfach innerhalb der Startmethode. Ich muss diese Werte nicht an zwei verschiedenen Stellen überprüfen . Feinde können auch mehrere Leben haben. Ich kann es hier einfach definieren, aber ich werde ihm nur in der Seitenstartmethode einen Wert zuweisen. Wenn wir also ein altes feindliches Objekt wiederverwenden, wird sein Leben auf den ursprünglichen Wert zurückgesetzt. Jeder Neme wird zwei Leben haben. Wir müssen ihn zweimal treffen. Ich werde die Leben hier mit der eingebauten Fülltext-Methode zeichnen . Wir übergeben ihr drei Argumente, den Text, den wir zeichnen möchten, und die X- und Y-Koordinaten des Ankerpunkts, in Bezug auf den wir den Text zeichnen wollen. Ich werde diesen Ankerpunkt genau in die Mitte des roten Klistierrechtecks setzen in die Mitte des roten Klistierrechtecks setzen Wir müssen den Füllstil des Textes auf eine andere Farbe einstellen des Textes auf eine andere Farbe Zum Beispiel Blau in Maine GS. In der Resize-Methode, die auch beim ersten Laden der Seite ausgeführt wird, setzen wir die Font-Eigenschaft von Cannava auf 50 Wir platzieren den Textankerpunkt genau in der Mitte der Gegner Es gibt zwei Navas-Eigenschaften, die die Beziehung zwischen dem Text und seinem Ankerpunkt regeln Wir können den Text zentriert ausrichten, um ihn horizontal auszurichten. Wir können die Textgrundlinie auf die Mitte setzen , um den Text vertikal exakt über den Koordinaten der Ankerpunkte auszurichten , so wie wir sie in Zeile 49 in der feindlichen GS-Datei definiert haben . Anstatt den Feind sofort zurückzusetzen, reduzieren wir seine Lebenspunkte jetzt um eins, wenn wir ihn anklicken Die Kollision wird einmal pro Animationsframe registriert, sodass die Lebenspunkte sehr schnell abnehmen Bevor wir das beheben, sagen wir auch wenn die Lebenspunkte des Feindes weniger als eins sind, setzen wir den Feind zurück und fügen ihn in den Objektpool zurück. Wir werden diese Überprüfung später an mehreren Stellen benötigen , damit der Code leichter lesbar ist. Ich werde es in einen separaten Helfer legen. Methode ist aktiv, Methode wertet diesen Ausdruck direkt aus und gibt true zurück, wenn der Feind am Leben ist und fällt, wenn seine Leben weniger als eins sind. Wenn der Feind nicht am Leben ist, wird der Feind zurückgesetzt. Jetzt brauchen wir eine Flagge , die anzeigt, ob der Mausklick bereits zum Abfeuern unserer Waffe benutzt wurde. Da wir nur einmal pro Klick feuern wollen, gebe ich der Maus eine neue Eigenschaft namens gefeuert. Anfangs wird es falsch sein. Der Schuss wurde noch nicht abgefeuert. Wir reduzieren die Lebenspunkte des Feindes nur um eins, wenn abgefeuert wird. Das ist falsch. Und dann setzen wir das Feuer sofort auf True. Um sicherzustellen, dass dieser Codeblock nur einmal ausgeführt wird, müssen wir unsere Waffe neu laden und fire wieder auf False setzen , damit wir erneut schießen können Ich denke, wir können feuerte entweder mit gedrückter oder gedrückter Maus wieder auf False setzen Lass es uns versuchen. Ja, das funktioniert. Jetzt entfernen wir nur ein Leben pro Klick. Wir müssen jeden Feind zweimal anklicken, um beide Leben zu verlieren und ihn zurückzusetzen Ich entferne den Füllstil hier. Stattdessen habe ich es hier auf Weiß gesetzt. Ich werde auch den Strichstil auf Weiß setzen. Es ist besser für die Leistung , diese Deklarationen hier in die Resize-Methode einzufügen, die beim ersten Laden nur einmal ausgeführt wird Oder wenn wir die Größe ändern, die Screendraw-Methode 60 Mal pro Sekunde ausgeführt Sie hier den Füllstil angeben, ist das weniger effizient, wenn es um die Leistung geht Ich lösche diesen Füllstil, kleinere Schrift, wir haben eine responsive Welt, deren Größe sich an jede Bildschirmgröße, ob vertikal oder horizontal, Es funktioniert auf einem riesigen, superbreiten Computerbildschirm Es wird auf Tablets funktionieren. Und es funktioniert auf Mobilgeräten sowohl im Querformat als auch im Hochformat. Lassen Sie uns es noch benutzerfreundlicher für mobile Geräte machen , indem wir Touch-Events einbeziehen. 57. Touch-Events: Ich könnte ein separates benutzerdefiniertes Touch-Objekt erstellen, aber ich werde tatsächlich ein Mausobjekt verwenden , das hier perfekt funktioniert. Es funktioniert automatisch sowohl für Mausklicks als auch für Berührungsereignisse. Touch-Start macht im Grunde dasselbe wie mit der Maus nach unten. Das Touch-End-Ereignis bewirkt dasselbe wie das Mouse-Up-Ereignis. Dies wird noch nicht funktionieren, da dieses automatisch generierte Ereignisobjekt bei Berührungsereignissen dieses automatisch generierte Ereignisobjekt bei die Informationen über das Ereignis auf eine andere Weise strukturiert das Ereignis auf eine andere Weise Ich konsologate hier im Google Chrome-Browser und klicke auf die Gerätesymbolleiste, um eine responsive Bildschirmansicht auszulösen , sodass wir Berührungsereignisse in der responsiven Ansicht emulieren Berührungsereignisse in Ich klicke auf den Bildschirm und weiß, dass es funktioniert hat, weil wir das Touch-Event-Objekt trösten. Ich suche nach den X - und Y-Koordinaten des Touchstart-Events Und ich kann sehen, dass der Browser die Kontaktpunkte mit einer Touch-Oberfläche innerhalb dieser Touch-Listen-Eigenschaft organisiert die Kontaktpunkte mit einer Touch-Oberfläche innerhalb dieser Touch-Listen-Eigenschaft Ich denke, wir können es wie ein Array behandeln. Das werden wir später testen. Ich kann sehen, dass ich, um auf X- und Y-Koordinaten des Touch-Events zuzugreifen, auf das Event-Change-Touches-Element die X- und Y-Koordinaten des Touch-Events zuzugreifen, auf das Event-Change-Touches-Element mit einem Index von 0 Punkt Pagex und Pagey zugreifen muss mit einem Index von 0 Punkt Pagex und Pagey Ich mache dasselbe auch für das Touch-End-Event. 58. Spieltext: Jetzt haben wir eine ansprechende Spielwelt. Und wir können Touch-Events verwenden, um Feinde zu erschießen. Ich werde die Gerätesymbolleiste in Google Chrome deaktivieren. Jetzt sehen wir uns unser Spiel in einer normalen Computerbrowser-Ansicht an. Ich kann den Code zur Anzeige von Punktzahlen und anderem Spieltext direkt in den Rendermodus einfügen , aber ich möchte das sauber halten. Auch hier werde ich es in eine separate Methode einfügen, um die Navigation in unserer Codebasis zu vereinfachen. In der Textmethode Draw Status rufe ich die eingebaute Fülltext-Methode auf und übergebe ihr den Text, den ich zeichnen möchte sowie die X- und Y-Koordinaten des Ankerpunkts , in Bezug auf den ich diesen Text zeichnen möchte. Ich nenne es von innen rendern Ich kann sehen, dass die Partitur nicht vollständig sichtbar ist. Ich möchte, dass der Text, feindliche Leben anzeigt, mittig bleibt, aber ich möchte, dass der Punktetext linksbündig ausgerichtet ist. Ich binde ihn zwischen integrierten sicheren Methoden und Wiederherstellungsmethoden , die alle Änderungen, die ich am Canvas-Status vornehme, auf diesen Bereich beschränken . Der gesamte Zeichencode außerhalb dieses Blocks bleibt davon unberührt Auf diese Weise kann ich eine Textzeile für die Punktzahl übrig lassen und Textzeile den Mittelpunkt für die Punktzahl von gegnerischen Leben festlegen Der Text fließt jetzt von diesem X- und Y-Anker nach rechts. Lassen Sie uns die Punktzahl tatsächlich zählen. Ich definiere es hier, Startmethode im Inneren, wir setzen die Punktzahl auf Null, weil dies auch als Neustartmethode verwendet wird. Jetzt füge ich diese Variable hier hinzu, kleinere Schrift. Wenn wir die Leben eines Feindes aufbrauchen, indem wir ihn abschießen, setzen wir den Feind Wir bringen es zurück in den Objektpool. Wir erhöhen die Spielpunktzahl um eins. Ich muss die Punktzahl korrekt buchstabieren , wenn wir mehrere Feindtypen haben. Wir könnten hier auch eine Punktzahl angeben, die zum Beispiel im Verhältnis zu den Leben von Feinden steht. Oder jeder Feindtyp kann seine eigene Punkteeigenschaft haben , die bestimmt, wie viel Punktzahl er gewährt. Wenn wir jetzt auf Feinde klicken, erhalten wir Punktepunkte. Ich möchte dem Spiel auch Leben geben. Das sind die Leben der Spieler. Wenn wir sie verlieren, ist das Spiel vorbei. Verwechsle es nicht mit Leben auf jedem Feind. Diese geben an, wie oft wir jeden Feind treffen müssen. Das sind Spielerleben. Wenn wir das Spiel starten oder neu starten, setzen wir die Lebenspunkte der Spieler beispielsweise auf zehn. Im Text zum Status der Ziehung werde ich eine Schleife mit vier Schlaufen erstellen, die pro Leben einmal ausgeführt werden. Ich werde ein einfaches Rechteck zeichnen , das jedes Leben darstellt. Zuerst benötigt es x, y, Breite und Höhe. Jetzt zeichne ich sie alle übereinander , okay? Das sind X, Y, Breite und Höhe. Dieser Wert entspricht dem Rand von links. Ich multipliziere die horizontale X-Position mit dem Index aus den vier Schleifen, wodurch sie in einer Reihe nebeneinander angeordnet werden. Wenn die Breite jedes Lebensrechtecks mehr oder gleich dem Wert des Leerzeichens ist , sieht es aus wie ein durchgehender Gesundheitsbalken, was Sie vielleicht möchten. Wenn der Raum größer als die Breite ist , können wir einzelne Leben sehen. Ich werde mich für diese Ansicht entscheiden , weil ich später jedes Leben in eine andere humanoide außerirdische Spezies verwandeln möchte jedes Leben in eine andere humanoide außerirdische Spezies verwandeln Dadurch wird unser Space-Horror-Spiel noch interessanter, weil wir nicht wollen, dass die Feinde unsere einzigartigen Besatzungsmitglieder auffressen Wenn das nicht klar ist, spielen Sie einfach mit den Werten. Beobachten Sie, was passiert. Es ist ziemlich einfach, wenn Feind es schafft, den ganzen Weg über den Bildschirm zu reisen, nach unten, und wir unterbrechen ihn nicht, wir schießen nicht auf ihn Es hat es geschafft, unsere fliehende Flotte einzuholen und wird eines unserer Besatzungsmitglieder auffressen Wenn der Feind zurückgesetzt wird, reduzieren wir die Lebenspunkte im Spiel um eins. Wenn wir die Anzahl der Leben ändern, erscheinen die Rechtecke, die jedes Leben repräsentieren Das funktioniert gut. Lass uns die Spielschleife schließen. Das Siegergebnis wird drei sein. Wir werden drei Nachrichten auf dem Bildschirm anzeigen. Wenn das Spiel vorbei ist, werden wir drei Sätze dieser Nachrichten haben. Eine Nachricht wird nur beim ersten Laden angezeigt. Bevor wir die Taste drücken, um das Spiel zu starten, entferne ich diesen Wert aus Game Over. Wenn wir das Spiel starten, setzen wir Game Over auf False. Ich werde eine separate Trigger-Game-Over-Methode erstellen , um sicherzustellen, dass dieser Codeblock nur einmal ausgeführt wird. An dieser Stelle könnte ich diese Logik in den Rendervorgang integrieren, aber wenn ich sie in eine separate Methode einfüge, wird es einfacher sein, später, wenn wir das Spiel gewinnen und wenn wir verlieren, einen speziellen Horrorsound abzuspielen einen speziellen Horrorsound . Es wird nur einmal ausgeführt, wenn es aufgerufen wird. Und das stellen wir sicher , indem wir seinen Code nur ausführen , wenn Game Over im Inneren falsch ist Wir setzen Game Over sofort auf true. Wir haben Game Over ausgelöst, was bedeuten könnte, dass wir das Spiel verloren haben. Es gibt weniger als ein Spielerleben oder wir haben das Spiel gewonnen , wenn die Punktzahl mehr oder gleich der Gewinnpunktzahl ist . Wenn wir verlieren, setzen wir einen eindeutigen Text für Nachricht eins und Nachricht zwei ein. Wenn wir gewinnen, geben wir denselben Variablen unterschiedliche Texte, dann müssen wir diese Methode aufrufen. Ich kann das hier vom Rendern oder vom Text mit dem Status eines Unentschieden Wenn das Leben weniger als eins ist oder wenn dieses Ergebnis mehr oder gleich dem Siegerpunktestand ist, rufen wir Trigger Game Over auf, wodurch Game Over auf True gesetzt wird und die Nachrichten eingerichtet werden. Wenn Game Over dann wahr ist, wollen wir die Meldung 12.3 in der Mitte des Bildschirms zeichnen Text mittig ausrichten, große Schrift kann 80 Pixel groß sein, wir nennen Textnachricht Eins füllen, X- und Y-Koordinaten sind die Mitte des Spielfeldes Botschaft zwei und Nachricht drei. Wir machen das Gleiche. Ich passe ihre vertikalen Koordinaten an, um sie auszurichten. -25 Nachricht eins, plus 25 für Nachricht zwei und plus 50 für Nachricht drei Ich spiele. Ich warte, bis wir verlieren. Okay, das hat funktioniert. Ich möchte , dass Nachrichten 2.3 in einer kleineren Schrift gezeichnet werden. Ich versuche es mit 20 Pixeln, das ist die verlorene Botschaft. Ich aktualisiere die Seite, spiele erneut und ich gewinne die Gewinnnachricht. Wir haben noch eine Nachricht, wir werden in einer Minute eine Anzeige machen. Ich habe gesagt, das Spiel ist vorbei. 59. Neustart starten: Ich habe diese Reset-Taste hier erstellt, ich möchte, dass sie die Startmethode auslöst, wenn sie angeklickt wird Zuerst erstelle ich eine Eigenschaft für das Hauptspielobjekt und habe anhand seiner ID auf dieses Button-Element hingewiesen Dann füge ich dieser Schaltfläche einen Event-Listener hinzu. Wir warten auf ein Klickereignis. Auch hier muss ich eine Pfeilfunktion verwenden, um sicherzustellen, dass wir vom Event-Listener aus auf diesen Punkt zugreifen können, um sicherzustellen , dass dieses Schlüsselwort auf das Spielobjekt zeigt Von dort aus nenne ich das Do Start. Jetzt kann ich die Startnachrichten sehen, aber sie sind nicht formatiert Nun, ich behebe das, indem ich das hier aufrufe und Größe automatisch im die Größe automatisch im Spielklassenkonstruktor Dadurch werden die Einstellungen für den Füllstil und die Textgrundlinie korrigiert, aber jetzt ist die Punktzahl undefiniert Ich werde es hier in der Methode „ Feinde behandeln“ zunächst auf Null setzen hier in der Methode „ Feinde behandeln“ Wir aktivieren regelmäßig neue feindliche Objekte und fügen sie dem Spiel hinzu Ich möchte nur, dass immer wieder neue Feinde kommen, wenn das Game Over falsch ist. Wenn ich jetzt die Seite aktualisiere, sehen wir nur die ersten Nachrichten und nichts passiert, bis wir hier auf die Schaltfläche R klicken , um das Spiel zu starten. Eine Schaltfläche, die das Spiel durch Benutzerinteraktion startet , löst auch Probleme mit den Sounds des Spiels. Darüber werden wir später sprechen. In modernen Browsern werden Sounds jedoch grundsätzlich erst abgespielt, nachdem der Benutzer mit dem Dokument interagiert hat Benutzer auf diese Schaltfläche klickt, wird dies als Interaktion gewertet, sodass die Sounds in unserem Spiel abgespielt werden können Ich erstelle einen weiteren Event-Listener. Diesmal protokolliere ich für ein Schlüsselereignis das Ereignisobjekt Wir suchen nach dieser Schlüsseleigenschaft. Ich sage, wenn die Taste, die auf der Tastatur gedrückt wurde, Enter ist, oder wenn die Taste R ist, verwende ich hier die Kleinbuchstabenmethode, um zu berücksichtigen, dass sowohl Klein- als auch Großbuchstaben gedrückt werden. Wir führen eine Startmethode aus , um das Spiel zu starten. R muss hier in Anführungszeichen stehen. Moment setzt die Startmethode den Punktestand im Grunde einfach auf Null zurück und unser Leben wird wieder aufgefüllt Ich möchte auch alle aktiven Gegner zurücksetzen. Ich überfasse alle Objekte innerhalb des feindlichen Pool-Arrays und rufe bei ihnen Reset auf. Vielleicht möchte ich schnell zwei Gegner aktivieren. Sofort, wenn das Spiel beginnt. Ich könnte so eine Vierschleife machen. Ich habe versucht, den Feind zu wenn ein freies gegnerisches Objekt verfügbar war. Wir haben festgestellt, dass wir durch den Aufruf der Feind-Startmethode aktiviert wurden. Lass dich hier nicht verwirren. Startmethode, die sich auf dem Spielobjekt befindet , wird gestartet und neu gestartet Die Spielstartmethode, die wir in feindlichem GS für das gegnerische Objekt definiert haben , aktiviert zwei verschiedene Methoden, die Mitglied des feindlichen Objektpools Jetzt können wir das Spiel jederzeit starten oder neu starten, indem wir auf diese R-Taste klicken oder tippen oder Eingabetaste oder den Buchstaben R auf der Tastatur drücken Dies soll Ihnen nur zeigen, wie Sie Ihren Spielern verschiedene Optionen bieten können Ihren Spielern verschiedene Optionen bieten Wir können hier auch eine schnellere Anfangswelle erzeugen, indem sofort ein paar Gegner aktivieren wir zu Beginn sofort ein paar Gegner aktivieren. Dann kommt der Rest in 1-Sekunden-Intervallen. 60. Vollbild-Spiele: Moderne Webentwicklung hat viele coole Funktionen. Haben Sie schon einmal versucht, die integrierte Vollbildmodus zu verwenden ? Im Grunde werden nur die Browserschaltflächen und die Navigationsleiste entfernt und Ihre Anwendung so erweitert, dass sie den gesamten Bildschirm einnimmt. Dies funktioniert sowohl auf Computermonitoren, auf Tablets als auch auf mobilen Bildschirmen. Wir werden es verwenden, um ein richtiges Vollbild-Spiel zu machen. Jetzt ist es sehr einfach , es zu implementieren. Lassen Sie mich Ihnen zeigen, interessieren Sie sich für Spiele im Vollbildmodus? Lass es mich wissen, indem du einen Kommentar hinterlässt. Und wenn es den Leuten gefällt, kann ich diese Funktion häufiger für andere Projekte verwenden . Sie können auf diese Webadresse gehen, um die Dokumentation zu lesen , wenn Sie weitere Informationen wünschen. Damit das funktioniert, brauchen wir nur diese einfache wenn L-Anweisung, die im Grunde besagt, ob der Vollbildmodus gerade nicht aktiv ist, aktiviert ist. Wenn es gerade aktiv ist, ist es deaktiviert. All diese Eigenschaften sind in jeden modernen Browser integriert in jeden modernen Browser Sie können also einfach diesen kleinen Codeblock kopieren, den ich hier in die Vollbildschirmmethode der Hauptspielklasse eingefügt habe hier in die Vollbildschirmmethode der Hauptspielklasse eingefügt Vollbildschirmmethode der Hauptspielklasse eingefügt Immer wenn diese Methode von irgendwo in unserer Codebasis aufgerufen wird , prüft sie, ob sie im Vollbildmodus aktiv ist, falls sie nicht Wenn sie aktiv oder deaktiviert ist, testen wir, ob sie hier funktioniert, andernfalls, ob die Taste, die gedrückt wurde , die Leertaste ist oder ob die gedrückte Taste ein Buchstabe ist gerne verschiedene Steuertasten verwenden hier gerne verschiedene Steuertasten verwenden, wenn Sie wissen, was Sie tun Jetzt drücke ich die Buchstaben - oder Leertaste und mein Spiel wechselt zwischen Vollbild und normaler Ich zeige es hier nicht wirklich gut weil ich die Ränder meines Bildschirms nicht aufnehme Aber wenn Sie alleine programmieren, können Sie es mit Ihrer Codebasis selbst machen und sehen, dass dies eine sehr nette, ordentliche Vollbild-Funktionalität ist . Wir wollen auch in der Lage sein , auf Mobilgeräten, auf denen wir keine Tastatur haben, den Vollbildmodus umzuschalten , diese Schaltfläche für den Vollbildmodus, die ich zuvor erstellt habe Sie können den Vollbildmodus auch durch Gesten aktivieren und deaktivieren, indem beispielsweise nach oben und unten wischen Wenn das Wischen länger als eine festgelegte Anzahl von Pixeln es in eine bestimmte Richtung geht, können wir unseren benutzerdefinierten Code auslösen, beispielsweise das Aktivieren und Deaktivieren des Vollbildmodus sein könnte Aktivieren und Deaktivieren Ich habe das in einem anderen Tutorial für dieses einfache, mobilfreundliche Endless Runner- und Jumper-Spiel Es war vor einiger Zeit auf meinem Youtube. So wie wir es mit der Reset-Taste gemacht haben. Ich verweise in meinem Code auf die Schaltfläche „Vollbild“. Ich füge Event-Listener hinzu. Beim Rückruf rufe ich die gesamte Vollbildmodus auf. Das Spiel ist im Vollbildmodus vollständig spielbar Was Sie jetzt sehen, ist nicht ganz richtig , weil ich nicht meinen gesamten Bildschirm aufnehme Der Planet und der Text sind ein bisschen mittig und die Knöpfe sind nicht hier. Aber wenn Sie mitprogrammieren, können Sie in Ihrem Projekt sehen , dass alles gut funktioniert. Wir haben ein unbeschriebenes Spiel mit allen Funktionen, komplettem Spiel, Loop-, Gewinn-und-Lose-Status, Maus-, Tastatur- und Touch-Steuerung, Reset und Vollbildmodus. Dieses Spiel funktioniert sowohl auf großen horizontalen Computermonitoren als auch auf kleinen vertikalen mobilen Bildschirmen. können Sie die Gewinnpunktzahl in Zeile 16 oder Leben in Zeile 77 anpassen, um die Gewinn- und Videobeschreibung können Sie die Gewinnpunktzahl in Zeile 16 oder Leben in Zeile 77 anpassen, um die Gewinn- und Verlustbedingungen Ich gebe Ihnen einen vollständigen Quellcode aus dem Projekt. In diesem Stadium können Sie ihn mit Ihrem eigenen Code vergleichen , wenn Sie auf Probleme stoßen. Sie können diesen Code verwenden um viele verschiedene Spiele zu erstellen, kreativ mit den Themen und Grafiken umzugehen und ihn zu Ihrem eigenen zu machen. Im nächsten Teil werde ich daraus Cartoon-Space-Horror-Spiel mit Sounds und einer zufällig zusammengestellten Crew machen , die wir vor phasenweise fleischfressenden Aliens schützen müssen phasenweise fleischfressenden Aliens schützen Damit ist der anfängerfreundliche Teil des Kurses abgeschlossen. Im nächsten Teil werde ich Ihnen eine verbesserte Implementierung von State Design Pattern zeigen Ihnen eine verbesserte Implementierung und ich werde etwas schneller vorgehen. Gut gemacht. Wenn Sie so weit gehen, lassen Sie es mich wissen, indem Sie einen Kommentar hinterlassen, in dem es heißt, dass ich das Anfängerprojekt abgeschlossen habe. 61. Simple Crew-Mitglieder: Wir haben ein Spiel, das auf kleinen und großen Bildschirmen, Hoch- und Querformat, vertikal und horizontal, funktioniert . Jetzt ist es Zeit für Spaß. Wir fügen einige Grafiken und Animationen hinzu. Ich habe die Anzahl der Leben auf 15 gesetzt. Ich erstelle ein Diff mit einer Vorstellung von den darin enthaltenen Vermögenswerten. Ich werde die Bilder und Sounds unseres Projekts einfügen. Sie können alles, was ich verwende, hier herunterladen. Im Abschnitt mit den Ressourcen unter der Crew wird das PNG-Bild und die ID „Crew I give assets“ angezeigt, zeige keine. Wir wollen alles darin verstecken. Wir wollen diese Bilder nur in den Speicher laden , damit wir sie mit Javascript auf Canvas zeichnen können. Ich möchte Leben als kleines Crewmitglied zeichnen . Fangen wir einfach an. Ich bringe das Crew-Image in mein Projekt mit Get Element by ID hier ein. Bei der Methode Draw Statistics zeichnen wir Rechtecke, um das Leben der Spieler darzustellen Stattdessen werde ich die integrierte Methode zum Zeichnen von Bildern verwenden. Wir übergeben ihm das Bild, das wir zeichnen möchten, sowie die X- und Y-Koordinaten. Wo ich es zeichnen soll, passe ich an und verstecke es hier, damit es dem Bild entspricht, das wir verwenden. Das ist der linke Rand, das ist der Raum zwischen den kleinen Besatzungsmitgliedern. Ich kann dafür sorgen, dass sie um sie herum Platz haben , oder ich lasse sie näher übereinander stecken. Als wir alle Besatzungsmitglieder verloren haben, als sie von den Aliens gefressen wurden , haben wir das Spiel verloren 62. Einfacher Feindtyp: Ich möchte verschiedene Feindtypen erstellen. Dies ist die Hauptfeindklasse. Sie enthält alle Methoden und Eigenschaften , die von allen Gegnertypen gemeinsam genutzt werden. Ich werde zuerst einen einfachen Feind mit der grundlegendsten Implementierung erstellen, aber mein oberstes Ziel ist es, Ihnen zu zeigen, wie Sie einen komplexen Feind mit mehreren Zuständen einsetzen können. Und wie Sie Ihren Code so strukturieren, dass sich jeder Status in einem eigenen Codeblock befindet eigenen Codeblock sogenanntes State-Entwurfsmuster verwenden. Einfacher Feind mit einem Leben, ohne besondere Eigenschaften, ohne besondere Fähigkeiten und Interaktion. Ich nenne es Beetle Moorph. Ich werde aus einem anderen Tutorial zu meinem Space Invaders-Spiel zurückkehren und mir die Tabelle holen , die wir dort in diesem Projekt verwendet haben Die Feinde sind etwas größer. Wenn du dir die erweiterte Version ansiehst, gebe ich dir alle größeren Exporte aller Gegnertypen, die wir in dieser gesamten Weltraumspielserie verwendet haben. Wenn du willst, kannst du später damit spielen und implementieren, was auch immer du willst Im Moment nehme ich einfach die Beetle Moorph-Tabelle im Ressourcenbereich und lege sie in meinen lege sie in Beetle Moorph-Unterklasse erweitert den gegnerischen Superklassen-Konstruktor Die Beetle Moorph-Unterklasse erweitert den gegnerischen Superklassen-Konstruktor wird das Spiel als Argument erwarten. Und ich werde es an den Superclass-Konstruktor weitergeben, wenn wir es hier aufrufen. Ich rufe den Klassenkonstruktor für die übergeordnete feindliche Klasse auf , die wir erweitern Klasse auf , die wir erweitern. Diese Klasse ist hier definiert, sodass gesamte Code hier zuerst ausgeführt wird Dies sind alles gemeinsame Eigenschaften. Die Eigenschaften, die für alle Gegnertypen der Beetle-Morph-Unterklasse gleich alle Gegnertypen der Beetle-Morph-Unterklasse Ich werde nur Eigenschaften definieren diesen speziellen Feindtyp spezifisch sind Es wird ein einzigartiges Bild haben, das ich hier importiert habe. Laden Sie den Link im Abschnitt Ressourcen unten Ich richte Javascript mit Get Element by ID auf das Bild. Es wird auch eine eigene Startmethode benötigen. Zuerst rufen wir die Startmethode für die übergeordnete feindliche Klasse auf. solchen Superklasse die Wenn wir in einer solchen Superklasse die Start-Methode für Beetlemorph Enemy aufrufen, wird zuerst der gesamte Code innerhalb dieser übergeordneten Startmethode ausgeführt innerhalb dieser Hier aus Zeile 13 kopiere ich Geschwindigkeit x und Geschwindigkeit Y. Wenn wir Start auf Betlemorph aufrufen kopiere ich Geschwindigkeit x und Geschwindigkeit Y. Wenn wir Start auf Betlemorph aufrufen, gebe ich ihnen Werte. Ich möchte, dass sich dieser Gegnertyp direkt auf Geschwindigkeit x Null und etwas positive Geschwindigkeit Y bewegt Geschwindigkeit x Null und etwas . Ich entferne diese Werte in den Zeilen 8.9. Sie werden für jeden Gegnertyp innerhalb der zugehörigen Startmethoden auf einen anderen Wert gesetzt jeden Gegnertyp innerhalb Ich entferne auch Leben aus der gemeinsamen Startmethode, weil jeder Feindtyp eine andere Anzahl von Leben haben wird eine andere Anzahl von Leben haben In der erweiterten Version kehre ich auch zu den Projekten Space Invaders und Planet Defense zurück , die alle Teil desselben großen Kurses sind, einschließlich dieses Spiels und anderer Ich füge Bonuslektionen hinzu, indem ich die neue Phase im Feindtyp implementiere , der nur getroffen werden kann wenn er sich in derselben Dimension wie der Spieler befindet Wir erweitern eine Klasse, bei der die gegnerische Oberklasse gemeinsame Eigenschaften der die gegnerische Oberklasse Eigenschaften , die für jeden Gegnertyp einzigartig , werden der Unterklasse zugewiesen Zuerst lösen wir den Konstruktor aus und starten Methode für die Oberklasse der Elternklasse Dann fügen wir der Aktualisierungsmethode einige Eigenschaften und Werte hinzu, die für diesen speziellen Feindtyp spezifisch Aktualisierungsmethode einige Eigenschaften und Werte hinzu, die für diesen speziellen Feindtyp spezifisch Hier suchen wir nach Kollisionen zwischen Feind und Maus. Ich werde das ausschneiden und in eine separate Methode einbauen. Das liegt hauptsächlich daran, dass wir eine Phase des Feindtyps haben werden, der nicht immer getroffen werden kann. Ich muss besser kontrollieren können, wann wir nach Kollisionen suchen. Jetzt ist es in einer separaten Treffermethode. Der gesamte Code im Update wird für alle verschiedenen Feindtypen in unserem Spiel geteilt und ausgeführt. Beetle Moorph wird über eine eigene Aktualisierungsmethode verfügen , die diese Methode außer Kraft setzt Wenn dieses feindliche Objekt gerade aktiv ist, rufen wir den gesamten Code innerhalb der Update-Methode ab Zeile Wenn dieser Feind noch am Leben ist, rufen wir die Treffermethode auf und überprüfen Kollision zwischen diesem Feind und der Maus. Jetzt muss ich diese Unterklasse nennen. Jetzt schieben wir New Beetle Morph in den gegnerischen Pool. Richtig, ich habe vergessen, dass ich die Update-Methode für die übergeordnete feindliche Klasse aufrufen muss. Das funktioniert. 63. Sprite-Animation: Jetzt wollen wir die Tabelle zeichnen. Ich kann diesen Code in die Draw-Methode der gemeinsamen übergeordneten Enemy-Klasse einfügen . Da alle Gegnertypen eine Tabelle haben werden , nenne ich die Methode „ Built-in-Draw-Image Und ich gebe ihr das Bild, das ich zeichnen möchte, und die X- und Y-Koordinaten, wo es gezeichnet werden soll Dadurch wird einfach die gesamte Tabelle mit all ihren Rahmen in ihrer Originalgröße gezeichnet . Wir können ihr hier optionale Breite und Höhe geben, wodurch die Tabelle in den von uns definierten Bereich gedrückt oder gedehnt wird . Wir benötigen die längste Version der Draw-Image-Methode mit neun Argumenten. Dadurch haben wir die vollständige Kontrolle über diese Tabelle. Wir übergeben ihm das Bild, wir wollen Quelle X, Quelle Y, Quellbreite und Quellhöhe des Bereichs zeichnen Quelle Y, Quellbreite und Quellhöhe des Bereichs Wir möchten aus dem Quellbild und dem Zielbild X, dem Ziel-Y, der Zielbreite und der Zielhöhe ausschneiden dem Quellbild und dem Zielbild X, dem Ziel-Y, , um zu definieren, wo auf der Ziel-Leinwand wir das ausgeschnittene Bild platzieren möchten . Die Spritesheets, die wir verwenden, haben eine individuelle Rahmengröße von Die Spritesheets, die wir verwenden, haben eine 100 mal 100 Pixeln Ich definiere die Eigenschaften hier, die ich im Verhältnis zur Höhe festlege Diese beiden Werte gelten für Crop In. Diese beiden Werte bestimmen die Größe, in wir das Bild auf Destination Canvas zeichnen. Diese beiden Wertesätze müssen nicht identisch sein. Wir können das skalieren und werden es später tun. Was ist, wenn ich den oberen linken Rahmen von 00 auf Sprite mit Sprite-Höhe ausschneiden von 00 auf Sprite mit Sprite-Höhe Gut, wir verwenden die Methode „Bild zeichnen um diesen Rahmen aus dem Spritesheet herauszuschneiden . Die Eigenschaft Frame Y hilft uns dabei, die Tabelle vertikal zu bewegen . Wie ich diese Tabelle organisiert habe, können Sie sehen, dass sie uns verschiedene Varianten desselben Feindtyps Alle Gegner werden vier Sprite-Reihen haben, vier Varianten. Also kann ich Frame Y hier innerhalb der Shared Start-Methode nach dem Zufallsprinzip anordnen hier innerhalb der Shared Start-Methode nach dem Wenn jeder Feind eine andere Tabelle hätte, müssten wir das auf jede einzelne feindliche Unterklasse legen. Methode nach jede einzelne feindliche Unterklasse legen. Methode Zufallsprinzip mal vier in Methodenboden eingeschlossen bedeutet , dass jedes Mal Startmethode ausgeführt wird und wir einen Feind aus dem Pool ziehen, diesem zufällig Frame Y Null oder eins, zwei oder drei zugewiesen Y Null oder eins, zwei oder Quell-Y-Argument, das an die Methode draw image übergeben wird, bestimmt den vertikalen Zuschnitt als Koordinate Also übergeben wir es Frame Y mal Sprite-Höhe. Frame y kann das 01230-fache der Sprite-Höhe sein. Ist das eine Mal die Sprite-Höhe, das Zweifache der Sprite-Höhe, das Dreifache der Sprite-Höhe . Das ist eine einfache Methode, wie ihr eure Spritesheets ein wenig nach dem Zufallsprinzip anordnen und uns einige Varianten desselben Gegnertyps geben könnt . Sie haben alle die gleichen Funktionen und die gleiche Farbpalette, aber sie haben unterschiedliche Details was das Ganze interessanter macht Ich denke, wir werden auch Frame X benötigen um horizontal durch das Spritesheet zu navigieren Ich werde auch die Eigenschaft Last Frame haben , um zu definieren, wie viele horizontale Frames wir pro Zeile haben , wenn die Startmethode Gegnern ausgeführt wird. Um sie zu aktivieren, setzen wir Frame X auf Null Jeder Feind hat vielleicht ein anderes Letztes Bild Beetle-Morph hat ein sehr einfaches Spritesheet, nur drei Frames Bei der Methode zur gemeinsamen Aktualisierung kümmern wir uns um die horizontale Sprite-Navigation, kümmern wir uns um die horizontale Sprite-Navigation wenn der Feind Wenn dieser Punkt lebt falsch ist, wir Frame X immer weiter um eins Wenn Bild X länger als das letzte Bild ist, wissen wir, dass alle Explosionsanimationsframes angezeigt wurden. Und wir wissen, dass es sicher ist, den Feind zurückzusetzen und ihn wieder in den Objektpool zurückzubringen. Wenn Game Over falsch ist, erhöhen wir die Punktzahl um eins und ich kann das löschen. Nachdem das Argument source x die Methode draw image, die die horizontale Koordinate des Zuschneidebereichs bestimmt , bestanden Methode draw image, die die horizontale Koordinate des Zuschneidebereichs bestimmt hat, übergeben wir das Bild x mal die Sprite-Breite Wenn Frame x das 00-fache der hellen Breite hat, zeichnen wir diesen Frame einmal mit der Sprite-Breite und zweimal zeichnen wir diesen Frame einmal mit der Sprite-Breite und zweimal mit der hellen Breite. Ich hoffe, es macht Sinn. Ich spiele, ich klicke zweimal auf jede Animation, verliere ihre Lebensdauer und die Animation wird abgespielt, die funktioniert 64. Animations-Timing: Es wird sehr schnell abgespielt. Ich möchte kontrollieren können, wie schnell wir von Bild zu Bild springen. Wenn wir unsere Spritesheets animieren, führen wir ein weiteres periodisches Ereignis aus, genau wie zuvor, wenn wir alle 1.000 Millisekunden einen neuen Feind aktivieren alle 1.000 Millisekunden einen neuen Feind Wir werden alle Spritesheets in unserem Spiel aktualisieren. Alle, sagen wir 200 Millisekunden. Das Sprite-Update wird falsch sein. Jedes Mal, wenn Sprite bis 200 zählt, wird Sprite Update auf true gesetzt Ich kann diese Logik direkt in meine Render-Methode übernehmen, aber um unseren Code sauberer zu halten, werde ich ihn in einen eigenen Codeblock aufteilen aber um unseren Code sauberer zu halten, werde ich ihn in einen eigenen Codeblock Ich nenne es zum Beispiel Handle Sprite Timer. Es wird Deltazeit als Argument erwarten. Wenn der Sprite-Timer kleiner als das Sprite-Intervall ist, akkumulieren wir weiterhin Delta-Zeit Andernfalls setzen wir den Timer auf Null zurück, sobald wir genug akkumuliert haben, sodass er erneut zählen kann Und wir werden Sprite Update auf true setzen, sodass unsere Spritesheets von Bild zu Bild springen Dies wird nur für diesen einzelnen Animationsframe gelten. In allen anderen Fällen wird es von Angesicht zu Angesicht zurückgestellt. Jetzt rufen wir hier Handle Sprite Timer auf, dieser Delta-Zeitwert wird weitergegeben Das sollte hier alles gut sein. Innerhalb der Aktualisierungsmethode für die feindliche Klasse nur, wenn der Feind nicht am Leben ist und wenn das Sprite-Update nur wahr ist springen Sie im Spritesheet von Bild zu Bild Ich werde diesen Codeblock anpassen. Wir müssen hier sehr vorsichtig mit den Klammern sein. Es ist einfach, unseren Code mit einer einzigen fehlenden Klammer zu knacken . Es funktioniert nicht, weil ich hier Sprite Update verwenden muss. Eigentlich noch ein Fehler, den ich irgendwo gemacht habe. Ich sehe, dass ich Sprite Interval hier in Zeile 24 falsch geschrieben habe. Ich kann es verlangsamen oder beschleunigen, indem ich dem Sprite-Intervall hier unterschiedliche Werte gebe. 1.000 Millisekunden führen dazu, dass sie Sprite-Intervall hier unterschiedliche Werte gebe. 1.000 Millisekunden führen dazu, 1.000 Millisekunden 50 Millisekunden sind sehr schnell. 150 Millisekunden scheinen hier ein guter Wert zu sein. Vorerst. Jetzt funktioniert alles gut. Wir haben ein Verhalten, das ich hier nicht mag. Wenn sich zwei Gegner überschneiden und wir sie anklicken, wird der Feind im Hintergrund zerstört. Warum ist das so und wie bringen wir es dazu , den Feind an der Front zu zerstören? Wir radeln über den Feindpool und rufen Update und Draw auf. Wir zeichnen alle Feinde auf demselben Leinwandelement in einem einzigen Animationsframe. Während wir dies für jede Methode ausführen, zeichnen wir Feinde nacheinander. Wenn wir zuerst ziehen, erscheinen hinter dem Feind, der danach gezogen wurde. Gleichzeitig werden die Kollisionsprüfungen innerhalb der Aktualisierungsmethode zuerst für den Feind ausgeführt , der zuerst gezogen wurde , und dieser Feind wird zerstört. Eine Möglichkeit, dies zu beheben, besteht darin , den gegnerischen Pool aus einer Richtung zu überfahren , um Update aufzurufen, und dann aus der entgegengesetzten Richtung, Draw aufzurufen. Auf diese Weise werden die Gegner, die zuletzt gezogen werden, also an der Spitze, mit dieser Codestruktur zuerst auf Kollisionen überprüft . Wenn sich zwei Gegner überlappen und ich sie anklicke, der oberste, wird die Vorderseite zerstört. Das ist besser, da wir den Zuschnitt nach Größe und Zeichnungsgröße getrennt haben. Wir können hier tatsächlich etwas skalieren. Ich kann Breite und Höhe mit demselben Größenmodifikatorwert multiplizieren demselben Größenmodifikatorwert Ich kann die Sprites größer oder kleiner machen. Wie wäre es mit einem zufälligen Größenmodifikator zwischen 0,4 und 1,2. Das macht das Spiel vielleicht ein bisschen interessanter Wie wäre es mit einem Zufallswert zwischen 0,6 und 1,3 65. Debug-Modus: Ich möchte in der Lage sein, einige Helferelemente im Spiel ein- und auszublenden wie diese weißen Kollisionsfelder und Zahlen, die feindliche Leben anzeigen Lass uns einen Debug-Modus für die Hauptspielklasse erstellen. Ich habe diese Bug-Eigenschaft erstellt und sie zunächst auf true inside key gesetzt Wenn die Taste, die in Kleinbuchstaben gedrückt wurde, der Buchstabe D ist, werden wir auf den entgegengesetzten Wert zurückgesetzt. Wenn es wahr ist, setzen wir es auf falsch. Wenn es falsch ist, setzen wir es auf wahr. In der Draw-Methode für die feindliche Klasse zeichnen wir nur das weiße Rechteck um Feinde und feindliche Leben. Wenn der Fehler wahr ist, stelle ich sicher, dass ich die Änderungen an all meinen Dateien speichere Jetzt kann ich D auf meiner Tastatur drücken, um den Debug-Modus umzuschalten Immer wieder gebe ich Beetle Morph nur ein Leben. Denn wenn wir feindliche Leben nicht anzeigen, dieser erste Klick kein visuelles Feedback und wir wissen nicht, ob wir den Feind getroffen haben Ich passe den Bereich der Größenmodifikatoren hier in Zeile sechs an, ich sagte Gewinnpunktzahl 220 Anfänglich wird „Zurück“ auf „Falsch“ gesetzt. Wenn du damit zufrieden bist, einfache Gegner wie diesen zu haben , kannst du sie so implementieren, wie wir es gerade getan haben. Ich habe eine Reihe von Spielen und Projekten mit diesem Kunststil erstellt , und alle Grafikelemente sind kompatibel und können zwischen den Projekten verwendet werden. Wenn du willst, schnappe ich mir jetzt einen der komplexeren Gegner, die wir zuvor implementiert haben , und werde versuchen die gleiche Weise zu implementieren, wie wir es gerade getan haben. Dann werden wir einen dritten Feindtyp hinzufügen staatliche Entwurfsmuster verwenden. Das ist etwas komplexer, gibt uns aber viel mehr Freiheit und hält gleichzeitig unseren Code sauber, modular und organisiert. In den anderen Projekten haben wir zum Beispiel einen gepanzerten Feind, einen aufblasbaren Feind, einen rachsüchtigen Feind, der auf uns zurückschießt Wir haben einen übergroßen Bost-Feindtyp mit einem riesigen Gesundheitspool. Wir haben einen anstürmenden Feind, der schneller wird und uns trifft. Ich werde mir zum Beispiel diesen schnappen, einen Feind, der sich in Klone aufteilt, wenn wir ihn treffen Weil ich eine interessante Idee habe, wie man es in diesem speziellen Projekt mit dieser Art von Gameplay einsetzen in diesem speziellen Projekt mit dieser Art von 66. Feindliche Vielfalt: In dieser Klasse verwenden wir etwas größere Spritesheets mit Frames von 100 mal 100 Pixeln Sie können die Sprites verwenden, die Sie bereits haben, wenn Sie den anderen Klassen gefolgt Sie haben Frames von 80 mal 80 Pixeln In der erweiterten Version dieses Projekts werde ich auch Exporte aller Gegnertypen in einer Framegröße von 100 mal 100 aufnehmen , damit sie besser zu diesem Projekt passen Wenn du später mit dieser Codebasis spielen und auch die anderen Gegnertypen einbeziehen möchtest, kannst du diese größeren Spritesheets verwenden Ich habe mein Bild in den HTML-Index aufgenommen und hier darauf verwiesen Der letzte Frame wird 14 sein. Auch hier löst die Startmethode zuerst den Start der gegnerischen übergeordneten Klasse aus. Horizontale Geschwindigkeit x Null. Geschwindigkeit y wird ein zufälliger Wert zwischen 0,2 und 0,7 sein . Dieser Feind hat drei Leben, also muss ich sicherstellen, dass er etwas langsamer ist , damit der Spieler genug Zeit hat, sich damit auseinanderzusetzen. Mein Plan ist, dass dieser Feind drei Leben hat. Wir haben es einmal getroffen. Die Frames werden abgespielt, bis wir wieder drauf drücken. Die Frames werden abgespielt, bis wir diesen Punkt das dritte Mal erreichen, und der Rest der Tabelle wird abgespielt Dies lässt sich am besten mithilfe eines State-Entwurfsmusters implementieren , bei dem jeder dieser Zustände seinen eigenen Codeblock hat Aber ich werde versuchen, es irgendwie auf einfache Weise zu implementieren , wie wir es mit dem Beetle Morph getan haben Dann können wir es mit dem letzten Feind vergleichen, wo ich dir tatsächlich zeige, wie man State Design Pattern benutzt Sie können die relative Komplexität zwischen diesen beiden Ansätzen vergleichen und Sie werden die Vorteile des endgültigen Ansatzes erkennen . Update-Methode führt zunächst den gesamten Code innerhalb der Update-Methode für die übergeordnete Oberklasse aus Wenn dann dieses feindliche Objekt gerade aktiv ist der Feind noch am Leben ist, lassen wir ihn darauf reagieren, wenn er von der Maus getroffen wird Wir werden einen horizontalen Frame-Bereich haben, der durch die Hilfsvariablen Min Frame und Max Frame definiert wird. Wenn Frame X kleiner als Max Frame ist und Sprite Update wahr ist, erhöhen wir Frame X um eins Okay, was ich hier mache, Frame Y kümmert sich um die vertikale Sprite-Navigation Frame X kümmert sich um die horizontale Sprite-Navigation. Der letzte Frame ist der letzte horizontale Frame. Sobald es angezeigt wurde, wissen wir, dass der Feind deaktiviert werden kann Mint Frame und Max Frame definieren einen Animationsbereich mit dem wir gerade arbeiten Je nachdem, in welchem Zustand sich der Feind gerade befindet. Es gibt viele Möglichkeiten, wie ich das umsetzen kann. Ich möchte es so einfach wie möglich machen. Wir werden es tun, wenn die Anzahl Leben des Feindes mehr oder gleich drei ist. Max. Frame ist Null, wenn die Lebenspunkte des Feindes zwei sind. Max. Frame ist drei Wenn wir den Feind aufgrund dieser Codezeile einmal treffen , wird er von hier aus animiert und hier hört er Andernfalls lassen wir, wenn der Feind nur ein Leben hat, die Animation bis zu maximalem Bild sieben gehen Ich denke, das könnte reichen, weil wir in der gemeinsamen Aktualisierungsmethode noch einen anderen Animationscode haben , der den Rest der Tabelle animiert, wenn der Feind nicht am Leben ist und weniger als ein Leben hat . Die Explosion wird automatisch gerahmt Mal sehen, wo ich den Feindpool erstelle. Ich möchte nach dem Zufallsprinzip Gegnertypen Käfer- oder Hummer-Morph erstellen Gegnertypen Käfer- oder Hummer-Morph Ich kann zum Beispiel eine Zufallszahl von 0 bis 1 würfeln. Die Zufallszahl ist kleiner als 0,8. In etwa 80% der Fälle erzeugen wir In den anderen 20% der Fälle erzeugen wir eine Käfer-Morphe. Okay, beide Arten von Feinden kommen. Ich klicke einmal, zweimal, dreimal auf den Feind. Das hat alles perfekt funktioniert. Ich passe das Sprite-Intervall an, um die Animation etwas schneller zu machen Dieser Codeblock funktioniert gut, aber wenn Sie Ihren Code schreiben, denken Sie immer darüber nach, welcher Code einmal ausgeführt wird und welcher Code immer wieder ausgeführt wird Dieser Code läuft die ganze Zeit und ich setze Max Frame 60 Mal pro Sekunde auf denselben Wert , immer wieder. Was ist also, wenn ich einen bestimmten Sound spielen möchte , wenn der Feind in die nächste Phase Ich habe nicht wirklich einen guten Ort, um diesen Code hier abzulegen, da dieser Codeblock immer wieder läuft Dieser Sound würde also immer wieder abgespielt werden , wenn ich ihn hier nenne. Idealerweise benötige ich einen Codeblock, nur einmal ausgeführt wird, um diesen Sound für mich abzuspielen. Wir werden all diese Probleme in einer Minute lösen. Lassen Sie uns zunächst Tabellen für Besatzungsmitglieder verwenden. 67. L18 Randomisierte Weltraumbesatzung: Im Moment sehen alle unsere Besatzungsmitglieder gleich aus. Geben wir ihnen ein bisschen mehr Persönlichkeit. Eine intergalaktische Crew aus verschiedenen Arten von humanoiden und robotischen Alle Kunstgegenstände können im Bereich Ressourcen heruntergeladen werden im Bereich Ressourcen heruntergeladen Unten füge ich Crew-Sprite in das Projekt ein, gebe ihm eine ID und verweise das Crew-Bild auf diese neue Tabelle. Wie machen wir das? Ich werde die zufälligen Koordinaten dieser Frames tatsächlich irgendwo hinhalten müssen Vielleicht habe ich es hier auf dem Grundstück der Besatzungsmitglieder einem leeren Array gleichgesetzt Ich erstelle eine Hilfsmethode. Ich rufe Generate Crew Inside an. Ich setze Crewmitglieder auf ein leeres Array und lösche alles darin, falls es nicht leer ist. Wenn wir diese Methode aufrufen, laufe ich für Loop. Jedem Spielerleben wird ein zufälliges Bild eines Besatzungsmitglieds zugewiesen. Jedes Besatzungsmitglied wird ein einfaches Objekt mit den Eigenschaften Frame X und Frame Y sein. Frame X wird eine Zufallszahl von 0-5 sein und wir verwenden die Methode Floor, um sie auf die nächste niedrigere Ganzzahl abzurunden sie auf die nächste niedrigere Ganzzahl abzurunden Es wird Null oder eins oder zwei, oder drei oder 44 Frame Y sein. Jedes Mal, wenn wir ein neues Besatzungsmitglied anschieben, erhält es zufällige Koordinaten irgendwo in der Tabelle Wir werden diese Methode jedes Mal aufrufen wir das Spiel starten oder neu starten Nachdem wir den Wert der verschiedenen Leben festgelegt haben. Das ist wichtig hier im Zeilenstatustext, ich mit und Höhe eines einzelnen Frames in der Tabelle für die Teammitglieder definiere eines einzelnen Frames in der Tabelle für die Teammitglieder Ich muss vier weitere Argumente hinzufügen , um den Schnittbereich zu definieren Zum Beispiel kann ich den oberen linken Rahmen von Corint 00 auf Breite und Höhe ausschneiden von Corint 00 auf Breite und Höhe ausschneiden Ich werde auch die Argumente with und hide here in destination width und destination de verwenden here in destination width und , um die Größe zu definieren , in der sie auf die Leinwand gezeichnet werden Wir müssen unser Spiel immer starten , um die Leben zu sehen, indem wir den Enter-Buchstaben R auf der Tastatur drücken den Enter-Buchstaben R auf der Tastatur oder auf diese R-Taste klicken oder tippen Toll, jetzt verwenden wir die Rahmenkoordinaten aus dem Array der Besatzungsmitglieder Wir haben diese Eigenschaft hier in Zeile 100 erstellt . Da wir diese vier Schleifen 15 Mal durchlaufen, weil wir insgesamt 15 Leben haben, verwenden wir den Index aus diesen vier Schleifen und greifen auf das entsprechende Element im Array der Besatzungsmitglieder zu. Und wir greifen auf diese Frame-X- und Frame-Y-Koordinaten zu. Wir multiplizieren sie mit der Breite und der Höhe eines einzelnen Frames, damit der Tabelle korrekt bewegen Jetzt erzeugen wir jedes Mal, wenn ich ein Spiel starte oder neu starte, eine Reihe von humanoiden und robotischen Alienarten Ich hoffe, das erhöht den Druck auf den Spieler, sicherzustellen, dass keines unserer einzigartigen Crewmitglieder von den Aliens gefressen wird Dieser Wert definiert hier den linken Rand. Dieser Wert ist hier der Abstand zwischen den Bildern. Wir haben einen einfachen Feindtyp und komplexeren Feindtyp die einfachste Weise implementiert ist, die ich mir vorstellen kann. Wir haben eine zufällige Gruppe von Besatzungsmitgliedern und wir müssen sie davor schützen, Außerirdische zu essen Wenn Sie sich die erweiterte Version ansehen, steht Ihnen in dieser Phase des Projekts ein Quellcode zur Verfügung dieser Phase des Projekts Wenn Sie mit etwas Schwierigkeiten haben, können Sie es gerne verwenden und Ihren Code mit meinem vergleichen. Wenn der Abstand hier etwas geringer ist als die Breite eines einzelnen Rahmens, werden wir sie hintereinander hängen lassen. Sie können mit diesen Werten spielen und alles verwenden, was für Sie gut aussieht. 68. Zustandsmanagement in Spielen: Jetzt ist es an der Zeit zu lernen, wie man mithilfe von State-Entwurfsmustern einen feindlichen Typ implementiert , der nur getroffen werden kann , wenn er sich in derselben Dimension wie der Spieler befindet, wenn er sich in einem festen Zustand befindet Wenn du den Feind triffst, während er sich im unsichtbaren Zustand befindet, wird er dadurch verstärkt und beschleunigt, was ihn noch gefährlicher macht Außerdem werden wir verschiedenen Phasen und Aktionen Sounds hinzufügen verschiedenen Phasen und Aktionen Weil es wichtig ist , dass unsere Spiele Spielern Feedback geben , wenn du etwas anklickst oder damit interagierst. Wir geben visuelles Feedback, indem wir die Gegner animieren. Wir werden auch akustisches Feedback erhalten indem wir einen bestimmten Sound abspielen Kleines Geld, das repariert werden muss. Ich möchte nur die Leben von Spielern reduzieren , wenn Game Over falsch ist Auf diese Weise füge ich eine Phantom-Morph-Tabelle in das Projekt ein und gebe ihr eine ID Hier haben wir die gemeinsame Klistierklasse mit allen gemeinsam genutzten Wir erweitern es um einen einfachen Käfer-Morph-Einlauf und einen komplexeren Hummer-Einlauf Jetzt werden wir es auf den komplexesten Phantom-Morph-Einlauftyp ausweiten komplexesten Phantom-Morph-Einlauftyp Ich hatte eine Menge Spaß damit und wenn Sie sich den erweiterten Kurs ansehen, bin ich zurück zu den Projekten Space Invaders und Planet Defense gegangen und habe zu jedem Projekt eine zusätzliche Lektion hinzugefügt Sowie der vollständige Quellcode, in dem ich diesen Phantom Morph Phasing-Feindtyp in diese beiden Projekte implementiere diesen Phantom Morph Phasing-Feindtyp in diese beiden Sie können auch einfach die Tabelle nehmen, die ich Ihnen hier gebe und versuchen, sie selbst zu implementieren, wenn Sie Aber wenn du meine Hilfe brauchst, sind diese Lektionen für dich da Phantom Morph wird das letzte Bild 14 haben, etwas Geschwindigkeit X und Geschwindigkeit Y. Wir werden das in einer Minute ändern Es wird nur ein Leben haben, aber es wird nicht so einfach sein Weil man es nur treffen kann, wenn es nicht phasenweise ist, wenn es fest ist Wenn du es zur falschen Zeit triffst, kannst du ihm versehentlich einen Schub geben und dazu führen, dass eines unserer Besatzungsmitglieder gefressen wird Und es wird wahrscheinlich nicht schön sein, denn wenn du dir die Geräusche anhörst, werden sie abgespielt. Wenn das passiert, gehe ich zur GS-Hauptdatei hier in Create Enemy Pool und erstelle nur Phantom-Morphs Ich muss vorsichtig mit den Klammern sein. Ich speichere Änderungen an all meinen Dateien und los geht's. Ich kopiere die Update-Methode aus dem Beetle-Morph-Codeblock und verwende sie Wenn ich es jetzt einmal drücke, spielt es einfach den Rest der Tabelle ab. Wie erwartet wird die vertikale Geschwindigkeit ein zufälliger Wert zwischen 0,2 und 0,7 sein . Ich möchte auch, dass sie sich nach links und rechts bewegen. horizontale Geschwindigkeit wird ein zufälliger Wert zwischen minus eins und plus eins sein. Wir wollen sicherstellen, dass sie sich nicht vom Bildschirm bewegen können Deshalb lasse ich sie nach links und rechts springen, wenn die horizontale X-Koordinate des Feindes kleiner oder gleich Null ist Oder wenn der rechte Rand des gegnerischen Rechtecks den rechten Rand des sichtbaren Spielbereichs berührt In beiden Fällen stellen horizontale Geschwindigkeit einfach auf den entgegengesetzten Wert um. Lass den Feind abprallen und bewege dich in die andere Richtung, in die entgegengesetzte Richtung Perfekt, das hat funktioniert. Die anderen Feindtypen waren statisch und zeigten immer einen einzigen Animationsframe. Es sei denn, sie animierten in einen anderen Zustand. Dieser Feindtyp wird anders sein. Ich möchte, dass es immer in einem bestimmten Bildbereich animiert wird. Anfänglich ist der minimale Frame im Null und der maximale Frame ist zwei Ich gebe diesem Feindtyp eigene spezielle Methode, die mit Animationsframes umgehen kann. Wenn das Spray-Update wahr ist, verschieben wir für mich den horizontalen Frame. Wenn Frame X kleiner als der maximale Frame ist, erhöhen wir Frame X weiter, wir erreichen den maximalen Frame und setzen ihn auf den Min-Frame zurück, damit der Zyklus fortgesetzt werden kann. Ich werde hier Handle Frames von Inside Update aufrufen. Nett. Das wird der Bildbereich für den Basisflugzustand sein. Ich versuche es wahrscheinlich mit drei bis fünf. Das wird der Animationsbereich im Phasing-State-Modus sein. Okay, wenn Sie ein Anfänger sind, kommt jetzt der komplizierte Teil Wir werden ein State-Design-Muster implementieren , das es uns ermöglicht, Eigenschaften und Verhaltensweisen dieses Feindes für jeden Staat separat in einem eigenen Codeblock zu definieren Eigenschaften und Verhaltensweisen dieses Feindes für . 69. State-Design-Muster: Ich erstelle eine völlig separate Klasse namens Enemy State Constructor, die Spiel und Feind als Argumente erwartet Wir werden diese benötigen, um auf unser Hauptspielobjekt zeigen und darauf zugreifen zu können , und auch auf das jeweilige feindliche Objekt, das sich in diesen verschiedenen Zuständen befinden wird Dann werden wir jeden Staat in seiner eigenen Unterklasse definieren. All diese drei Unterklassen werden die Hauptklasse der feindlichen Staaten erweitern Hier werden wir die gemeinsamen Eigenschaften in jedem dieser Codeblöcke speichern in jedem dieser Codeblöcke Wir werden Eigenschaften, Werte und Verhaltensweisen definieren, die der Feind haben soll. Nur solange es sich in diesen bestimmten Staaten befindet. Es gibt uns viel Freiheit und Flexibilität. Und es erleichtert das Debuggen unseres Codes . Denn wenn wir einen Fehler erhalten und wissen, in welchem Zustand sich das Objekt befand, wissen wir, wo im Code wir suchen müssen, um ihn zu beheben Die einzige Herausforderung für Anfänger besteht darin, den Überblick darüber zu behalten , wie all diese Objekte verbunden sind und miteinander kommunizieren. Wenn Sie ein Anfänger sind, wird der nächste Teil eine Herausforderung sein Sie sich also nicht zu viele Gedanken darüber. Dies ist eine dieser Techniken, die Sie einige Male anwenden müssen , um eine mentale Landkarte zu erstellen. Es ist hilfreich, wenn Sie dies für ein paar verschiedene Projekte verwenden. wird es einfacher zu navigieren , wenn Sie sich daran gewöhnen. Alles, was Sie jetzt verstehen müssen, ist, dass wir eine eigene Klasse feindlicher Staaten haben. Wir erweitern sie in drei staatliche Unterklassen: fliegend, phasenweise und implodierend Jede dieser Klassen wird ihre eigene Startmethode haben , bei der wir zum Beispiel den Bildbereich für die Animation definieren, zum Beispiel den Bildbereich für die Animation definieren, während sich der Feind in diesem Zustand befindet Von hier aus können wir auch einen bestimmten Sound abspielen. Wenn der Feind diesen Zustand betritt, hat jeder Staat auch seine eigene Aktualisierungsmethode, die zuerst die gemeinsame Aktualisierungsmethode für die feindliche Hauptklasse aufruft . Dann wird ein bisschen einzigartige Logik hinzugefügt, die nur angewendet wird, wenn sich der Feind in diesem bestimmten Zustand befindet. Vielleicht wollen wir zum Beispiel nur die Kollisionserkennung überprüfen , wenn der Feind fliegt. Wenn der Feind in der Phase ist, kann er nicht getroffen werden, also werden wir die Kollisionskontrollmethode von dort aus nicht ausführen . Solche Dinge Jedes feindliche Objekt hat eine Reihe von möglichen Zuständen, in denen es sich befinden kann. Wir instanziieren alle drei Zustandsunterklassen auf diese Weise. Hier in Zeile 174 kann ich sehen, dass der Konstruktor des gegnerischen Staates die Argumente Spiel und Feind S erwartet Ich gebe ihm diese Spielreferenz weiter. Zusammen mit dieser Referenz werden wir in der Lage sein, den Punktestand der Spielerleben oder den Spiel-Over-Status zu sehen , also all die Dinge, die wir benötigen könnten und die sich auf dem Hauptspielobjekt befinden. Dann übergebe ich ihm dieses Schlüsselwort für dieses spezielle feindliche Objekt steht. Dieses Schlüsselwort, das hier in diesem Gerichtsblock verwendet wird, bedeutet Feind. Durch diese zweite Referenz werden wir in der Lage sein, auf alle Methoden und Eigenschaften zuzugreifen, die sich auf das feindliche Objekt, auf die feindliche Klasse, beziehen. Das ist wahrscheinlich der Punkt, an dem es für Anfänger etwas komplizierter wird, mach einfach weiter. Ich werde jetzt versuchen, dich anzuleiten. Phantom Morph wird eine Eigenschaft für den aktuellen Status haben , die auf verschiedene Zustände verweist, die wir hier in diesem States-Array haben hier in diesem States-Array Durch diese Verbindung werden wir in der Lage sein, den Feind einem anderen Staat zuzuordnen Die benutzerdefinierte Methode mit festgelegtem Status erwartet den Status als Argument. Dieser Status wird eine Zahl sein, die den Index darstellt. In diesem Zustand befindet sich das Array von 93090 im Flugzustand, da es sich um ein Array handelt . Arrays, die mit dem Index 01 beginnen , befinden sich im Phasenzustand und zwei im Zustand implodieren Wenn wir set state aufrufen, nehmen wir diese Eigenschaft des Punktes Current State von 940 und zeigen auf eines der Objekte innerhalb dieses Punktstates-Arrays Null oder eins oder An diesem Punkt enthält dieser aktuelle Punktstatus eine ganze Instanz von, sagen wir, fliegende Klasse Wir haben dieser Klasse ihre eigene Startmethode gegeben. Ich sagte und gebe die Flugklasse an und nenne den zugehörigen Start. Wir definieren diese Methode hier in Zeile 186. Und die Aufgabe dieser Methode besteht darin, die Eigenschaften des Phantom-Morph-Enemes auf die gewünschten Werte zu setzen des Phantom-Morph-Enemes die gewünschten Werte zu Wenn der Feindtyp Phantommorph in diesen Zustand eintritt, müssen wir sicherstellen, dass jede Zustands-Unterklasse ihre eigene Startmethode hat. Wir haben hier mehrere Ebenen. Wenn wir nun Start für den Feindtyp Phantom-Morph aufrufen , um ihn aus dem Objektpool zu aktivieren, führen wir zuerst den Code aus, der für alle Feindtypen gemeinsam genutzt wird Dann führen wir den Code aus, der nur für den Gegnertyp Phantommorph spezifisch ist nur für den Gegnertyp Phantommorph spezifisch Dann rufen wir die Set-State-Methode auf. Wir setzen den aktuellen Status auf eine Instanz der Unterklasse Flying State Von dort aus rufen wir die Startmethode für den Flugzustand auf , die Eigenschaften für diesen bestimmten Phantom-Morph-Zustand festlegt diesen bestimmten Phantom-Morph-Zustand In der Update-Methode wird es genauso sein, wenn wir Update für die feindliche Klasse Phantom Moorph aufrufen Zuerst rufen wir den gesamten Code innerhalb der Update-Methode für die gemeinsame übergeordnete feindliche Klasse auf Dann fügen wir Code hinzu, der nur für den Feindtyp Phantom Morph spezifisch nur für den Feindtyp Phantom Morph Darüber hinaus lösen wir den letzten Teil des Codes aus , der nur für den aktuellen Zustand spezifisch ist, in dem sich der phantomorphe Je nachdem, auf welchen Codeblock der aktuelle Status gerade zeigt Es könnte dieser Codeblock aus 191 oder dieser Codeblock aus Zeile 200 sein, oder dieser aus Zeile 209. Jetzt haben wir alle Ebenen und Sie können Ihre Logik überall dort platzieren, wo Sie sie benötigen. Haben Sie eine Idee für ein Verhalten, das nur für einen dieser Zustände spezifisch ist? Wir haben einen Bereich geschaffen, in den Sie diesen Code eingeben können. Ich werde Ihnen einige Beispiele dafür zeigen, wie das gemacht wird. Dies ist die gesamte Logikschleife des State Designs. Ich bin mir nicht sicher, ob ich es gut genug erklärt habe. Wenn Sie es immer noch nicht vollständig verstehen, lassen Sie mich einfach jeden Codeblock erweitern und sehen, was passiert. Hoffentlich wird es klarer, während wir damit arbeiten . Durch diese Codestruktur haben wir eine vollständige und detaillierte Kontrolle über unsere Objekte. Wir können so viele verschiedene Dinge tun. Im Grunde können wir mit unseren Weltraumkreaturen alles machen, was wir wollen. Jetzt können wir über diese Feindreferenz auf die Eigenschaften des Feindes zugreifen . Wenn wir in den Flugmodus wechseln, setzen wir den Min-Frame auf Null und den Max-Frame. Das bedeutet, dass ich diese Werte von hier entfernen kann , weil sie sowieso ausgelöst werden , wenn diese Zeile läuft. Wenn wir in den frontalen Zustand wechseln, setzen wir das Min.- Bild auf drei und das maximale Bild auf fünf. Wenn wir in den implodierenden Zustand wechseln, wird das Bild sechs sein und das maximale Bild entspricht dem letzten Bild plus eins Wenn wir den Feind aktivieren, wird er vielleicht in den Flugzustand Null versetzt Diese Startmethode legt den Frame-Bereich so fest, wie wir ihn gerade definiert haben. Wir können sie auch vom Phasing-Status aus starten lassen. Ich kann sehen, dass sie bei Frame X Null beginnen. Ich möchte eigentlich, dass sie bei dem Min-Frame beginnen , der in diesem Bereich liegt. Ich kann sagen, dass sdtenemetframex dem Didot Enemy im Frame entspricht . Ich mache hier und hier dasselbe Jetzt sind sie sofort unsichtbar. Sie beginnen im richtigen Bildbereich. Wenn sie in einen bestimmten Zustand übergehen, möchte ich, dass sie nach dem Zufallsprinzip erscheinen, und zwar entweder im Flugmodus oder im Phasing-Zustand Ich will hier Null oder Eins. Mathe zu zufälligen Zeiten, zwei in Mathot-Boden eingewickelt, erledigen das für uns Jetzt fangen manche Feinde unsichtbar an, manche fangen unsichtbar Ich möchte, dass die Feinde automatisch zwischen dem Flugstatus und dem Phasenzustand wechseln zwischen dem Flugstatus und dem Phasenzustand Wir haben bereits periodische Ereignisse verwendet , um den Feind alle 1 Sekunde zu aktivieren und um Spritesheets alle 120 Millisekunden aktualisieren zu lassen alle 120 Millisekunden aktualisieren Wir können dafür dieselbe Technik verwenden. Jeder Gegner hat seinen eigenen Schalter, Timer und sein eigenes Schaltintervall Ich lege sie auf jedes Eneme-Objekt. Jeder Gegner hat seine eigenen Timer, sodass wir zum Beispiel jedem Feind ein anderes Intervall geben Wenn ich aus Performancegründen wollte, dass alle Gegner gleichzeitig den Status wechseln , wäre es sinnvoll diese Logik auf die Hauptspielklasse Im Moment möchte ich, dass jeder Gegner alle 2 Sekunden, alle 2000 Millisekunden, den Status wechselt alle 2 Sekunden, alle 2000 Millisekunden, Wir werden diesen Wert in einer Minute randomisieren. Wenn wir mit der Arbeit fertig sind, kann ich die Logik hier einfügen. Der Feind lebt. Wir werden dafür sorgen, dass es regelmäßig zwischen fliegendem und phasengesteuerten Zustand wechselt zwischen fliegendem und phasengesteuerten Zustand Wenn der Schalt-Timer kürzer als das Schaltintervall ist, erhöhen Sie den Schalt-Timer immer um die Delta-Zeit. Andernfalls, d. h. wenn der Switch-Timer genügend Millisekunden gesammelt hat, setzen Sie den Switch-Timer auf Null zurück, damit er bis zum nächsten periodischen Ereignis erneut zählen kann Und rufen Sie die benutzerdefinierte Switch-Methode auf, die wir definieren werden. Jetzt prüft die Switch-Methode einfach, ob dieser aktuelle Punktstatus von 940 diesem Punktzustandsindex Null entspricht , was der Flugzustand ist , wie wir ihn hier in Zeile 139 definiert haben. Wir rufen Set State auf und übergeben ihm Index eins Phasing, wodurch der aktuelle Status der Phase zugewiesen wird der aktuelle Status der Phase zugewiesen Sie ruft start auf, um den Frame-Bereich einzustellen und so weiter. Index eins ist hier. Ich hätte ein Enum-Objekt erstellen können, das 01.2-Indizes in etwas Lesbareres übersetzt , aber ich denke, da wir nur drei Zustände haben, können wir Wir müssen uns nur daran erinnern, dass Index Null fliegt, Index eins phasenweise, Index zwei implodiert sonst, was bedeutet, dass der aktuelle Zustand phasenweise ist Index Null fliegt, Index eins phasenweise, Index zwei implodiert sonst, was bedeutet, dass der aktuelle Zustand phasenweise ist. Wir setzen Zustand zwei ein, um den implodierenden Zustand zu aktivieren . Nur um zu sehen, was passiert, verwenden wir die Delta-Zeit Hier berechnen wir sie in Zeile 215 im Haupt-GS. Wir geben es an die Render-Methode weiter, dann übergeben wir es an die Update-Methode für feindliche Objekte, die sich hier innerhalb der feindlichen GS befinden. Es ist für die Hauptaktualisierungsmethode der gegnerischen Klasse verfügbar , aber wir brauchen es hier nicht wirklich. Wir können einfach direkt zur Phantom Morph-Klasse gehen und hier einfach auf Delta Time zugreifen Dann wird es für unsere Zustandsumschaltlogik verfügbar sein. Wir wechseln den Zustand zwischen Fliegen und Implodieren, was dazu führt Wir wollen eigentlich zwischen Fliegen und Phasen wechseln. Das ist besser. Ich kann dieses Intervall randomisieren, um es unvorhersehbarer zu machen Ich möchte, dass der Feind zerstört wird, wenn wir darauf klicken. Nur im fliegenden Zustand. Wenn wir es während der Phase treffen, geben wir ihm einen Geschwindigkeitsschub Dies wird den Spieler dazu ermutigen, die Klicks richtig zu Hier in der Phantommorph-Unterklasse nennen wir, wenn der Feind am Leben ist, diese Methode, wenn der Feind am Leben ist, diese Methode, die sich auf die Hauptfeindklasse Ich werde das tatsächlich abfangen. Ich gebe ihm eine eigene Treffermethode, die zuerst den gesamten Code innerhalb der Treffermethode auf der übergeordneten feindlichen Klasse ausführt , hier in Zeile 34, um die Kollision zwischen Maus und feindlichem Objekt zu überprüfen Wenn wir den Feind treffen, indem wir ihn mit der Maus anklicken oder mit unseren Berührungsereignissen darauf tippen, werden wir überprüfen, ob der Feind nicht am Leben ist Wir werden es in den implodierenden Zustand versetzen, der auf Zustand zwei eingestellt ist. Ich werde diesen Codeblock herausschneiden. Wir wollen die Treffermethode nur aufrufen, wenn wir uns im Phasing-Zustand oder im Flugzustand befinden oder im Flugzustand Ich werde die Hit-Methode aus dem Phasing-Status entfernen. Vielleicht kann der Feind nicht getroffen werden, wenn er sich in einer Phase bewegt Wenn er unsichtbar ist, kann er nur getroffen werden, wenn er fliegt Ich kann sehen, dass wir Explosionsanimationsbilder zu schnell Ich werde diese Handle-Frames-Methode entfernen , die zwischen Min-Frame und Max-Frame wechselt. Ich werde es nur aufrufen, wenn wir uns im Flugzustand befinden oder im Phasing-Zustand, wenn der Feind implodiert Wir müssen uns nicht mehr mit Frames befassen, weil dieser Codeblock für die gemeinsame übergeordnete Feindklasse die Kontrolle übernimmt Und wenn der Feind nicht am Leben ist, gibt es weniger als Null Leben. Wir erhöhen Frame X weiter, bis wir letzten Frame erreichen, und setzen dann den Feind zurück. Okay, wir räumen das auf. Ich kann hier alle möglichen Dinge tun. Wenn wir auf den Feind klicken, kopiere ich vielleicht diesen Codeblock und setze ihn hier in den Phasing-Status Wenn wir im Phasing-Zustand auf den Feind klicken, geben wir ihm einen Geschwindigkeitsschub Wir schieben ihn vertikal um 25 Pixel. Vielleicht kann ich diesen mit der Maus abgefeuerten Scheck dafür entfernen. Wir sind in der staatlichen Unterklasse Ich muss diesen Anime hier sagen Wenn wir auf einen Feind klicken, während er sich in der Phase bewegt, lassen wir ihn vorwärts springen, anstatt ihn zu zerstören Wenn wir klicken, während er sich im fliegenden Zustand befindet, versetzen wir ihn in den Zustand Implodieren Und wenn die Explosionsanimationsframes nicht mehr abgespielt werden, erhalten wir Punkte Ich könnte zum Beispiel diese beiden Zeilen kopieren. Jedes Mal, wenn wir in den Flugmodus wechseln, füge ich hier und hier Feinde hinzu. Jedes Mal, wenn wir in den Phasing-Status wechseln, werden Geschwindigkeit X und Geschwindigkeit Y wieder zufällig Geschwindigkeit X und Geschwindigkeit Y wieder um sicherzustellen, dass der Feind die Richtung ändert Wenn wir auf den Feind klicken, während er sich im Phasing-Zustand befindet, lassen wir ihn direkt auf Geschwindigkeit X Null herunterfahren Und Geschwindigkeit Y wird zwei Pixel pro Animationsframe betragen. Wenn wir auf den Feind klicken, obwohl wir das nicht sollten, geben wir ihm einen Geschwindigkeitsschub, sodass es wahrscheinlicher ist , dass er eines unserer Besatzungsmitglieder auffrisst. Okay, wir haben eine gemeinsame Feindklasse. Wir erweitern es um eine Phantom-Morph-Unterklasse. Dieser Feindtyp kann fliegen, sich in Phasen bewegen oder implodieren . Wir wechseln zwischen diesen Zuständen, indem wir die aktuellen Zustandseigenschaften auf verschiedene Zustandsunterklassen verweisen aktuellen Zustandseigenschaften auf verschiedene Zustandsunterklassen Mit dieser Set-State-Methode rufen wir von hier aus das Update für die aktuelle Unterklasse des aktiven Zustands auf aus das Update für die aktuelle Unterklasse des aktiven Zustands Mit dieser Codestruktur haben wir für jeden Status einen separaten Codeblock Und wir können dem Feind hier beliebige Verhaltensweisen und Eigenschaften hinzufügen . Auf diese Weise können wir jedes Detail kontrollieren und unseren Code sauber und organisiert halten. Wir haben unserem Feind einen Flugzustand gegeben, in dem wir diesen Bildbereich animieren und Gegner treffen und vernichten können Phasenmodus, in dem wir diesen Bildbereich animieren. Der Feind erhält einen Schub, wenn wir ihn in diesem Zustand und im implodierenden Zustand treffen diesem Zustand und im implodierenden Zustand Das wird nur die Zerstörung des Feindes anregen. So wie ich meinen Code strukturiert habe, müssen wir diese Aktualisierungsmethode hier beibehalten sonst würde unser Code kaputt gehen, wenn Sie ihn hier nicht haben wollen, weil wir ihn momentan für nichts verwenden Sie können es auch auf die übergeordnete Klasse Enemy State setzen, und die Javascript-Vererbung stellt sicher, dass der Code weiterhin funktioniert. Unseren Code auf diese Weise zu strukturieren hat so viele Vorteile. Ich habe eine Reihe von Soundeffekten für dieses Spiel vorbereitet. Lassen Sie mich Ihnen zeigen, wie einfach es ist, es jetzt zu implementieren. 70. L21-Sounds: Sie können mein Horror-Sound-Set im Abschnitt Ressourcen unten herunterladen Horror-Sound-Set im Abschnitt Ressourcen unten Wir werden sie in das Projekt einbringen. Hier in der Datei „Assets erstelle ich ein HTML-Audioelement und habe auf meine neue P3-Datei und ihr die ID des neuen Spiels gegeben Noch eins. Nehmen wir Boom eins P drei ID wird Bum eins sein. Hier oben in der Haupt-GS erstelle ich eine Klasse namens Audio Control Constructor, die nur eine Eigenschaft für jeden Sound hat , der anhand seiner ID auf das Audioelement verweist Ich erstelle eine Instanz dieser Klasse, sodass die Sounds von disdotoundpperty aus in der Hauptspielklasse verfügbar sind . Wenn wir das Spiel starten, kann ich einfach didotundegame nehmen didotundegame Die meisten modernen Browser spielen Audio nicht automatisch ab. Wenn eine Webseite in unserem Projekt geladen wird, muss der Benutzer Enter R drücken oder auf diese R-Taste klicken , um das Spiel zu starten. Das wird als Benutzeraktion betrachtet und ermöglicht das Abspielen von Spielaudio. Wir haben noch einen Sound namens Boom One. Hier gehe ich zu den NEMGs hier unten im implodierenden Zustand. Wenn der Feind in diesen Zustand eintritt und die Startmethode läuft, können wir ihn jetzt abspielen. Du wirst ein Problem feststellen, wenn ich schnell mehrere Gegner zerstöre , weil wir auf dasselbe Audioelement zugreifen Der Sound wird erst abgespielt, wenn der vorherige Loop beendet ist. Ich möchte, dass der Sound jedes Mal abgespielt wird, wenn wir einen Feind vernichten. Wir könnten mehrere Instanzen dieses Sounds erstellen, was der Performance nicht zuträglich wäre. Ich denke, ich kann hier die Web-Audio-API verwenden, was uns eine viel bessere Kontrolle über den Sound gibt. Oder die einfachste Lösung besteht darin, den Sound jedes Mal, wenn er abgespielt werden soll, einfach zum Anfang zurückzuspulen zum Anfang Es mag seltsam klingen, aber es wird tatsächlich den Effekt erzeugen, den wir wollen Lass es mich dir zeigen. Ich erstelle hier eine benutzerdefinierte Spielmethode. Diese Methode verwendet Audio als Argument, was eines davon sein wird. Es wird zum Anfang zurückgespult, indem es seine aktuelle Zeiteigenschaft auf Null setzt, und erst danach ruft es automatisch die eingebaute Play-Methode auf Ich muss die benutzerdefinierte Wiedergabemethode so aufrufen und ihr das Audio übergeben Ich möchte zurückspulen und so spielen. Selbst wenn ich Feinde schnell vernichte, reagiert der Ton jetzt Feinde schnell vernichte, reagiert der Ton Wir werden diesen Sound noch weiter verbessern. Aber bevor wir das tun, importiere ich Folie P drei. Ich gebe ihm eine Vorstellung von Folie. Ich benötige visuelles und akustisches Feedback zu jeder wichtigen Aktion des Feindes. Ich bringe es hier im Phasing-Zustand in das Projekt hier im Phasing-Zustand in das Wenn wir auf den Feind klicken, während er unsichtbar ist, während er sich in der Phase bewegt, spielen wir die Sound Wenn die Spieler nun auf den Feind klicken, obwohl sie es nicht sollten, während der Feind sich in der Phase bewegt, während der Feind sich in der Phase bewegt, gibt das dem Feind einen Geschwindigkeitsschub Und wir werden auch eine akustische Bestätigung erhalten , dass es passiert ist, wenn wir die Folie hören Sound ich bringe mehr Boom-Effekte mit, Bum, 234. Und ich gebe ihm Ideen von Bum, 234. Ich bringe sie in das Projekt ein, hier in der Audiosteuerungsklasse. Wenn wir einen Feind vernichten, möchte ich Boom Two spielen, eine etwas andere Version des Sounds. Ich speichere die Änderungen an all meinen Dateien, drücke Enter oder will das Spiel starten. Schön jetzt, bumm zum Spielen. Lass uns testen, Bum drei. Ich habe beim Mischen einige matschige Schleimuntertöne hinzugefügt , um ihn besonders saftig zu machen Bei Boom Four sind einige Knack - und Flattergeräusche zu hören. Lass mich einen Feind beschreiben. Ich möchte, dass einer dieser vier Sounds zufällig abgespielt wird. Ich erstelle ein Array namens Boom Sounds und füge alle vier als Elemente ein. Wenn wir einen Feind vernichten. Der Sound, den ich abspielen möchte, ist einer der Indizes im Boom Sounds-Array Anstatt Index eins fest zu codieren, möchte ich eine Zufallszahl von 0 bis 3 verwenden. Wenn ich das tue, erhalte ich eine Ganzzahl, entweder Null oder Eins, Zwei oder Ich zerstöre Feinde und zufällige Sounds werden perfekt wiedergegeben Wir haben auch einen Siegessound, wenn wir ein Spiel gewinnen, und einen gruseligen Horrorsound, wenn wir verlieren. Und wir hören einen Horrorschrei , wenn eines unserer Crewmitglieder gefressen wird Um herauszufinden, wo der Code platziert werden muss, der diese Sounds abspielt, müssen wir verstehen, welche Codeblöcke für jeden Animationsframe immer wieder ausgeführt werden und welche Codeblöcke nur einmal ausgeführt Ich möchte, dass der Ton, der verloren geht, nur einmal abgespielt wird , wenn das Spiel beendet ist. Ich kann das hier in unserer benutzerdefinierten Trigger-Game-Over-Methode machen. Wenn der Spieler alle Leben verloren hat, wenn alle Besatzungsmitglieder gegessen wurden, spielen wir den Verlierer. Sound Wenn die Punktzahl höher ist als die Gewinnpunktzahl, spielen wir den Sieg. Sound Gehe zum gegnerischen GS hier in Zeile 57, innerhalb der Aktualisierungsmethode für die gegnerische Klasse. Wenn sich der Feind von oben nach unten über den Bildschirm bewegt und wir ihn nicht zerstört haben, verliert der Spieler ein Leben. Ein Besatzungsmitglied wird gefressen und wir spielen den Schrei Klingt okay, ich habe die Gewinnpunktzahl auf drei gesetzt , damit wir das einfach testen können Wir haben 15 Leben hier auf Linie 108. 15 Besatzungsmitglieder. Wenn die Feinde auf den Grund gehen, werden sie gefressen. Es ist ein bisschen seltsam, wenn mehrere Gegner den Boden erreichen Der Stream-Sound wird von Anfang an so zurückgesetzt, wie wir ihn entworfen haben, aber ich denke nicht, dass er in diesem speziellen Szenario gut funktioniert . Wenn wir unser Leben verlieren, verlieren wir uns. Sound Das hat funktioniert. Ich starte das Spiel neu und habe drei Feinde getötet . Ich gewinne das Spiel. Und der Gewinnersound ertönt. Wir haben eine nette Auswahl an Sounds und sie haben nicht nur einen ästhetischen Effekt, sie haben auch einen Gameplay-Effekt, indem sie dem Spieler eine akustische Bestätigung geben, dass die Interaktion registriert und ausgeführt wurde. Ich möchte den Schrei nicht zurückspulen, also möchte ich, dass er erst wieder abgespielt wird , wenn der vorherige Schrei vollständig abgespielt wurde Lasst uns ein paar Besatzungsmitglieder verlieren und sehen, dass, wenn mehrere Gegner auf den Grund gehen, wir nicht für jedes verlorene Besatzungsmitglied einen Schrei hören , wenn sie sich Aber ich denke, es ist sauber, wenn wir diesen speziellen Sound erst zurücksetzen, wenn er fertig ist Jetzt ist es vielleicht eine gute Idee, deine Gewinnpunktzahl anzupassen und dein Leben so zu spielen , dass das Spiel so schwierig oder einfach wird, wie du es dir wünschst Wenn Sie es auf einem Mobilgerät testen, können Sie einen kurzen Kommentar hinterlassen und mir mitteilen, ob es für Sie funktioniert hat oder ob Sie Probleme mit bestimmten Mobiltelefonen hatten und welcher Browser verwendet wurde. Ich empfehle dafür Google Chrome zu verwenden. Wenn möglich, kannst du einfach einen kurzen Kommentar hinterlassen. Zum Beispiel der Galaxy S 22 Firefox-Browser, er funktioniert oder so ähnlich. Wenn du das tust und dieses Spiel auf deinem Handy testest, würde es mir helfen, die nächsten Handyspiel-Tutorials zu verbessern. Wir können unseren Code testen und andere Geräte in Google Chrome emulieren, aber die Emulation ist möglicherweise nicht zu 100% korrekt Die einzige Möglichkeit, absolut sicher zu sein, besteht darin, den Code auf einem tatsächlichen Gerät auszuführen Das war ein großes Projekt, gut gemacht, wenn Sie es bis hierher geschafft haben.