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/it/6-space-game/3-moving-elements-around/README.md

14 KiB

Costruire un Gioco Spaziale Parte 3: Aggiungere il Movimento

Quiz Pre-Lezione

Quiz pre-lezione

I giochi non sono molto divertenti finché non ci sono alieni che si muovono sullo schermo! In questo gioco, utilizzeremo due tipi di movimenti:

  • Movimento tramite tastiera/mouse: quando l'utente interagisce con la tastiera o il mouse per spostare un oggetto sullo schermo.
  • Movimento indotto dal gioco: quando il gioco sposta un oggetto a intervalli di tempo definiti.

Quindi, come facciamo a muovere le cose sullo schermo? Tutto si basa sulle coordinate cartesiane: cambiamo la posizione (x, y) dell'oggetto e poi ridisegniamo lo schermo.

Tipicamente, sono necessari i seguenti passaggi per ottenere il movimento sullo schermo:

  1. Impostare una nuova posizione per un oggetto; questo è necessario per percepire che l'oggetto si è spostato.
  2. Pulire lo schermo, lo schermo deve essere ripulito tra un disegno e l'altro. Possiamo farlo disegnando un rettangolo riempito con un colore di sfondo.
  3. Ridisegnare l'oggetto nella nuova posizione. In questo modo riusciamo finalmente a spostare l'oggetto da una posizione all'altra.

Ecco come potrebbe apparire in codice:

//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);

Riesci a pensare a un motivo per cui ridisegnare il tuo eroe molte volte al secondo potrebbe comportare costi di prestazioni? Leggi di più su alternative a questo schema.

Gestire gli eventi della tastiera

Gli eventi vengono gestiti collegando eventi specifici al codice. Gli eventi della tastiera vengono attivati sull'intera finestra, mentre eventi del mouse come un click possono essere collegati al clic su un elemento specifico. Utilizzeremo eventi della tastiera in tutto questo progetto.

Per gestire un evento, è necessario utilizzare il metodo addEventListener() della finestra e fornirgli due parametri di input. Il primo parametro è il nome dell'evento, ad esempio keyup. Il secondo parametro è la funzione che dovrebbe essere invocata quando l'evento si verifica.

Ecco un esempio:

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

Per gli eventi della tastiera ci sono due proprietà sull'evento che puoi utilizzare per vedere quale tasto è stato premuto:

  • key, questa è una rappresentazione in stringa del tasto premuto, ad esempio ArrowUp
  • keyCode, questa è una rappresentazione numerica, ad esempio 37, che corrisponde a ArrowLeft.

La manipolazione degli eventi della tastiera è utile anche al di fuori dello sviluppo di giochi. A quali altri usi puoi pensare per questa tecnica?

Tasti speciali: una nota importante

Ci sono alcuni tasti speciali che influenzano la finestra. Ciò significa che se stai ascoltando un evento keyup e utilizzi questi tasti speciali per muovere il tuo eroe, si verificherà anche uno scorrimento orizzontale. Per questo motivo potresti voler disattivare questo comportamento predefinito del browser mentre sviluppi il tuo gioco. Ti serve un codice come questo:

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);

Il codice sopra assicurerà che i tasti freccia e la barra spaziatrice abbiano il loro comportamento predefinito disattivato. Il meccanismo di disattivazione avviene quando chiamiamo e.preventDefault().

Movimento indotto dal gioco

Possiamo far muovere le cose autonomamente utilizzando timer come le funzioni setTimeout() o setInterval() che aggiornano la posizione dell'oggetto a ogni intervallo di tempo. Ecco come potrebbe apparire:

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

Il ciclo del gioco

Il ciclo del gioco è un concetto che consiste essenzialmente in una funzione invocata a intervalli regolari. Si chiama ciclo del gioco perché tutto ciò che dovrebbe essere visibile all'utente viene disegnato all'interno del ciclo. Il ciclo del gioco utilizza tutti gli oggetti di gioco che fanno parte del gioco, disegnandoli tutti a meno che, per qualche motivo, non debbano più farne parte. Ad esempio, se un oggetto è un nemico colpito da un laser e viene distrutto, non fa più parte del ciclo del gioco corrente (imparerai di più su questo nelle lezioni successive).

