You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/fr/6-space-game/3-moving-elements-around
leestott 7cfaffabb5
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 4 weeks ago

README.md

Construire un jeu spatial Partie 3 : Ajouter du mouvement

Quiz avant le cours

Quiz avant le cours

Les jeux ne sont pas très amusants tant que vous n'avez pas des extraterrestres qui se déplacent à l'écran ! Dans ce jeu, nous allons utiliser deux types de mouvements :

  • Mouvement clavier/souris : lorsque l'utilisateur interagit avec le clavier ou la souris pour déplacer un objet à l'écran.
  • Mouvement induit par le jeu : lorsque le jeu déplace un objet à un certain intervalle de temps.

Alors, comment déplace-t-on des objets à l'écran ? Tout repose sur les coordonnées cartésiennes : on modifie la position (x, y) de l'objet, puis on redessine l'écran.

En général, voici les étapes nécessaires pour réaliser un mouvement à l'écran :

  1. Définir une nouvelle position pour un objet ; cela est nécessaire pour donner l'impression que l'objet s'est déplacé.
  2. Effacer l'écran, l'écran doit être nettoyé entre chaque dessin. On peut le faire en dessinant un rectangle rempli avec une couleur de fond.
  3. Redessiner l'objet à sa nouvelle position. Cela permet finalement de déplacer l'objet d'un endroit à un autre.

Voici à quoi cela peut ressembler en code :

//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);

Pouvez-vous penser à une raison pour laquelle redessiner votre héros plusieurs fois par seconde pourrait entraîner des coûts de performance ? Lisez à propos des alternatives à ce modèle.

Gérer les événements clavier

Vous gérez les événements en attachant des événements spécifiques à du code. Les événements clavier sont déclenchés sur l'ensemble de la fenêtre, tandis que les événements souris comme un click peuvent être connectés à un élément spécifique. Nous utiliserons des événements clavier tout au long de ce projet.

Pour gérer un événement, vous devez utiliser la méthode addEventListener() de la fenêtre et lui fournir deux paramètres. Le premier paramètre est le nom de l'événement, par exemple keyup. Le second paramètre est la fonction qui doit être invoquée lorsque l'événement se produit.

Voici un exemple :

window.addEventListener('keyup', (evt) => {
  // `evt.key` = string representation of the key
  if (evt.key === 'ArrowUp') {
    // do something
  }
})

Pour les événements clavier, il existe deux propriétés sur l'événement que vous pouvez utiliser pour savoir quelle touche a été pressée :

  • key, qui est une représentation sous forme de chaîne de la touche pressée, par exemple ArrowUp.
  • keyCode, qui est une représentation numérique, par exemple 37, correspondant à ArrowLeft.

La manipulation des événements clavier est utile en dehors du développement de jeux. À quelles autres utilisations pouvez-vous penser pour cette technique ?

Touches spéciales : une mise en garde

Certaines touches spéciales affectent la fenêtre. Cela signifie que si vous écoutez un événement keyup et que vous utilisez ces touches spéciales pour déplacer votre héros, cela entraînera également un défilement horizontal. Pour cette raison, vous pourriez vouloir désactiver ce comportement intégré du navigateur lorsque vous développez votre jeu. Vous avez besoin d'un code comme celui-ci :

let onKeyDown = function (e) {
  console.log(e.keyCode);
  switch (e.keyCode) {
    case 37:
    case 39:
    case 38:
    case 40: // Arrow keys
    case 32:
      e.preventDefault();
      break; // Space
    default:
      break; // do not block other keys
  }
};

window.addEventListener('keydown', onKeyDown);

Le code ci-dessus garantit que les touches fléchées et la barre d'espace ont leur comportement par défaut désactivé. Le mécanisme de désactivation se produit lorsque nous appelons e.preventDefault().

Mouvement induit par le jeu

Nous pouvons faire bouger des objets par eux-mêmes en utilisant des minuteries comme les fonctions setTimeout() ou setInterval() qui mettent à jour la position de l'objet à chaque tick, ou intervalle de temps. Voici à quoi cela peut ressembler :

let id = setInterval(() => {
  //move the enemy on the y axis
  enemy.y += 10;
})

La boucle de jeu

