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/4-collision-detection
softchris 9837770ac1
🌐 Update translations via Co-op Translator
1 month ago
..
solution 🌐 Update translations via Co-op Translator 3 months ago
your-work 🌐 Update translations via Co-op Translator 3 months ago
README.md 🌐 Update translations via Co-op Translator 1 month ago
assignment.md 🌐 Update translations via Co-op Translator 1 month ago

README.md

Costruire un Gioco Spaziale Parte 4: Aggiungere un Laser e Rilevare le Collisioni

Quiz Pre-Lettura

Quiz pre-lettura

Pensa al momento in Star Wars in cui i siluri protonici di Luke colpiscono il condotto di scarico della Morte Nera. Quella precisa rilevazione della collisione ha cambiato il destino della galassia! Nei giochi, la rilevazione delle collisioni funziona allo stesso modo: determina quando gli oggetti interagiscono e cosa accade successivamente.

In questa lezione, aggiungerai armi laser al tuo gioco spaziale e implementerai la rilevazione delle collisioni. Proprio come i pianificatori di missioni della NASA calcolano le traiettorie delle navicelle spaziali per evitare detriti, imparerai a rilevare quando gli oggetti del gioco si intersecano. Suddivideremo il processo in passaggi gestibili che si costruiscono l'uno sull'altro.

Alla fine, avrai un sistema di combattimento funzionante in cui i laser distruggono i nemici e le collisioni attivano eventi di gioco. Gli stessi principi di collisione sono utilizzati in tutto, dalle simulazioni fisiche alle interfacce web interattive.

Fai una piccola ricerca sul primo videogioco mai scritto. Qual era la sua funzionalità?

Rilevazione delle collisioni

La rilevazione delle collisioni funziona come i sensori di prossimità del modulo lunare Apollo: controlla costantemente le distanze e attiva avvisi quando gli oggetti si avvicinano troppo. Nei giochi, questo sistema determina quando gli oggetti interagiscono e cosa dovrebbe accadere successivamente.

L'approccio che utilizzeremo tratta ogni oggetto del gioco come un rettangolo, simile a come i sistemi di controllo del traffico aereo utilizzano forme geometriche semplificate per tracciare gli aeromobili. Questo metodo rettangolare potrebbe sembrare basilare, ma è efficiente dal punto di vista computazionale e funziona bene per la maggior parte degli scenari di gioco.

Rappresentazione del rettangolo

Ogni oggetto del gioco necessita di confini di coordinate, simile a come il rover Mars Pathfinder ha mappato la sua posizione sulla superficie marziana. Ecco come definiamo queste coordinate di confine:

rectFromGameObject() {
  return {
    top: this.y,
    left: this.x,
    bottom: this.y + this.height,
    right: this.x + this.width
  }
}

Analizziamo questo:

  • Bordo superiore: È semplicemente il punto in cui l'oggetto inizia verticalmente (la sua posizione y)
  • Bordo sinistro: Dove inizia orizzontalmente (la sua posizione x)
  • Bordo inferiore: Aggiungi l'altezza alla posizione y - ora sai dove finisce!
  • Bordo destro: Aggiungi la larghezza alla posizione x - e hai il confine completo

Algoritmo di intersezione

Rilevare le intersezioni tra rettangoli utilizza una logica simile a quella con cui il telescopio spaziale Hubble determina se gli oggetti celesti si sovrappongono nel suo campo visivo. L'algoritmo verifica la separazione:

function intersectRect(r1, r2) {
  return !(r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top);
}

Il test di separazione funziona come i sistemi radar:

  • Il rettangolo 2 è completamente a destra del rettangolo 1?
  • Il rettangolo 2 è completamente a sinistra del rettangolo 1?
  • Il rettangolo 2 è completamente sotto il rettangolo 1?
  • Il rettangolo 2 è completamente sopra il rettangolo 1?

Se nessuna di queste condizioni è vera, i rettangoli devono sovrapporsi. Questo approccio rispecchia il modo in cui gli operatori radar determinano se due aeromobili sono a distanze di sicurezza.

Gestione del ciclo di vita degli oggetti

Quando un laser colpisce un nemico, entrambi gli oggetti devono essere rimossi dal gioco. Tuttavia, eliminare gli oggetti durante il ciclo può causare crash - una lezione imparata duramente nei primi sistemi informatici come il Computer di Guida Apollo. Invece, utilizziamo un approccio "segna per eliminazione" che rimuove in sicurezza gli oggetti tra i frame.

