Cette page a été traduite à partir de l'anglais par la communauté. Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.

View in English Always switch to English

Créer un champ de briques

C'est la 6e étape sur 11 de créer un jeu casse-briques en utilisant uniquement JavaScript. Voyons comment créer un groupe de briques, les afficher à l'écran à l'aide d'une boucle et les supprimer lorsque la balle les touche. Construire le champ de briques est un peu plus compliqué que d'ajouter un seul objet à l'écran.

Dessiner les briques

Toutes les briques utilisent la même image, nous pouvons donc la créer et la décoder une seule fois, puis la partager entre elles. Ajoutez une carte statique assets et un champ url à ObjetJeu, et remplacez son constructeur et sa méthode precharger() :

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

Le cache associe chaque URL à une promesse qui se résout avec l'image décodée. Le premier appel à precharger() pour une URL crée l'image et commence à la décoder ; les appels suivants attendent la même promesse et reçoivent la même image. Chaque objet a toujours sa propre position et taille.

Comme la Balle et la Raquette, la Brique repose également sur la classe ObjetJeu. Une brique n'a pas de position ou de taille par défaut et doit être définie explicitement dans le constructeur. Comme les briques ont des dimensions explicites, nous pouvons utiliser les paramètres supplémentaires dWidth et dHeight de ctx.drawImage(), qui redimensionnent automatiquement l'image si elle n'a pas déjà les dimensions souhaitées.

js
class Brique extends ObjetJeu {
  constructor(url, ctx, x, y, w, h) {
    super(url, ctx);
    this.pos = { x, y };
    this.taille = { w, h };
  }
  dessiner() {
    const { left, top } = this.boiteDeCollision;
    this.ctx.drawImage(this.asset, left, top, this.taille.w, this.taille.h);
  }
}

Vous devez également récupérer l'image de la brique et la sauvegarder dans votre répertoire /img.

Nous plaçons tout le code pour dessiner les briques à l'intérieur d'une fonction initialiserBriques afin de le séparer du reste du code. Ajoutez un appel à initialiserBriques sous elementsCollision.push(raquette); :

js
// …
const raquette = new Raquette("img/paddle.png", ctx);
elementsCollision.push(raquette);
const briques = initialiserBriques();
// …

Assurez-vous également que le jeu attend que les briques soient préchargées avant de commencer le jeu, en ajoutant , ...briques au tableau à l'intérieur de Promise.all(). Ces appels partagent la promesse de décodage mise en cache, donc l'image de la brique n'est décodée qu'une seule fois.

Passons maintenant à la fonction elle-même. Ajoutez la fonction initialiserBriques à la fin du fichier script.js. Pour commencer, nous ajoutons l'objet dispositionBriques, car il nous est très utile très bientôt :

js
function initialiserBriques() {
  const dispositionBriques = {
    width: 50,
    height: 20,
    count: {
      row: 3,
      col: 7,
    },
    offset: {
      top: 50,
      left: 60,
    },
    padding: 10,
  };
  const briques = [];
  // continuer d'ajouter le code ici...
  return briques;
}

Cet objet dispositionBriques contient toutes les informations dont nous avons besoin : la largeur et la hauteur d'une seule brique, le nombre de lignes et de colonnes de briques que nous voyons à l'écran, le décalage par le haut et par la gauche (l'emplacement sur le canevas où nous commençons à dessiner les briques) et l'espacement entre chaque ligne et colonne de briques.

Maintenant, commençons à créer les briques elles-mêmes. Nous pouvons boucler à travers les lignes et les colonnes pour créer une nouvelle brique à chaque itération — ajoutez la boucle imbriquée suivante sous la ligne de code précédente :

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

    const nouvelleBrique = new Brique(
      "img/brick.png",
      ctx,
      briqueX,
      briqueY,
      dispositionBriques.width,
      dispositionBriques.height,
    );
    briques.push(nouvelleBrique);
  }
}

Chaque position briqueX est calculée comme dispositionBriques.width plus dispositionBriques.padding, multipliée par le numéro de colonne, c, plus le dispositionBriques.offset.left ; la logique pour le briqueY est identique sauf qu'elle utilise les valeurs pour le numéro de ligne, r, dispositionBriques.height et dispositionBriques.offset.top. Maintenant, chaque brique peut être placée à sa place correcte, avec un espacement entre chaque brique, et dessinée avec un décalage par rapport aux bords gauche et supérieur du canevas.

Enfin, nous pouvons dessiner ces briques à l'écran à l'intérieur de la fonction actualiser(). Ajoutez ce qui suit sous l'appel à raquette.dessiner() :

js
for (const brique of briques) {
  brique.dessiner();
}

Si vous rechargez index.html à ce stade, vous devez voir les briques affichées à l'écran, à une distance égale les unes des autres.

Détecter la collision Brique/Balle

Passons au défi suivant — la détection des collisions entre la balle et les briques. Heureusement, nous avons déjà implémenté un système de collision très générique, donc nous pouvons simplement y connecter nos briques.

Tout d'abord, enregistrez chaque brique en tant qu'élément de collision, juste en dessous de l'appel à initialiserBriques() :

js
const briques = initialiserBriques();
for (const brique of briques) {
  elementsCollision.push(brique);
}

Ajoutez une méthode enCollision() à chaque brique, qui se supprime elle-même des collections briques et elementsCollision :

js
class Brique extends ObjetJeu {
  // …
  enCollision() {
    briques.splice(briques.indexOf(this), 1);
    elementsCollision.splice(elementsCollision.indexOf(this), 1);
  }
}

La brique doit disparaître le plus rapidement possible, afin que la balle ne rebondisse pas dessus.

Et c'est tout ! Rechargez votre code, et vous devez voir la nouvelle détection des collisions fonctionner comme prévu.

Comparer votre code

Voici ce que vous devez avoir jusqu'à présent, en cours d'exécution en direct. Pour voir son code source, cliquez sur le bouton « Exécuter ».

Prochaines étapes

Nous pouvons frapper les briques et les supprimer, ce qui est déjà un bel ajout à la jouabilité. Il est encore mieux de suivre le score et de gagner lorsque toutes les briques sont détruites.