La boucle de jeu est un concept qui consiste essentiellement en une fonction invoquée à intervalles réguliers. On l'appelle la boucle de jeu car tout ce qui doit être visible pour l'utilisateur est dessiné dans cette boucle. La boucle de jeu utilise tous les objets du jeu qui en font partie, en les dessinant tous sauf si, pour une raison quelconque, ils ne font plus partie du jeu. Par exemple, si un objet est un ennemi touché par un laser et explose, il ne fait plus partie de la boucle de jeu actuelle (vous en apprendrez davantage à ce sujet dans les leçons suivantes).

Voici à quoi une boucle de jeu peut ressembler, exprimée en code :

let gameLoopId = setInterval(() =>
  function gameLoop() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawHero();
    drawEnemies();
    drawStaticObjects();
}, 200);

La boucle ci-dessus est invoquée toutes les 200 millisecondes pour redessiner le canvas. Vous pouvez choisir l'intervalle qui convient le mieux à votre jeu.

Poursuivre le jeu spatial

Vous allez prendre le code existant et l'étendre. Soit vous commencez avec le code que vous avez complété lors de la partie I, soit vous utilisez le code dans Partie II - starter.

  • Déplacer le héros : vous ajouterez du code pour permettre de déplacer le héros à l'aide des touches fléchées.
  • Déplacer les ennemis : vous devrez également ajouter du code pour que les ennemis se déplacent de haut en bas à un rythme donné.

Étapes recommandées

Trouvez les fichiers qui ont été créés pour vous dans le sous-dossier your-work. Il devrait contenir les éléments suivants :

-| assets
  -| enemyShip.png
  -| player.png
-| index.html
-| app.js
-| package.json

Vous démarrez votre projet dans le dossier your_work en tapant :

cd your-work
npm start

Cela démarrera un serveur HTTP à l'adresse http://localhost:5000. Ouvrez un navigateur et entrez cette adresse, pour l'instant, cela devrait afficher le héros et tous les ennemis ; rien ne bouge - encore !