Ecco come si segna qualcosa per la rimozione:

// Mark object for removal
enemy.dead = true;

Perché questo approccio funziona:

  • Segniamo l'oggetto come "morto" ma non lo eliminiamo immediatamente
  • Questo permette al frame corrente del gioco di terminare in sicurezza
  • Nessun crash derivante dal tentativo di utilizzare qualcosa che è già stato eliminato!

Poi filtriamo gli oggetti segnati prima del ciclo di rendering successivo:

gameObjects = gameObjects.filter(go => !go.dead);

Cosa fa questo filtro:

  • Crea una lista nuova con solo gli oggetti "vivi"
  • Elimina tutto ciò che è stato segnato come morto
  • Mantiene il gioco fluido
  • Previene l'accumulo di oggetti distrutti che occupano memoria inutilmente

Implementazione della meccanica del laser

I proiettili laser nei giochi funzionano sullo stesso principio dei siluri fotonici in Star Trek: sono oggetti discreti che viaggiano in linea retta fino a colpire qualcosa. Ogni pressione della barra spaziatrice crea un nuovo oggetto laser che si muove sullo schermo.

Per far funzionare tutto questo, dobbiamo coordinare alcuni elementi diversi:

Componenti chiave da implementare:

  • Creare oggetti laser che si generano dalla posizione dell'eroe
  • Gestire l'input da tastiera per attivare la creazione del laser
  • Gestire il movimento e il ciclo di vita del laser
  • Implementare una rappresentazione visiva per i proiettili laser

Implementazione del controllo della frequenza di fuoco

Frequenze di fuoco illimitate sovraccaricherebbero il motore del gioco e renderebbero il gameplay troppo facile. Anche i sistemi di armi reali affrontano vincoli simili: persino i phaser della USS Enterprise avevano bisogno di tempo per ricaricarsi tra un colpo e l'altro.

Implementeremo un sistema di cooldown che impedisce lo spam di fuoco rapido mantenendo i controlli reattivi:

class Cooldown {
  constructor(time) {
    this.cool = false;
    setTimeout(() => {
      this.cool = true;
    }, time);
  }
}

class Weapon {
  constructor() {
    this.cooldown = null;
  }
  
  fire() {
    if (!this.cooldown || this.cooldown.cool) {
      // Create laser projectile
      this.cooldown = new Cooldown(500);
    } else {
      // Weapon is still cooling down
    }
  }
}

Come funziona il cooldown:

  • Quando viene creato, l'arma inizia "calda" (non può ancora sparare)
  • Dopo il periodo di timeout, diventa "fredda" (pronta per sparare)
  • Prima di sparare, controlliamo: "L'arma è fredda?"
  • Questo impedisce lo spam di clic mantenendo i controlli reattivi

Consulta la lezione 1 della serie di giochi spaziali per ricordarti dei cooldown.

Costruire il sistema di rilevazione delle collisioni

Estenderai il codice del tuo gioco spaziale esistente per creare un sistema di rilevazione delle collisioni. Come il sistema di evitamento delle collisioni automatizzato della Stazione Spaziale Internazionale, il tuo gioco monitorerà continuamente le posizioni degli oggetti e risponderà alle intersezioni.

Partendo dal codice della lezione precedente, aggiungerai la rilevazione delle collisioni con regole specifiche che governano le interazioni tra gli oggetti.

💡 Suggerimento Pro: Lo sprite del laser è già incluso nella tua cartella delle risorse ed è referenziato nel tuo codice, pronto per essere implementato.

Regole di collisione da implementare

Meccaniche di gioco da aggiungere:

  1. Il laser colpisce il nemico: L'oggetto nemico viene distrutto quando colpito da un proiettile laser
  2. Il laser colpisce il confine dello schermo: Il laser viene rimosso quando raggiunge il bordo superiore dello schermo
  3. Collisione tra nemico ed eroe: Entrambi gli oggetti vengono distrutti quando si intersecano
  4. Il nemico raggiunge il fondo: Condizione di game over quando i nemici raggiungono il fondo dello schermo

Configurazione dell'ambiente di sviluppo

Buone notizie: abbiamo già preparato gran parte del lavoro per te! Tutte le risorse del gioco e la struttura di base sono pronte nella sottocartella your-work, pronte per aggiungere le fantastiche funzionalità di collisione.

Struttura del progetto

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

