Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Das Ziegelfeld erstellen

Dies ist der 6. von 11 Schritten des Tutorials zum Erstellen eines Breakout-Spiels mit reinem JavaScript. Sehen wir uns an, wie Sie eine Gruppe von Ziegeln erstellen, sie mithilfe einer Schleife auf dem Bildschirm zeichnen und entfernen, wenn der Ball sie trifft. Das Ziegelfeld zu erstellen ist etwas komplizierter, als ein einzelnes Objekt auf dem Bildschirm hinzuzufügen.

Die Ziegel zeichnen

Alle Ziegel verwenden dasselbe Bild. Daher können wir es einmal erstellen und dekodieren und anschließend für alle Ziegel verwenden. Fügen Sie GameObject eine statische assets-Map und ein url-Feld hinzu und ersetzen Sie den Konstruktor und die Methode preload():

js
class GameObject {
  static assets = new Map();
  url;
  // …
  constructor(url, ctx) {
    this.url = url;
    this.ctx = ctx;
  }
  async preload() {
    if (!GameObject.assets.has(this.url)) {
      const asset = new Image();
      asset.src = this.url;
      GameObject.assets.set(
        this.url,
        asset.decode().then(() => asset),
      );
    }
    this.asset = await GameObject.assets.get(this.url);
    if (this.size.w === undefined) {
      this.size.w = this.asset.width;
      this.size.h = this.asset.height;
    }
  }
  // …
}

Der Cache ordnet jeder URL ein Promise zu, das mit dem dekodierten Bild erfüllt wird. Der erste preload()-Aufruf für eine URL erstellt das Bild und startet die Dekodierung. Spätere Aufrufe warten auf dasselbe Promise und erhalten dasselbe Bild. Jedes Objekt hat weiterhin seine eigene Position und Größe.

Wie Ball und Paddle basiert auch Brick auf der Klasse GameObject. Ein Ziegel hat keine Standardposition und -größe; beides muss im Konstruktor ausdrücklich angegeben werden. Da die Ziegel feste Abmessungen haben, können wir die zusätzlichen Parameter dWidth und dHeight von ctx.drawImage() verwenden. Damit wird das Bild automatisch skaliert, falls es noch nicht die gewünschten Abmessungen hat.

js
class Brick extends GameObject {
  constructor(url, ctx, x, y, w, h) {
    super(url, ctx);
    this.pos = { x, y };
    this.size = { w, h };
  }
  draw() {
    const { left, top } = this.hitbox;
    this.ctx.drawImage(this.asset, left, top, this.size.w, this.size.h);
  }
}

Sie müssen außerdem das Ziegelbild herunterladen und in Ihrem Verzeichnis /img speichern.

Wir fügen den gesamten Code zum Zeichnen der Ziegel in eine Funktion initBricks ein, damit er vom übrigen Code getrennt bleibt. Fügen Sie unter colliders.push(paddle); einen Aufruf von initBricks hinzu:

js
// …
const paddle = new Paddle("img/paddle.png", ctx);
colliders.push(paddle);
const bricks = initBricks();
// …

Stellen Sie außerdem sicher, dass das Spiel wartet, bis die Ziegel vorgeladen sind, bevor es startet: Fügen Sie dazu , ...bricks zum Array in Promise.all() hinzu. Diese Aufrufe verwenden dasselbe zwischengespeicherte Dekodierungs-Promise, sodass das Ziegelbild nur einmal dekodiert wird.

Nun zur Funktion selbst. Fügen Sie die Funktion initBricks am Ende der Datei script.js hinzu. Zunächst definieren wir das Objekt bricksLayout, das wir gleich benötigen:

js
function initBricks() {
  const bricksLayout = {
    width: 50,
    height: 20,
    count: {
      row: 3,
      col: 7,
    },
    offset: {
      top: 50,
      left: 60,
    },
    padding: 10,
  };
  const bricks = [];
  // continue adding here...
  return bricks;
}

bricksLayout enthält alle benötigten Informationen: die Breite und Höhe eines einzelnen Ziegels, die Anzahl der Ziegelreihen und -spalten auf dem Bildschirm, den Abstand vom oberen und linken Rand (also die Stelle auf dem Canvas, an der wir mit dem Zeichnen der Ziegel beginnen) sowie den Abstand zwischen den einzelnen Reihen und Spalten.

Erstellen wir nun die Ziegel. Wir können die Reihen und Spalten mit Schleifen durchlaufen und bei jedem Durchlauf einen neuen Ziegel erstellen. Fügen Sie die folgende verschachtelte Schleife unter der vorherigen Codezeile hinzu:

js
for (let c = 0; c < bricksLayout.count.col; c++) {
  for (let r = 0; r < bricksLayout.count.row; r++) {
    const brickX =
      c * (bricksLayout.width + bricksLayout.padding) +
      bricksLayout.offset.left;
    const brickY =
      r * (bricksLayout.height + bricksLayout.padding) +
      bricksLayout.offset.top;

    const newBrick = new Brick(
      "img/brick.png",
      ctx,
      brickX,
      brickY,
      bricksLayout.width,
      bricksLayout.height,
    );
    bricks.push(newBrick);
  }
}

Jede brickX-Position ergibt sich aus bricksLayout.width plus bricksLayout.padding, multipliziert mit der Spaltennummer c, zuzüglich bricksLayout.offset.left. Die Berechnung für brickY funktioniert genauso, verwendet aber die Reihennummer r, bricksLayout.height und bricksLayout.offset.top. So lässt sich jeder Ziegel an der richtigen Stelle platzieren, mit Abstand zu den anderen Ziegeln und zu den linken und oberen Rändern des Canvas.

Zum Schluss zeichnen wir die Ziegel innerhalb der Funktion update() auf den Bildschirm. Fügen Sie Folgendes unter dem Aufruf von paddle.draw() hinzu:

js
for (const brick of bricks) {
  brick.draw();
}

Wenn Sie index.html jetzt neu laden, sollten die Ziegel in gleichmäßigen Abständen auf dem Bildschirm erscheinen.

Kollisionserkennung zwischen Ziegel und Ball

Die nächste Herausforderung ist die Kollisionserkennung zwischen dem Ball und den Ziegeln. Glücklicherweise haben wir bereits ein allgemeines Kollisionssystem implementiert und müssen nur noch die Ziegel daran anbinden.

Registrieren Sie zunächst jeden Ziegel als Collider, direkt unter dem Aufruf von initBricks():

js
const bricks = initBricks();
for (const brick of bricks) {
  colliders.push(brick);
}

Fügen Sie jedem Ziegel eine Methode onCollide() hinzu, die ihn aus den Sammlungen bricks und colliders entfernt:

js
class Brick extends GameObject {
  // …
  onCollide() {
    bricks.splice(bricks.indexOf(this), 1);
    colliders.splice(colliders.indexOf(this), 1);
  }
}

Der Ziegel muss so schnell wie möglich entfernt werden, damit der Ball nicht an ihm abprallt.

Das war’s! Laden Sie Ihren Code neu. Die neue Kollisionserkennung sollte nun wie gewünscht funktionieren.

Vergleichen Sie Ihren Code

So sollte Ihr bisheriger Stand aussehen – Sie können ihn hier direkt ausprobieren. Um den Quellcode anzuzeigen, klicken Sie auf die Schaltfläche „Play“.

Nächste Schritte

Wir können die Ziegel treffen und entfernen – das ist bereits eine schöne Erweiterung des Spiels. Noch besser wäre es, den Punktestand zu verfolgen und das Spiel zu gewinnen, sobald alle Ziegel zerstört sind.