Ecco come potrebbe apparire un ciclo del gioco, espresso in codice:

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);

Il ciclo sopra viene invocato ogni 200 millisecondi per ridisegnare il canvas. Hai la possibilità di scegliere l'intervallo migliore per il tuo gioco.

Continuare il Gioco Spaziale

Prenderai il codice esistente e lo estenderai. Puoi iniziare con il codice che hai completato durante la parte I o utilizzare il codice in Parte II - starter.

  • Muovere l'eroe: aggiungerai codice per assicurarti di poter muovere l'eroe usando i tasti freccia.
  • Muovere i nemici: dovrai anche aggiungere codice per assicurarti che i nemici si muovano dall'alto verso il basso a una velocità definita.

Passaggi consigliati

Trova i file che sono stati creati per te nella sottocartella your-work. Dovrebbe contenere quanto segue:

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

Avvia il tuo progetto nella cartella your_work digitando:

cd your-work
npm start

Il comando sopra avvierà un server HTTP all'indirizzo http://localhost:5000. Apri un browser e inserisci quell'indirizzo; al momento dovrebbe mostrare l'eroe e tutti i nemici, ma nulla si muove - ancora!

Aggiungi codice

  1. Aggiungi oggetti dedicati per hero, enemy e game object, che dovrebbero avere proprietà x e y. (Ricorda la sezione su Ereditarietà o composizione).

    SUGGERIMENTO: game object dovrebbe essere quello con x e y e la capacità di disegnarsi su un canvas.

    suggerimento: inizia aggiungendo una nuova classe GameObject con il suo costruttore delineato come segue, e poi disegnala sul 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);
      }
    }
    

    Ora, estendi questo GameObject per creare Hero e 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. Aggiungi gestori di eventi per i tasti per gestire la navigazione tramite tasti (muovi l'eroe su/giù, sinistra/destra).

    RICORDA: è un sistema cartesiano, l'angolo in alto a sinistra è 0,0. Ricorda anche di aggiungere codice per fermare il comportamento predefinito.

    suggerimento: crea la tua funzione onKeyDown e collegala alla finestra:

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

    Controlla la console del tuo browser a questo punto e osserva i tasti premuti che vengono registrati.

  3. Implementa il modello Pub-Sub, questo manterrà il tuo codice pulito mentre segui le parti rimanenti.

    Per fare quest'ultima parte, puoi:

    1. Aggiungere un listener di eventi alla finestra:

       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. Creare una classe EventEmitter per pubblicare e sottoscrivere messaggi:

      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. Aggiungere costanti e configurare 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. Inizializzare il gioco

    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. Configura il ciclo del gioco

    Rifattorizza la funzione window.onload per inizializzare il gioco e configurare un ciclo del gioco con un buon intervallo. Aggiungerai anche un raggio 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. Aggiungi codice per muovere i nemici a un certo intervallo.

    Rifattorizza la funzione createEnemies() per creare i nemici e aggiungerli alla nuova 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);
        }
      }
    }
    

    e aggiungi una funzione createHero() per fare un processo simile per l'eroe.

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

    infine, aggiungi una funzione drawGameObjects() per iniziare il disegno:

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

    I tuoi nemici dovrebbero iniziare ad avanzare verso la tua navicella spaziale!


🚀 Sfida

Come puoi vedere, il tuo codice può trasformarsi in un "codice spaghetti" quando inizi ad aggiungere funzioni, variabili e classi. Come puoi organizzare meglio il tuo codice in modo che sia più leggibile? Disegna un sistema per organizzare il tuo codice, anche se risiede ancora in un unico file.

Quiz Post-Lezione

Quiz post-lezione

Revisione e Studio Autonomo

Anche se stiamo scrivendo il nostro gioco senza utilizzare framework, esistono molti framework basati su JavaScript per lo sviluppo di giochi con canvas. Dedica del tempo a fare ricerche su questi.

Compito

Commenta il tuo codice


Disclaimer:
Questo documento è stato tradotto utilizzando il servizio di traduzione automatica Co-op Translator. Sebbene ci impegniamo per garantire l'accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa deve essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale eseguita da un traduttore umano. Non siamo responsabili per eventuali fraintendimenti o interpretazioni errate derivanti dall'uso di questa traduzione.