Comprendere la struttura dei file:

  • Contiene tutte le immagini degli sprite necessarie per gli oggetti del gioco
  • Include il documento HTML principale e il file dell'applicazione JavaScript
  • Fornisce la configurazione del pacchetto per il server di sviluppo locale

Avviare il server di sviluppo

Naviga nella tua cartella di progetto e avvia il server locale:

cd your-work
npm start

Questa sequenza di comandi:

  • Cambia directory nella tua cartella di lavoro del progetto
  • Avvia un server HTTP locale su http://localhost:5000
  • Serve i file del tuo gioco per test e sviluppo
  • Abilita lo sviluppo live con ricaricamento automatico

Apri il tuo browser e naviga su http://localhost:5000 per vedere lo stato attuale del tuo gioco con l'eroe e i nemici renderizzati sullo schermo.

Implementazione passo-passo

Come l'approccio sistematico utilizzato dalla NASA per programmare la navicella Voyager, implementeremo la rilevazione delle collisioni in modo metodico, costruendo ogni componente passo dopo passo.

1. Aggiungere i confini di collisione rettangolari

Per prima cosa, insegniamo agli oggetti del gioco come descrivere i loro confini. Aggiungi questo metodo alla tua classe GameObject:

rectFromGameObject() {
    return {
      top: this.y,
      left: this.x,
      bottom: this.y + this.height,
      right: this.x + this.width,
    };
  }

Questo metodo realizza:

  • Crea un oggetto rettangolare con coordinate di confine precise
  • Calcola i bordi inferiore e destro utilizzando posizione più dimensioni
  • Restituisce un oggetto pronto per gli algoritmi di rilevazione delle collisioni
  • Fornisce un'interfaccia standardizzata per tutti gli oggetti del gioco

2. Implementare la rilevazione delle intersezioni

Ora creiamo il nostro detective delle collisioni: una funzione che può dire quando due rettangoli si sovrappongono:

function intersectRect(r1, r2) {
  return !(
    r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top
  );
}

Questo algoritmo funziona:

  • Testa quattro condizioni di separazione tra rettangoli
  • Restituisce false se una qualsiasi condizione di separazione è vera
  • Indica collisione quando non esiste separazione
  • Utilizza la logica di negazione per un test di intersezione efficiente

3. Implementare il sistema di sparo del laser

Ecco dove le cose si fanno emozionanti! Configuriamo il sistema di sparo del laser.

Costanti dei messaggi

Per prima cosa, definiamo alcuni tipi di messaggi in modo che le diverse parti del nostro gioco possano comunicare tra loro:

KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",

Queste costanti forniscono:

  • Standardizzano i nomi degli eventi in tutta l'applicazione
  • Abilitano una comunicazione coerente tra i sistemi di gioco
  • Prevengono errori di battitura nella registrazione dei gestori di eventi
Gestione dell'input da tastiera

Aggiungi il rilevamento della barra spaziatrice al tuo listener di eventi di tasti:

} else if(evt.keyCode === 32) {
  eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}

Questo gestore di input:

  • Rileva le pressioni della barra spaziatrice utilizzando il keyCode 32
  • Emette un messaggio di evento standardizzato
  • Abilita una logica di sparo decentrata
Configurazione del listener di eventi

Registra il comportamento di sparo nella tua funzione initGame():

eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
 if (hero.canFire()) {
   hero.fire();
 }
});

Questo listener di eventi:

  • Risponde agli eventi della barra spaziatrice
  • Controlla lo stato del cooldown di sparo
  • Attiva la creazione del laser quando consentito

Aggiungi la gestione delle collisioni per le interazioni laser-nemico:

eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
  first.dead = true;
  second.dead = true;
});

Questo gestore di collisioni:

  • Riceve i dati dell'evento di collisione con entrambi gli oggetti
  • Segna entrambi gli oggetti per la rimozione
  • Garantisce una corretta pulizia dopo la collisione

4. Creare la classe Laser

Implementa un proiettile laser che si muove verso l'alto e gestisce il proprio ciclo di vita:

class Laser extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 9;
    this.height = 33;
    this.type = 'Laser';
    this.img = laserImg;
    
    let id = setInterval(() => {
      if (this.y > 0) {
        this.y -= 15;
      } else {
        this.dead = true;
        clearInterval(id);
      }
    }, 100);
  }
}

