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.