Ajouter du code

  1. Ajoutez des objets dédiés pour hero, enemy et game object, ils devraient avoir des propriétés x et y. (Rappelez-vous la section sur Héritage ou composition).

    CONSEIL game object devrait être celui avec les propriétés x et y et la capacité de se dessiner sur un canvas.

    Conseil : commencez par ajouter une nouvelle classe GameObject avec son constructeur défini comme ci-dessous, puis dessinez-la sur le canvas :

    
    class GameObject {
      constructor(x, y) {
        this.x = x;
        this.y = y;
        this.dead = false;
        this.type = "";
        this.width = 0;
        this.height = 0;
        this.img = undefined;
      }
    
      draw(ctx) {
        ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
      }
    }
    

    Maintenant, étendez cet objet GameObject pour créer le Hero et l'Enemy.

    class Hero extends GameObject {
      constructor(x, y) {
        ...it needs an x, y, type, and speed
      }
    }
    
    class Enemy extends GameObject {
      constructor(x, y) {
        super(x, y);
        (this.width = 98), (this.height = 50);
        this.type = "Enemy";
        let id = setInterval(() => {
          if (this.y < canvas.height - this.height) {
            this.y += 5;
          } else {
            console.log('Stopped at', this.y)
            clearInterval(id);
          }
        }, 300)
      }
    }
    
  2. Ajoutez des gestionnaires d'événements clavier pour gérer la navigation (déplacer le héros vers le haut/bas gauche/droite).

    RAPPEL c'est un système cartésien, le coin supérieur gauche est 0,0. N'oubliez pas non plus d'ajouter du code pour arrêter le comportement par défaut.

    Conseil : créez votre fonction onKeyDown et attachez-la à la fenêtre :

     let onKeyDown = function (e) {
           console.log(e.keyCode);
             ...add the code from the lesson above to stop default behavior
           }
     };
    
     window.addEventListener("keydown", onKeyDown);
    

    Vérifiez la console de votre navigateur à ce stade, et observez les frappes de touches enregistrées.

  3. Implémentez le modèle Pub/Sub, cela gardera votre code propre pour les parties restantes.

    Pour cette dernière partie, vous pouvez :

    1. Ajouter un écouteur d'événements sur la fenêtre :

       window.addEventListener("keyup", (evt) => {
         if (evt.key === "ArrowUp") {
           eventEmitter.emit(Messages.KEY_EVENT_UP);
         } else if (evt.key === "ArrowDown") {
           eventEmitter.emit(Messages.KEY_EVENT_DOWN);
         } else if (evt.key === "ArrowLeft") {
           eventEmitter.emit(Messages.KEY_EVENT_LEFT);
         } else if (evt.key === "ArrowRight") {
           eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
         }
       });
      
    2. Créer une classe EventEmitter pour publier et s'abonner à des messages :

      class EventEmitter {
        constructor() {
          this.listeners = {};
        }
      
        on(message, listener) {
          if (!this.listeners[message]) {
            this.listeners[message] = [];
          }
          this.listeners[message].push(listener);
        }
      
        emit(message, payload = null) {
          if (this.listeners[message]) {
            this.listeners[message].forEach((l) => l(message, payload));
          }
        }
      }
      
    3. Ajouter des constantes et configurer l'EventEmitter :

      const Messages = {
        KEY_EVENT_UP: "KEY_EVENT_UP",
        KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
        KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
        KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
      };
      
      let heroImg, 
          enemyImg, 
          laserImg,
          canvas, ctx, 
          gameObjects = [], 
          hero, 
          eventEmitter = new EventEmitter();
      
    4. Initialiser le jeu

    function initGame() {
      gameObjects = [];
      createEnemies();
      createHero();
    
      eventEmitter.on(Messages.KEY_EVENT_UP, () => {
        hero.y -=5 ;
      })
    
      eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
        hero.y += 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
        hero.x -= 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
        hero.x += 5;
      });
    }
    
  4. Configurer la boucle de jeu

    Refactorisez la fonction window.onload pour initialiser le jeu et configurer une boucle de jeu à un bon intervalle. Vous ajouterez également un rayon laser :

    window.onload = async () => {
      canvas = document.getElementById("canvas");
      ctx = canvas.getContext("2d");
      heroImg = await loadTexture("assets/player.png");
      enemyImg = await loadTexture("assets/enemyShip.png");
      laserImg = await loadTexture("assets/laserRed.png");
    
      initGame();
      let gameLoopId = setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        drawGameObjects(ctx);
      }, 100)
    
    };
    
  5. Ajoutez du code pour déplacer les ennemis à un certain intervalle

    Refactorisez la fonction createEnemies() pour créer les ennemis et les ajouter à la nouvelle classe gameObjects :

    function createEnemies() {
      const MONSTER_TOTAL = 5;
      const MONSTER_WIDTH = MONSTER_TOTAL * 98;
      const START_X = (canvas.width - MONSTER_WIDTH) / 2;
      const STOP_X = START_X + MONSTER_WIDTH;
    
      for (let x = START_X; x < STOP_X; x += 98) {
        for (let y = 0; y < 50 * 5; y += 50) {
          const enemy = new Enemy(x, y);
          enemy.img = enemyImg;
          gameObjects.push(enemy);
        }
      }
    }
    

    et ajoutez une fonction createHero() pour effectuer un processus similaire pour le héros.

    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    et enfin, ajoutez une fonction drawGameObjects() pour commencer le dessin :

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    Vos ennemis devraient commencer à avancer vers votre vaisseau spatial héros !


🚀 Défi

Comme vous pouvez le constater, votre code peut devenir un "code spaghetti" lorsque vous commencez à ajouter des fonctions, des variables et des classes. Comment pouvez-vous mieux organiser votre code pour qu'il soit plus lisible ? Esquissez un système pour organiser votre code, même s'il reste dans un seul fichier.

Quiz après le cours

Quiz après le cours

Révision et auto-apprentissage

Bien que nous écrivions notre jeu sans utiliser de frameworks, il existe de nombreux frameworks JavaScript basés sur le canvas pour le développement de jeux. Prenez le temps de faire quelques lectures à ce sujet.

Devoir

Commentez votre code


Avertissement :
Ce document a été traduit à l'aide du service de traduction automatique Co-op Translator. Bien que nous nous efforcions d'assurer l'exactitude, veuillez noter que les traductions automatisées peuvent contenir des erreurs ou des inexactitudes. Le document original dans sa langue d'origine doit être considéré comme la source faisant autorité. Pour des informations critiques, il est recommandé de faire appel à une traduction humaine professionnelle. Nous déclinons toute responsabilité en cas de malentendus ou d'interprétations erronées résultant de l'utilisation de cette traduction.