Questa implementazione della classe:

  • Estende GameObject per ereditare la funzionalità di base
  • Imposta dimensioni appropriate per lo sprite del laser
  • Crea un movimento automatico verso l'alto utilizzando setInterval()
  • Gestisce l'auto-distruzione quando raggiunge la parte superiore dello schermo
  • Gestisce il proprio tempo di animazione e pulizia

5. Implementare il sistema di rilevazione delle collisioni

Crea una funzione completa per la rilevazione delle collisioni:

function updateGameObjects() {
  const enemies = gameObjects.filter(go => go.type === 'Enemy');
  const lasers = gameObjects.filter(go => go.type === "Laser");
  
  // Test laser-enemy collisions
  lasers.forEach((laser) => {
    enemies.forEach((enemy) => {
      if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
        eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
          first: laser,
          second: enemy,
        });
      }
    });
  });

  // Remove destroyed objects
  gameObjects = gameObjects.filter(go => !go.dead);
}

Questo sistema di collisione:

  • Filtra gli oggetti del gioco per tipo per test più efficienti
  • Testa ogni laser contro ogni nemico per le intersezioni
  • Emette eventi di collisione quando vengono rilevate intersezioni
  • Pulisce gli oggetti distrutti dopo l'elaborazione delle collisioni

⚠️ Importante: Aggiungi updateGameObjects() al tuo ciclo principale del gioco in window.onload per abilitare la rilevazione delle collisioni.

6. Aggiungere il sistema di cooldown alla classe Hero

Migliora la classe Hero con meccaniche di sparo e limitazione della frequenza:

class Hero extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 99;
    this.height = 75;
    this.type = "Hero";
    this.speed = { x: 0, y: 0 };
    this.cooldown = 0;
  }
  
  fire() {
    gameObjects.push(new Laser(this.x + 45, this.y - 10));
    this.cooldown = 500;

    let id = setInterval(() => {
      if (this.cooldown > 0) {
        this.cooldown -= 100;
      } else {
        clearInterval(id);
      }
    }, 200);
  }
  
  canFire() {
    return this.cooldown === 0;
  }
}

Comprendere la classe Hero migliorata:

  • Inizializza il timer di cooldown a zero (pronto per sparare)
  • Crea oggetti laser posizionati sopra la nave dell'eroe
  • Imposta il periodo di cooldown per prevenire spari rapidi
  • Decrementa il timer di cooldown utilizzando aggiornamenti basati su intervalli
  • Fornisce un controllo dello stato di sparo tramite il metodo canFire()

Testare la tua implementazione

Il tuo gioco spaziale ora presenta un sistema completo di rilevazione delle collisioni e meccaniche di combattimento. 🚀 Testa queste nuove funzionalità:

  • Naviga con i tasti freccia per verificare i controlli di movimento
  • Spara laser con la barra spaziatrice - nota come il cooldown impedisce lo spam di clic
  • Osserva le collisioni quando i laser colpiscono i nemici, attivando la rimozione
  • Verifica la pulizia mentre gli oggetti distrutti scompaiono dal gioco

Hai implementato con successo un sistema di rilevazione delle collisioni utilizzando gli stessi principi matematici che guidano la navigazione spaziale e la robotica.

Sfida GitHub Copilot Agent 🚀

Usa la modalità Agent per completare la seguente sfida:

Descrizione: Migliora il sistema di rilevazione delle collisioni implementando potenziamenti che si generano casualmente e forniscono abilità temporanee quando raccolti dalla nave dell'eroe.

Prompt: Crea una classe PowerUp che estende GameObject e implementa la rilevazione delle collisioni tra l'eroe e i potenziamenti. Aggiungi almeno due tipi di potenziamenti: uno che aumenta la velocità di fuoco (riduce il cooldown) e un altro che crea uno scudo temporaneo. Includi una logica di generazione che crea potenziamenti a intervalli e posizioni casuali.


🚀 Sfida

Aggiungi un'esplosione! Dai un'occhiata alle risorse del gioco nel repository Space Art e prova ad aggiungere un'esplosione quando il laser colpisce un alieno.

Quiz Post-Lettura

Quiz post-lettura

Revisione e Studio Autonomo

Sperimenta con gli intervalli nel tuo gioco fino ad ora. Cosa succede quando li cambi? Leggi di più sugli eventi di temporizzazione JavaScript.

Compito

Esplora le collisioni


Disclaimer:
Questo documento è stato tradotto utilizzando il servizio di traduzione AI 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 dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale umana. Non siamo responsabili per eventuali incomprensioni o interpretazioni errate derivanti dall'uso di questa traduzione.