Objekt-Baupraxis
In den vorherigen Artikeln haben wir alle wesentlichen Theorie- und Syntaxdetails zu JavaScript-Objekten behandelt und Ihnen eine solide Grundlage zum Starten gegeben. In diesem Artikel tauchen wir in eine praktische Übung ein, die Ihnen mehr Übung im Erstellen benutzerdefinierter JavaScript-Objekte bietet und dabei ein lustiges und farbenfrohes Ergebnis erzielt.
Voraussetzungen: | Grundkenntnisse in HTML und CSS, Vertrautheit mit den Grundlagen von JavaScript (siehe Erste Schritte und Bausteine) und die Grundlagen von OOJS (siehe Einführung in Objekte). |
---|---|
Ziel: | Übung im Einsatz von Objekten und objektorientierte Techniken in einem realen Kontext. |
Lassen Sie uns einige Bälle hüpfen
In diesem Artikel schreiben wir eine klassische "Bouncing Balls"-Demo, um Ihnen zu zeigen, wie nützlich Objekte in JavaScript sein können. Unsere kleinen Bälle werden über den Bildschirm hüpfen und ihre Farbe ändern, wenn sie sich gegenseitig berühren. Das fertige Beispiel wird in etwa so aussehen:
Dieses Beispiel nutzt die Canvas API zum Zeichnen der Bälle auf dem Bildschirm und die requestAnimationFrame
API zur Animation der gesamten Anzeige — Sie benötigen keine Vorkenntnisse dieser APIs, und wir hoffen, dass Sie am Ende dieses Artikels neugierig auf diese sein werden. Unterwegs nutzen wir einige praktische Objekte und zeigen Ihnen ein paar schöne Techniken wie Bälle von Wänden abprallen zu lassen und zu prüfen, ob sie sich gegenseitig getroffen haben (auch bekannt als Kollisionserkennung).
Erste Schritte
Zunächst erstellen Sie lokale Kopien unserer Dateien index.html
, style.css
und main.js
. Diese enthalten jeweils folgendes:
- Ein sehr einfaches HTML-Dokument mit einem h1-Element, einem
<canvas>
-Element, auf dem unsere Bälle gezeichnet werden, sowie Elementen, um unser CSS und JavaScript auf unser HTML anzuwenden. - Einige sehr einfache Stile, die hauptsächlich dazu dienen, das
<h1>
zu formatieren und zu positionieren sowie alle Scrollleisten oder Ränder am Rand der Seite zu entfernen (damit es schön und ordentlich aussieht). - Ein wenig JavaScript, das dazu dient, das
<canvas>
-Element einzurichten und eine allgemeine Funktion zur Verfügung zu stellen, die wir verwenden werden.
Der erste Teil des Skripts sieht so aus:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const width = (canvas.width = window.innerWidth);
const height = (canvas.height = window.innerHeight);
Dieses Skript erhält eine Referenz zum <canvas>
-Element und ruft dann die getContext()
-Methode darauf auf, um uns einen Kontext zu geben, auf dem wir zeichnen können. Die resultierende Konstante (ctx
) ist das Objekt, das direkt den Zeichenbereich der Leinwand darstellt und uns ermöglicht, 2D-Formen darauf zu zeichnen.
Als nächstes setzen wir Konstanten namens width
und height
und die Breite und Höhe des Canvas-Elements (dargestellt durch die Eigenschaften canvas.width
und canvas.height
) auf die Breite und Höhe des Browser-Viewports (der Bereich, in dem die Webseite erscheint — dieser kann durch die Eigenschaften Window.innerWidth
und Window.innerHeight
ermittelt werden).
Beachten Sie, dass wir mehrere Zuweisungen zusammenketten, um die Variablen schneller einzurichten — das ist völlig in Ordnung.
Dann haben wir zwei Hilfsfunktionen:
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomRGB() {
return `rgb(${random(0, 255)} ${random(0, 255)} ${random(0, 255)})`;
}
Die Funktion random()
nimmt zwei Zahlen als Argumente und gibt eine Zufallszahl im Bereich zwischen den beiden zurück. Die Funktion randomRGB()
generiert eine Zufallsfarbe, die als rgb()
-String dargestellt wird.
Modellierung eines Balls in unserem Programm
Unser Programm wird viele Bälle zeigen, die über den Bildschirm hüpfen. Da sich diese Bälle alle gleich verhalten, ergibt es Sinn, sie mit einem Objekt darzustellen. Lassen Sie uns mit der folgenden Klassendefinition am Ende unseres Codes beginnen.
class Ball {
constructor(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}
}
Diese Klasse enthält bisher nur einen Konstruktor, in dem wir die Eigenschaften initialisieren können, die jeder Ball benötigt, um in unserem Programm zu funktionieren:
x
undy
Koordinaten — die horizontalen und vertikalen Koordinaten, an denen der Ball auf dem Bildschirm beginnt. Dies kann zwischen 0 (linke obere Ecke) und der Breite und Höhe des Browser-Viewports (rechte untere Ecke) variieren.- horizontale und vertikale Geschwindigkeit (
velX
undvelY
) — jeder Ball erhält eine horizontale und vertikale Geschwindigkeit; diese Werte werden in realen Zahlen regelmäßig zu denx
/y
-Koordinatenwerten addiert, wenn wir die Bälle animieren, um sie um diesen Betrag in jedem Frame zu bewegen. color
— jeder Ball erhält eine Farbe.size
— jeder Ball erhält eine Größe — dies ist sein Radius in Pixeln.
Das behandelt die Eigenschaften, aber wie sieht es mit den Methoden aus? Wir wollen, dass unsere Bälle tatsächlich etwas in unserem Programm tun.
Den Ball zeichnen
Fügen Sie zuerst die folgende draw()
-Methode zur Ball
-Klasse hinzu:
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}
Mit dieser Funktion können wir dem Ball sagen, dass er sich selbst auf dem Bildschirm zeichnen soll, indem wir eine Reihe von Mitgliedern des 2D-Canvas-Kontextes aufrufen, den wir zuvor definiert haben (ctx
). Der Kontext ist wie das Papier, und jetzt wollen wir unseren Stift befehlen, etwas darauf zu zeichnen:
-
Zuerst verwenden wir
beginPath()
, um anzugeben, dass wir eine Form auf dem Papier zeichnen möchten. -
Anschließend verwenden wir
fillStyle
, um die Farbe der Form zu definieren — wir setzen sie auf diecolor
-Eigenschaft unseres Balls. -
Als Nächstes verwenden wir die Methode
arc()
, um eine Bogenform auf das Papier zu zeichnen. Ihre Parameter sind:- Die
x
- undy
-Position des Zentrums des Bogens — wir spezifizieren diex
- undy
-Eigenschaften des Balls. - Der Radius des Bogens — in diesem Fall die
size
-Eigenschaft des Balls. - Die letzten beiden Parameter geben den Anfangs- und Endwinkel des Bogens an. Hier geben wir 0 Grad und
2 * PI
an, was dem vollständigen Kreis (360 Grad in Radianten) entspricht (ärgerlicherweise müssen Sie dies in Radianten angeben). Das ergibt einen vollständigen Kreis. Wenn Sie nur1 * PI
angegeben hätten, würden Sie einen Halbkreis (180 Grad) erhalten.
- Die
-
Zuletzt verwenden wir die Methode
fill()
, die im Wesentlichen festlegt: „Beende das Zeichnen des Pfads, den wir mitbeginPath()
begonnen haben, und fülle den von ihm eingenommenen Bereich mit der Farbe, die wir zuvor infillStyle
angegeben haben.“
Sie können Ihr Objekt bereits testen.
-
Speichern Sie den bisherigen Code und laden Sie die HTML-Datei in einem Browser.
-
Öffnen Sie die JavaScript-Konsole des Browsers und aktualisieren Sie dann die Seite, damit die Leinwandgröße an den kleineren sichtbaren Viewport angepasst wird, der bleibt, wenn die Konsole geöffnet wird.
-
Geben Sie Folgendes ein, um eine neue Ballinstanz zu erstellen:
jsconst testBall = new Ball(50, 100, 4, 4, "blue", 10);
-
Versuchen Sie, seine Mitglieder aufzurufen:
jstestBall.x; testBall.size; testBall.color; testBall.draw();
-
Wenn Sie die letzte Zeile eingeben, sollte der Ball sich irgendwo auf der Leinwand zeichnen.
Aktualisieren der Balldaten
Wir können den Ball in Position zeichnen, aber um den Ball tatsächlich zu bewegen, benötigen wir eine Art Aktualisierungsfunktion. Fügen Sie folgenden Code innerhalb der Klassendefinition für Ball
hinzu:
update() {
if ((this.x + this.size) >= width) {
this.velX = -(this.velX);
}
if ((this.x - this.size) <= 0) {
this.velX = -(this.velX);
}
if ((this.y + this.size) >= height) {
this.velY = -(this.velY);
}
if ((this.y - this.size) <= 0) {
this.velY = -(this.velY);
}
this.x += this.velX;
this.y += this.velY;
}
Die ersten vier Teile der Funktion überprüfen, ob der Ball den Rand der Leinwand erreicht hat. Falls ja, kehren wir die Polarität der jeweiligen Geschwindigkeit um, damit der Ball in die entgegengesetzte Richtung reist. Wenn der Ball also nach oben unterwegs war (negative velY
), wird die vertikale Geschwindigkeit so geändert, dass er stattdessen nach unten reist (positive velY
).
In den vier Fällen überprüfen wir:
- ob die
x
-Koordinate größer ist als die Breite der Leinwand (der Ball bewegt sich über den rechten Rand). - ob die
x
-Koordinate kleiner ist als 0 (der Ball bewegt sich über den linken Rand). - ob die
y
-Koordinate größer ist als die Höhe der Leinwand (der Ball bewegt sich über den unteren Rand). - ob die
y
-Koordinate kleiner ist als 0 (der Ball bewegt sich über den oberen Rand).
In jedem Fall berücksichtigen wir die size
des Balls in der Berechnung, da die x
/y
-Koordinaten im Zentrum des Balls liegen, aber wir möchten, dass der Rand des Balls am Umfang abprallt — wir möchten nicht, dass der Ball halb von der Leinwand geht, bevor er zurückprallt.
Die letzten zwei Zeilen addieren den velX
-Wert zur x
-Koordinate und den velY
-Wert zur y
-Koordinate — der Ball wird in jedem Fall bei jedem Aufruf dieser Methode in Bewegung gesetzt.
Das reicht vorerst; lassen Sie uns mit etwas Animation fortfahren!
Animation des Balls
Jetzt macht's Spaß. Wir werden jetzt Bälle zur Leinwand hinzufügen und sie animieren.
Zuerst müssen wir einen Platz schaffen, um alle Bälle zu speichern und sie dann zu befüllen. Das folgende wird diese Aufgabe erfüllen — fügen Sie es jetzt an das Ende Ihres Codes hinzu:
const balls = [];
while (balls.length < 25) {
const size = random(10, 20);
const ball = new Ball(
// ball position always drawn at least one ball width
// away from the edge of the canvas, to avoid drawing errors
random(0 + size, width - size),
random(0 + size, height - size),
random(-7, 7),
random(-7, 7),
randomRGB(),
size,
);
balls.push(ball);
}
Die while
-Schleife erstellt eine neue Instanz unseres Ball()
, indem sie mit unseren Funktionen random()
und randomRGB()
zufällige Werte generiert und diese dann mit push()
an das Ende unseres balls-Arrays anhängt, jedoch nur, solange die Anzahl der Bälle im Array kleiner als 25 ist. Also, wenn wir 25 Bälle im Array haben, werden keine weiteren Bälle angehängt. Sie können versuchen, die Zahl in balls.length < 25
zu variieren, um mehr oder weniger Bälle im Array zu erhalten. Je nachdem, wie viel Rechenleistung Ihr Computer/Browser hat, könnte die Angabe mehrerer Tausend Bälle die Animation erheblich verlangsamen!
Fügen Sie als Nächstes das folgende Code-Beispiel an das Ende Ihres Codes hinzu:
function loop() {
ctx.fillStyle = "rgb(0 0 0 / 25%)";
ctx.fillRect(0, 0, width, height);
for (const ball of balls) {
ball.draw();
ball.update();
}
requestAnimationFrame(loop);
}
Alle Programme, die Dinge animieren, beinhalten in der Regel eine Animationsschleife, die dazu dient, die Informationen im Programm zu aktualisieren und dann die resultierende Ansicht in jedem Frame der Animation zu rendern; dies ist die Grundlage für die meisten Spiele und ähnliche Programme. Unsere loop()
-Funktion tut Folgendes:
- Setzt die Füllfarbe der Leinwand auf halbtransparentes Schwarz und zeichnet dann ein Rechteck dieser Farbe über die gesamte Breite und Höhe der Leinwand, mit
fillRect()
(die vier Parameter geben eine Startkoordinate und eine Breite und Höhe für das gezeichnete Rechteck an). Dies dient dazu, die Zeichnung des vorherigen Frames zu überdecken, bevor der nächste gezeichnet wird. Wenn Sie dies nicht tun, sehen Sie nur lange Schlangen, die sich über die Leinwand schlängeln, anstatt Bälle, die sich bewegen! Die Farbe des Füllens ist auf halbtransparent eingestellt,rgb(0 0 0 / 25%)
, um die vorherigen Frames leicht durchscheinen zu lassen und so die kleinen Spuren hinter den Bällen zu erzeugen, während sie sich bewegen. Wenn Sie 0.25 in 1 ändern, sehen Sie sie überhaupt nicht mehr. Versuchen Sie, diese Zahl zu variieren, um den Effekt zu sehen, den sie hat. - Durchläuft alle Bälle im
balls
-Array und führt diedraw()
undupdate()
Methoden jedes Balls aus, um jeden auf dem Bildschirm zu zeichnen, und dann die notwendigen Updates zu Position und Geschwindigkeit in der Zeit für den nächsten Frame durchzuführen. - Führt die Funktion erneut mit der
requestAnimationFrame()
-Methode aus — wenn diese Methode wiederholt ausgeführt und demselben Funktionsnamen übergeben wird, führt sie diese Funktion eine festgelegte Anzahl von Malen pro Sekunde aus, um eine flüssige Animation zu erstellen. Dies geschieht in der Regel rekursiv — was bedeutet, dass die Funktion sich selbst jedes Mal aufruft, wenn sie ausgeführt wird, sodass sie immer wieder läuft.
Fügen Sie schließlich die folgende Zeile an das Ende Ihres Codes hinzu — wir müssen die Funktion einmal aufrufen, um die Animation zu starten.
loop();
Das war's für die Grundlagen — versuchen Sie, zu speichern und zu aktualisieren, um Ihre hüpfenden Bälle zu testen!
Hinzufügen von Kollisionserkennung
Nun zum Spaß, lassen Sie uns etwas Kollisionserkennung zu unserem Programm hinzufügen, damit unsere Bälle wissen, wann sie einen anderen Ball getroffen haben.
Fügen Sie zunächst die folgende Methodendefinition zu Ihrer Ball
-Klasse hinzu.
collisionDetect() {
for (const ball of balls) {
if (this !== ball) {
const dx = this.x - ball.x;
const dy = this.y - ball.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + ball.size) {
ball.color = this.color = randomRGB();
}
}
}
}
Diese Methode ist etwas komplex, daher machen Sie sich keine Sorgen, wenn Sie nicht sofort verstehen, wie sie funktioniert. Eine Erklärung folgt:
- Für jeden Ball müssen wir jeden anderen Ball überprüfen, um zu sehen, ob er mit dem aktuellen Ball kollidiert ist. Um dies zu tun, starten wir eine weitere
for...of
Schleife, um alle Bälle imballs[]
-Array zu durchlaufen. - Unmittelbar innerhalb der for-Schleife verwenden wir eine
if
-Bedingung, um zu überprüfen, ob der aktuelle durchlaufene Ball derselbe Ball wie derjenige ist, den wir gerade überprüfen. Wir möchten nicht prüfen, ob ein Ball mit sich selbst kollidiert ist! Um dies zu tun, überprüfen wir, ob der aktuelle Ball (d. h. der Ball, dessen collisionDetect-Methode aufgerufen wird) derselbe ist wie der Schleifenball (also der Ball, auf den sich die aktuelle Iteration der for-Schleife in der collisionDetect-Methode bezieht). Wir verwenden dann!
, um die Prüfung zu negieren, sodass der Code innerhalb derif
-Bedingung nur dann ausgeführt wird, wenn sie nicht dieselben sind. - Wir verwenden dann einen allgemeinen Algorithmus, um die Kollision von zwei Kreisen zu prüfen. Wir überprüfen im Wesentlichen, ob sich die Flächen der beiden Kreise überlappen. Dies wird weiter in 2D-Kollisionserkennung erklärt.
- Wenn eine Kollision entdeckt wird, wird der Code innerhalb der inneren
if
-Bedingung ausgeführt. In diesem Fall setzen wir nur diecolor
Eigenschaft beider Kreise auf eine neue zufällige Farbe. Wir hätten etwas weitaus Komplexeres machen können, etwa die Bälle realistisch abprallen zu lassen, aber das wäre viel komplizierter zu implementieren gewesen. Für solche Physiksimulationen verwenden Entwickler in der Regel Spiele- oder Physikbibliotheken wie PhysicsJS, matter.js, Phaser usw.
Sie müssen diese Methode auch in jedem Frame der Animation aufrufen. Aktualisieren Sie Ihre loop()
-Funktion, um ball.collisionDetect()
nach ball.update()
aufzurufen:
function loop() {
ctx.fillStyle = "rgb(0 0 0 / 25%)";
ctx.fillRect(0, 0, width, height);
for (const ball of balls) {
ball.draw();
ball.update();
ball.collisionDetect();
}
requestAnimationFrame(loop);
}
Speichern und aktualisieren Sie die Demo erneut, und Sie werden sehen, dass Ihre Bälle ihre Farbe ändern, wenn sie kollidieren!
Hinweis: Wenn Sie Probleme haben, dieses Beispiel zum Laufen zu bringen, versuchen Sie, Ihren JavaScript-Code mit unserer fertigen Version zu vergleichen (siehe auch die Live-Demo hier).
Zusammenfassung
Wir hoffen, dass Sie Spaß daran hatten, Ihr eigenes reales Beispiel mit zufällig hüpfenden Bällen zu schreiben, wobei verschiedene Objekt- und objektorientierte Techniken aus dem gesamten Modul verwendet wurden! Dies sollte Ihnen einige nützliche Übungen zur Verwendung von Objekten und guten realen Kontext gegeben haben.
Das war's für die Artikel über Objekte — jetzt bleibt nur noch, dass Sie Ihre Fähigkeiten in der Objektbewertung testen.
Siehe auch
- Canvas-Tutorial — ein Leitfaden für Anfänger zur Verwendung von 2D-Canvas.
- requestAnimationFrame()
- 2D-Kollisionserkennung
- 3D-Kollisionserkennung
- 2D-Breakout-Spiel mit purem JavaScript — ein großartiges Einsteiger-Tutorial, das zeigt, wie man ein 2D-Spiel erstellt.
- 2D-Breakout-Spiel mit Phaser — erklärt die Grundlagen des Erstellens eines 2D-Spiels mit einer JavaScript-Spielebibliothek.