
GUIDE PER ASPIRANTI PROGRAMMATORI
JavaScript tutorial: la manipolazione del dom da principiante ad esperto


Vuoi avviare una nuova carriera o fare un upgrade?
Trova il corso Digital & Tech più adatto a te nel nostro catalogo!
- 4.1 - Configurazione iniziale
- 4.2 - Fase 1: creiamo la base del gioco
- 4.3 - Fase 2: aggiungiamo i proiettili!
- 4.4 - Fase 3: creiamo gli invaders!
- 4.5 - Fase 4: gestiamo i proiettili nemici
- 4.6 - Fase 5: implementiamo il sistema di punteggio e gli effetti grafici
- 4.7 - Fase 6: diamo gli ultimi ritocchi! Le chicche per perfezionare il gioco
1
Introduzione alla manipolazione del DOM
1.1
Cos'è il DOM?
Il DOM (Document Object Model) è un concetto fondamentale nel web development che consente di creare pagine web interattive e dinamiche.
Si tratta di una rappresentazione della struttura di un documento HTML o XML. Quando un browser carica una pagina web, esso crea automaticamente un modello del documento, noto come DOM, che rappresenta ogni parte del documento come un oggetto.
Spieghiamoci meglio. Immagina il DOM come un albero con rami e foglie. Ogni elemento come paragrafi, immagini, titoli e link è un "nodo" che un linguaggio di scripting potrà usare per accedere e modificare il contenuto e la struttura della pagina.
Più nel dettaglio, il nostro albero sarà così composto:
Nodo radice: all'apice dell'albero c'è il nodo radice, rappresentato dall'elemento <html> del documento HTML.
Nodi figli: ogni elemento HTML, come <head>, <body>, <div>, <p>, ecc., diventa un nodo figlio all'interno di questa struttura ad albero. Questi elementi sono organizzati gerarchicamente in base alla loro posizione all'interno del codice HTML.
Foglie: gli elementi senza figli, come il testo o gli attributi, sono i nodi foglia dell'albero.
Come già detto, ogni elemento, attributo e porzione di testo del documento rappresenterà un nodo nel DOM.
Come funziona il DOM?
Il DOM viene generato dal browser quando carica e interpreta un documento HTML o XML. Questo processo è chiamato parsing. Una volta creato il DOM, esso diventa un'interfaccia di programmazione che può essere manipolata dai linguaggi di scripting, come il linguaggio JavaScript, per rendere le tue pagine web interattive e accattivanti.
1.2
Perché manipolare il DOM con JavaScript?
Il DOM è fondamentale perché è ciò che permette di rendere le pagine web interattive e dinamiche. Con il linguaggio JavaScript, possiamo interagire con il DOM in modo fluido e immediato, offrendo agli utenti esperienze web coinvolgenti. JavaScript è il linguaggio di scripting nativo dei browser web, il che significa che può interagire con il DOM senza necessità di installare plug-in o software aggiuntivo. Questa integrazione diretta tra JavaScript e il DOM rende possibile aggiornare e manipolare in tempo reale la struttura e il contenuto di una pagina web.
Ecco come JavaScript ci permette di lavorare con il DOM:
Modificare il contenuto: aggiungere, rimuovere o modificare elementi e testo nella pagina. Ad esempio, possiamo cambiare il contenuto di un paragrafo, aggiornare l'immagine mostrata o alterare i link.
Cambiare stili: modificare dinamicamente gli stili CSS di un elemento. Ad esempio, possiamo cambiare il colore di un pulsante quando viene cliccato o nascondere un elemento.
Rispondere agli eventi: ascoltare eventi come clic, movimenti del mouse, input da tastiera e altro, per poi eseguire azioni in risposta. Ad esempio, possiamo creare un modulo che mostra un messaggio di ringraziamento dopo che l'utente ha inviato i propri dati.
Creare e rimuovere elementi: aggiungere nuovi elementi alla pagina o rimuovere quelli esistenti. Ad esempio, possiamo creare una lista di prodotti dinamica che cambia in base alle scelte dell'utente.
Questa interattività è ciò che trasforma una semplice pagina statica in un'applicazione web reattiva e coinvolgente. Ad esempio, possiamo aggiornare i contenuti senza dover ricaricare l'intera pagina, creare giochi online, form interattivi, e molto altro ancora.
1.3
Requisiti e conoscenze preliminari.
Prima di addentrarci nella manipolazione del DOM, è importante avere una comprensione di base dei seguenti concetti:
Linguaggio HTML: la struttura della pagina web, compresi i tag e gli attributi. Puoi rispolverare questi concetti consultando la nostra guida all’html in italiano!
Linguaggio CSS: la stilizzazione e il layout degli elementi HTML. Puoi approfondire queste nozioni consultando la nostra guida al css in italiano!
Linguaggio JavaScript: concetti fondamentali come variabili, funzioni, oggetti, e come interagire con il DOM. Non ti senti abbastanza padrone di questi argomenti? Niente paura: consulta la nostra guida Javascript in italiano!
Editor di testo: un editor come Visual Studio Code per scrivere ed eseguire il codice.
Se sei alle prime armi con uno di questi argomenti, ti consigliamo di rivedere le nozioni di base dando un’occhiata alle nostre guide avanzate, perchè questa guida presuppone una conoscenza introduttiva di HTML, CSS e JavaScript.
1.4
Struttura della guida e come seguirla.
Questa guida è strutturata per accompagnarti in un viaggio nell’esplorazione della manipolazione del DOM da livello principiante fino a livello esperto. Si compone di tre sezioni principali, ciascuna focalizzata su un progetto pratico, che potrai replicare, con difficoltà crescente:
Tutorial principiante - crea un cursorse personalizzato: inizieremo con la creazione di un cursore personalizzato. Questo progetto introduttivo ti insegnerà come selezionare e manipolare elementi del DOM, gestire eventi del mouse e aggiungere animazioni semplici utilizzando il linguaggio JavaScript.
Tutorial intermedio - crea un quiz interattivo: passeremo alla creazione di un quiz interattivo. In questa sezione imparerai a creare elementi DOM in modo dinamico, a gestire dati provenienti da un file JSON e a implementare una logica di gioco con feedback visivo basato sulle interazioni dell'utente.
Tutorial avanzato - crea Space Invaders: ti guideremo nella realizzazione del videogame Space Invaders, un classico gioco arcade. In questo progetto avanzato, utilizzerai il canvas HTML5 per creare e animare grafica complessa, gestire input della tastiera e implementare una logica di gioco completa con punteggio, livelli di difficoltà, e molto altro.
Ogni parte della guida includerà spiegazioni dettagliate e passaggi pratici per aiutarti a comprendere e applicare i concetti chiave della manipolazione del DOM.
La guida è progettata per essere seguita in sequenza, ma se hai già familiarità con i concetti base, puoi saltare direttamente ai tutorial di livello superiore. Inoltre, al termine di ogni sezione, troverai suggerimenti per estendere il progetto e mettere alla prova le tue nuove competenze.
Questa guida è pensata per essere un'esperienza pratica: mettiti comodo, preparati a sperimentare e, soprattutto, divertiti mentre impari a creare interfacce web dinamiche e coinvolgenti con JavaScript e la manipolazione del DOM! Pronto? Cominciamo!
2
Tutorial principiante: come creare un cursore personalizzato
2.1
Fase 1: creiamo la struttura HTML di base
Quante volte, visitando un sito web, hai notato che il puntatore del mouse appariva diverso dal solito? Ti è mai capitato di vedere che il cursore, seguendo il movimento del mouse, cambiasse, per esempio, la sua forma o, addirittura, si animasse al click?
Se ti è mai venuto in mente di provare a fare qualcosa di simile per il tuo sito, sei decisamente nel posto giusto!
In questo tutorial ti guideremo passo passo per farti creare un cursore personalizzato utilizzando il linguaggio HTML, il linguaggio CSS e il linguaggio JavaScript. Al termine del tutorial, avrai un cursore che seguirà i movimenti del mouse e si animerà ogni volta che l'utente clicca. Non preoccuparti se sei alle prime armi: ti spiegheremo tutto in modo davvero semplice e chiaro. Pronto? Partiamo!
Creiamo la struttura HTML di base.
Cominciamo creando un semplice file HTML che farà da base al nostro progetto.
Per iniziare, quindi, ti basterà copiare il codice a seguire; troverai, poi, la spiegazione di quanto scritto e, ricorda: se vuoi approfondire, puoi sempre consultare la nostra guida all’html e css in italiano.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cursore Personalizzato</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Cursore Personalizzato</h1>
<div class="cursor"></div>
<div class="cursor-dot"></div>
<script src="script.js"></script>
</body>
</html>
Spiegazione:
DOCTYPE e struttura HTML: iniziamo con la dichiarazione <!DOCTYPE html> che indica al browser che stiamo utilizzando HTML5. La struttura generale include il tag <html>, che racchiude tutto il contenuto del documento.
Tag <head>: qui definisci le informazioni meta del documento, il titolo della pagina (<title>), e colleghi il foglio di stile esterno (<link rel="stylesheet" href="style.css">).
Tag <body>: tutto il contenuto visibile della pagina va qui. Abbiamo un titolo (<h1>) e due div che fungeranno da cursori personalizzati. Infine, includiamo il file JavaScript (<script src="script.js"></script>).
Ora sei pronto per la fase 2, corri a scoprirla nel prossimo articolo!
2.2
Fase 2: stilizziamo il cursore con il CSS.
Una volta settata la struttura grazie al linguaggio html, passiamo a dare un po’ di stile al nostro cursore avvalendoci del linguaggio CSS. Creiamo, quindi, il file style.css per stilizzare i nostri cursori:
body {
cursor: none; /* Nasconde il cursore predefinito */
background-color: #1D1E22;
}
h1 {
color: #fff;
text-align: center;
margin-top: 50px;
}
.cursor {
width: 40px;
height: 40px;
border: 2px solid #fff;
border-radius: 50%;
position: absolute;
pointer-events: none;
transition: all 0.2s ease;
transform: translate(-50%, -50%);
}
.cursor-dot {
width: 10px;
height: 10px;
background-color: #43eeeb;
border-radius: 50%;
position: absolute;
pointer-events: none;
transition: all 0.1s ease;
transform: translate(-50%, -50%);
}
.cursor-click-effect {
width: 60px;
height: 60px;
border: 2px solid #fff;
border-radius: 50%;
position: absolute;
pointer-events: none;
transform: translate(-50%, -50%);
animation: clickEffect 0.4s ease-out forwards;
}
@keyframes clickEffect {
0% {
transform: scale(0.1);
opacity: 1;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}
Spiegazione:
body { cursor: none; }: nasconde il cursore standard del browser per permettere l'uso del cursore personalizzato.
background-color: #1D1E22;: imposta lo sfondo della pagina su un colore scuro.
h1 { color: #fff; }: diamo uno stile al titolo principale, centrando il testo e aggiungendo un margine superiore per distanziarlo dall'inizio della pagina.
.cursor e .cursor-dot: questi sono i due elementi div che funzioneranno come cursori. Li posizioniamo con “absolute” (position: absolute;), li centriamo rispetto alla posizione del mouse con transform: translate(-50%, -50%); e aggiungiamo una transizione fluida per il movimento.
.cursor-click-effect: definisce l'effetto di click. È un cerchio che si espande e svanisce, animato con @keyframes clickEffect.
A questo punto sei pronto per il terzo ed ultimo step: aggiungiamo interattività con Javascript, nel prossimo articolo!
2.3
Fase 3: aggiungiamo interattività con JavaScript
Siamo giunti allo step finale: dobbiamo far muovere i nostri cursori e attivare l'effetto di click. Come? Semplicissimo, con il linguaggio Javascript! Creiamo il file script.js:
const cursor = document.querySelector('.cursor');
const cursorDot = document.querySelector('.cursor-dot');
document.addEventListener('mousemove', e => {
const mouseX = e.clientX;
const mouseY = e.clientY;
cursorDot.style.left = `${mouseX}px`;
cursorDot.style.top = `${mouseY}px`;
cursor.style.left = `${mouseX}px`;
cursor.style.top = `${mouseY}px`;
});
document.addEventListener('click', e => {
const clickEffect = document.createElement('div');
clickEffect.className = 'cursor-click-effect';
clickEffect.style.left = `${e.clientX - 30}px`;
clickEffect.style.top = `${e.clientY - 30}px`;
document.body.appendChild(clickEffect);
setTimeout(() => {
clickEffect.remove();
}, 400); // Rimuove l'elemento dopo l'animazione
});
Spiegazione:
const cursor e const cursorDot: selezioniamo i due elementi div con le classi .cursor e .cursor-dot e li memorizziamo nelle variabili cursor e cursorDot.
document.addEventListener('mousemove', e => {...}): aggiungiamo un evento che rileva ogni movimento del mouse. La funzione anonima riceve l'evento “e” come parametro, che contiene le coordinate del mouse.
const mouseX e const mouseY: estraiamo le coordinate X e Y del mouse dal parametro “e”.
cursorDot.style.left/top: posizioniamo il cursore piccolo (cursorDot) alle coordinate X e Y del mouse.
cursor.style.left/top: posizioniamo il cursore grande (cursor) alle stesse coordinate.
document.addEventListener('click', e => {...}): aggiungiamo un evento che rileva il click del mouse.
const clickEffect = document.createElement('div');: creiamo un nuovo div per l'effetto di click.
clickEffect.className = 'cursor-click-effect';: applichiamo la classe CSS .cursor-click-effect al nuovo div.
clickEffect.style.left/top: posizioniamo l'effetto di click leggermente decentrato rispetto al punto in cui si è verificato il click (sottraendo 30px per centrarlo).
document.body.appendChild(clickEffect);: aggiungiamo l'effetto di click al body della pagina.
setTimeout(() => { clickEffect.remove(); }, 400);: dopo 400 millisecondi (il tempo dell'animazione), rimuoviamo l'elemento div dell'effetto di click dal DOM per pulire la memoria.
Procedendo con questo codice, dovresti aver ultimato la fase 3 del nostro tutorial. E adesso? Non ci resta che l'ultimo vero step, quello probabilmente più entusiasmante: la fase di test!
Prova il tuo cursore personalizzato!
È il momento di testare il tutto! Apri il tuo file HTML in un browser e inizia a muovere il mouse. Dovresti vedere il cursore personalizzato seguire i tuoi movimenti; cliccando, noterai l'animazione di espansione che, con l’utilizzo del linguaggio html, CSS e Javascript, sei stato in grado di realizzare in soli 3 step!
Questa è una fantastica aggiunta per rendere il tuo sito web più dinamico e memorabile. Sentiti libero di sperimentare con colori, dimensioni e animazioni per adattarlo al meglio al tuo progetto e divertiti a esplorare nuove possibilità e a creare qualcosa di unico!
Questo tutorial ha stuzzicato la tua voglia di imparare e sperimentare? Corri a vedere il prossimo!
3
Tutorial intermedio: come creare un quiz interattivo
3.1
Fase 1: creiamo l'HTML di base
Sei pronto a mettere le mani sul codice e creare qualcosa di davvero coinvolgente? In questo tutorial, realizzeremo insieme un quiz interattivo con il linguaggio JavaScript, perfetto per rendere il tuo sito web più stimolante e divertente.
Quante volte ti sei fermato a rispondere a delle domande per capire se "sei un vero esperto di film Marvel?". O magari stai pensando a quando hai fatto quel test per scoprire quale personaggio di "Breaking Bad" rappresenta meglio la tua personalità? Quale che sia il test che più ti ha tenuto incollato allo schermo, questo progetto getterà le basi perchè tu possa realizzare il tuo personalissimo quiz interattivo.
Attraverso la manipolazione del DOM, daremo vita a un quiz che non solo intrattiene ma offre anche un'esperienza utente memorabile. E, come sempre: non importa se sei all'inizio del tuo percorso come sviluppatore web: ti basterà seguire passo passo le fasi del tutorial che conterranno tutte le spiegazioni necessarie. E se volessi approfondire? Beh, corri a leggere le nostre guide avanzate!
Sei pronto a creare un quiz che farà impazzire tutti i tuoi amici? Mettiamoci al lavoro e trasformiamo insieme questa idea in realtà!
Creiamo l’ HTML di base.
Prima di tutto, esattamente come per il tutorial precedente, partiamo creando la struttura HTML di base. Includeremo il titolo del quiz nella navbar del nostro sito e un contenitore per le domande e i risultati.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Il Tuo Quiz</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
box-sizing: border-box;
}
.navbar {
background-color: #333;
color: white;
padding: 1em;
text-align: center;
position: fixed;
width: 100%;
top: 0;
z-index: 1000;
}
.above-the-fold {
margin-top: 60px; /* Per compensare la navbar fissa */
padding: 2em;
text-align: center;
}
.section {
padding: 2em;
text-align: center;
}
.section:nth-child(even) {
background-color: #f4f4f4;
}
.section:nth-child(odd) {
background-color: #ffffff;
}
.button {
background-color: #008CBA;
color: white;
padding: 10px 20px;
text-align: center;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border: none;
}
.result {
display: none;
padding: 2em;
text-align: center;
font-size: 1.5em;
}
</style>
</head>
<body>
<div class="navbar">
Il Tuo Quiz
</div>
<div id="quiz-container" class="section">
<!-- Le domande del quiz verranno caricate qui -->
</div>
<div id="result" class="result">
<!-- Il risultato finale verrà mostrato qui -->
</div>
<script src="script.js"></script>
</body>
</html>
Questo codice HTML è un po’ particolare, in quanto ogni elemento verrà utilizzato come contenitore per la parte dinamica proposta in JavaScript. Provando a caricarlo senza aver inserito il corretto codice js, infatti, mostrerà una pagina sostanzialmente vuota e inutilizzabile.
Presta sempre attenzione a come costruire l’HTML base e quali elementi includere già all’interno della sua struttura in base alle necessità di progetto che stai andando a sviluppare.
3.2
Fase 2: carichiamo le domande e creiamo il quiz!
Per procedere con il quiz dobbiamo passare al riflettere sul caricamento delle domande: queste saranno caricate dinamicamente da un file JSON. La struttura di questo file JSON è la seguente e la integreremo direttamente nel codice JavaScript. Se necessario, è possibile caricare queste domande in modo asincrono da un backend collegato ad un database così da creare più combinazioni di domande e risposte sempre mantenendo la stessa struttura.
[
{
"question": "Qual è la capitale della Francia?",
"choices": ["Parigi", "Londra", "Berlino", "Roma"],
"correctAnswer": 0
},
{
"question": "Qual è la formula chimica dell'acqua?",
"choices": ["H2O", "O2", "CO2", "NaCl"],
"correctAnswer": 0
},
{
"question": "Qual è il pianeta più vicino al sole?",
"choices": ["Venere", "Terra", "Mercurio", "Marte"],
"correctAnswer": 2
}
]
A questo punto implementiamo il codice JavaScript per caricare le domande dal JSON e visualizzarle nella pagina. Implementeremo, poi, anche la logica per calcolare i punteggi e visualizzare il risultato finale.
document.addEventListener('DOMContentLoaded', function() {
const quizContainer = document.getElementById('quiz-container');
const resultContainer = document.getElementById('result');
// Caricamento delle domande dal JSON
const questions = [
{
"question": "Qual è la capitale della Francia?",
"choices": ["Parigi", "Londra", "Berlino", "Roma"],
"correctAnswer": 0
},
{
"question": "Qual è la formula chimica dell'acqua?",
"choices": ["H2O", "O2", "CO2", "NaCl"],
"correctAnswer": 0
},
{
"question": "Qual è il pianeta più vicino al sole?",
"choices": ["Venere", "Terra", "Mercurio", "Marte"],
"correctAnswer": 2
}
];
// Funzione per creare le domande del quiz
function createQuiz() {
questions.forEach((question, index) => {
const section = document.createElement('div');
section.className = 'section';
const title = document.createElement('h2');
title.textContent = `Domanda ${index + 1}: ${question.question}`;
section.appendChild(title);
question.choices.forEach((choice, choiceIndex) => {
const label = document.createElement('label');
const input = document.createElement('input');
input.type = 'radio';
input.name = `question${index}`;
input.value = choiceIndex;
label.appendChild(input);
label.appendChild(document.createTextNode(choice));
section.appendChild(label);
section.appendChild(document.createElement('br'));
});
quizContainer.appendChild(section);
});
const submitButton = document.createElement('button');
submitButton.className = 'button';
submitButton.textContent = 'Invia';
submitButton.addEventListener('click', calculateResults);
quizContainer.appendChild(submitButton);
}
// Funzione per calcolare i risultati del quiz
function calculateResults() {
let score = 0;
questions.forEach((question, index) => {
const selectedOption = document.querySelector(`input[name="question${index}"]:checked`);
if (selectedOption && parseInt(selectedOption.value) === question.correctAnswer) {
score++;
}
});
displayResults(score);
}
// Funzione per visualizzare i risultati
function displayResults(score) {
quizContainer.style.display = 'none';
const totalQuestions = questions.length;
resultContainer.textContent = `Hai risposto correttamente a ${score} su ${totalQuestions} domande.`;
resultContainer.style.display = 'block';
if (score === totalQuestions) {
document.body.style.backgroundColor = 'lightgreen';
} else {
document.body.style.backgroundColor = 'lightcoral';
}
const resetButton = document.createElement('button');
resetButton.className = 'button';
resetButton.textContent = 'Ricomincia';
resetButton.addEventListener('click', resetQuiz);
resultContainer.appendChild(resetButton);
}
// Funzione per resettare il quiz
function resetQuiz() {
quizContainer.style.display = 'block';
resultContainer.style.display = 'none';
document.body.style.backgroundColor = '';
const selectedOptions = document.querySelectorAll('input[type="radio"]:checked');
selectedOptions.forEach(option => {
option.checked = false;
});
resultContainer.innerHTML = '';
}
// Creazione del quiz all'avvio
createQuiz();
});
A questo punto, il tuo quiz è pronto! Sei disorientato? Non preoccuparti, nell'articolo a seguire analizzeremo nel dettaglio tutto ciò che è stato fatto.
3.3
Analisi del codice JavaScript del quiz
Il codice JavaScript che abbiamo sviluppato per creare il quiz interattivo è il cuore del nostro progetto. Ma leggere e copiare degli snippet di codice non basta per farti assimilare le logiche alle spalle del funzionamento del quiz. Niente paura! In questa sezione andremo ad esaminare ogni parte del codice nel dettaglio, spiegando le scelte fatte e il funzionamento di ciascun componente. Cominciamo!
Caricamento del DOM
Per prima cosa, utilizziamo l'evento DOMContentLoaded per garantire che il DOM sia completamente caricato prima di eseguire qualsiasi manipolazione.
document.addEventListener('DOMContentLoaded', function() {
// Il resto del codice verrà eseguito qui
});
document.addEventListener('DOMContentLoaded', function() {...}) ricordiamo che aggiunge un listener per l'evento DOMContentLoaded che esegue il codice all'interno della funzione solo quando il DOM è completamente caricato. Questo garantisce che tutti gli elementi HTML necessari siano disponibili per la manipolazione.
Definizione delle domande del quiz.
Definiamo le domande del quiz direttamente nel codice. In una situazione reale, queste domande potrebbero essere caricate dinamicamente da un file JSON tramite una richiesta AJAX.
const questions = [
{
"question": "Qual è la capitale della Francia?",
"choices": ["Parigi", "Londra", "Berlino", "Roma"],
"correctAnswer": 0
},
{
"question": "Qual è la formula chimica dell'acqua?",
"choices": ["H2O", "O2", "CO2", "NaCl"],
"correctAnswer": 0
},
{
"question": "Qual è il pianeta più vicino al sole?",
"choices": ["Venere", "Terra", "Mercurio", "Marte"],
"correctAnswer": 2
}
];
Ogni oggetto rappresenta una domanda con un testo (question), un array Javascript di possibili risposte (choices) e l'indice della risposta corretta (correctAnswer). Questo formato è semplice e facilmente iterabile.
Creazione del quiz.
La funzione createQuiz genera dinamicamente le domande del quiz e le aggiunge al DOM.
function createQuiz() {
questions.forEach((question, index) => {
const section = document.createElement('div');
section.className = 'section';
const title = document.createElement('h2');
title.textContent = `Domanda ${index + 1}: ${question.question}`;
section.appendChild(title);
question.choices.forEach((choice, choiceIndex) => {
const label = document.createElement('label');
const input = document.createElement('input');
input.type = 'radio';
input.name = `question${index}`;
input.value = choiceIndex;
label.appendChild(input);
label.appendChild(document.createTextNode(choice));
section.appendChild(label);
section.appendChild(document.createElement('br'));
});
quizContainer.appendChild(section);
});
const submitButton = document.createElement('button');
submitButton.className = 'button';
submitButton.textContent = 'Invia';
submitButton.addEventListener('click', calculateResults);
quizContainer.appendChild(submitButton);
}
Iterazione sulle domande: utilizziamo forEach per iterare su ogni domanda dell'array questions.
Creazione delle sezioni: per ogni domanda, creiamo un elemento div con classe section, un titolo h2 con il testo della domanda e gli elementi label e input per le scelte multiple.
Nome degli Input Radio: gli input radio per ogni domanda hanno lo stesso nome (name="question${index}"), permettendo la selezione di una sola risposta per domanda.
Pulsante di Invio: creiamo un pulsante di invio che chiama la funzione calculateResults quando viene cliccato.
Calcolo dei risultati.
La funzione calculateResults calcola il punteggio del quiz basandosi sulle risposte selezionate dall'utente.
function calculateResults() {
let score = 0;
questions.forEach((question, index) => {
const selectedOption = document.querySelector(`input[name="question${index}"]:checked`);
if (selectedOption && parseInt(selectedOption.value) === question.correctAnswer) {
score++;
}
});
displayResults(score);
}
Iterazione sulle domande: utilizziamo forEach per iterare su ogni domanda e verificare la risposta selezionata.
Selezione della risposta: querySelector seleziona l'input radio selezionato per ogni domanda. Se esiste (selectedOption) e il suo valore corrisponde all'indice della risposta corretta (question.correctAnswer), incrementiamo il punteggio.
Visualizzazione dei risultati.
La funzione displayResults mostra il punteggio finale all'utente e cambia il colore di sfondo in base al punteggio.
function displayResults(score) {
quizContainer.style.display = 'none';
const totalQuestions = questions.length;
resultContainer.textContent = `Hai risposto correttamente a ${score} su ${totalQuestions} domande.`;
resultContainer.style.display = 'block';
if (score === totalQuestions) {
document.body.style.backgroundColor = 'lightgreen';
} else {
document.body.style.backgroundColor = 'lightcoral';
}
const resetButton = document.createElement('button');
resetButton.className = 'button';
resetButton.textContent = 'Ricomincia';
resetButton.addEventListener('click', resetQuiz);
resultContainer.appendChild(resetButton);
}
Nascondere il quiz: nascondiamo il contenitore del quiz impostando display: none.
Mostrare i risultati: mostriamo il punteggio nel contenitore dei risultati e lo rendiamo visibile.
Cambiamento del colore di sfondo: Cambiamo il colore di sfondo della pagina in verde se tutte le risposte sono corrette, altrimenti in rosso.
Pulsante di Reset: creiamo un pulsante di reset che chiama la funzione resetQuiz.
Reset del quiz.
La funzione resetQuiz ripristina lo stato iniziale del quiz per poter fare un nuovo round.
function resetQuiz() {
quizContainer.style.display = 'block';
resultContainer.style.display = 'none';
document.body.style.backgroundColor = '';
const selectedOptions = document.querySelectorAll('input[type="radio"]:checked');
selectedOptions.forEach(option => {
option.checked = false;
});
resultContainer.innerHTML = '';
}
Ripristinare la visibilità del Quiz: mostriamo di nuovo il contenitore del quiz e nascondiamo il contenitore dei risultati.
Ripristinare lo sfondo: resettiamo il colore di sfondo della pagina.
Deselezionare le risposte: deselezioniamo tutte le risposte selezionate impostando checked a false per ogni input radio selezionato.
Pulizia del contenitore dei risultati: svuotiamo il contenuto del contenitore dei risultati.
Con questo tutorial hai a disposizione le basi per creare il tuo quiz interattivo. La cosa migliore? Puoi personalizzarlo sulla base degli argomenti e degli stili che preferisci. La pratica costante e l'esplorazione di nuove idee ti aiuteranno a perfezionare le tue abilità di web developer e a creare progetti sempre più innovativi e interessanti. Continua a sperimentare!
Non sei ancora sazio? Corri a provare il tutorial successivo!
4
Tutorial avanzato: come creare Space Invaders
4.1
Configurazione iniziale
Anche se non sappiamo esattamente quanti anni hai, siamo pronti a scommettere che conosci sicuramente Space Invaders! Questo gioco iconico è uno dei più grandi classici degli anni '80 e ha fatto la storia dei videogiochi.
L’idea è semplice: una navicella spaziale, controllata da te, deve respingere ondate di alieni che scendono lentamente dall’alto dello schermo, cercando di evitare i loro attacchi. Più alieni elimini, più il gioco diventa impegnativo e veloce, mettendo alla prova i tuoi riflessi!
Se hai sempre desiderato creare il tuo videogioco, questa è la tua occasione. In questo tutorial, ti guideremo passo dopo passo nella creazione di una tua versione di questo classico arcade, imparando a usare il linguaggio JavaScript e il canvas di HTML5 per animare gli elementi e costruire un’esperienza di gioco completa. Pronto per quest’avventura spaziale?
Panoramica delle tecnologie utilizzate
Per creare il nostro gioco useremo tre ingredienti principali:
Linguaggio HTML: la struttura del gioco, come se fosse lo scheletro.
Linguaggio CSS: lo stile del gioco, i colori e l’aspetto grafico.
Linguaggio JavaScript: la magia che darà vita alla nostra navicella e agli invasori!
Struttura del Progetto
Per partire, ti serviranno solo due file:
index.html: qui metteremo il nostro canvas (l'area di gioco) e il punteggio.
index.js: qui scriveremo tutte le regole del gioco, come muovere la navicella, sparare e gestire gli invasori.
Configurazione iniziale
Impostazione dell’ambiente di sviluppo
Prima di cominciare, assicurati di avere un editor di testo (come Visual Studio Code) e un browser (tipo Chrome o Firefox). Crea una cartella sul tuo computer e chiamala come preferisci, sarà la base per il nostro progetto.
Creazione della struttura dei file
Dentro la tua cartella, crea due file: index.html e index.js. Questi due file saranno il cuore del nostro gioco. Non preoccuparti, ti guideremo passo passo su cosa metterci dentro!
Introduzione al file index.html
A questo punto, apri il file index.html e copia questo codice html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Invader</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
background-color: black;
}
p {
position: absolute;
z-index: 10;
color: white;
margin: 0;
top: 1rem;
left: 1rem;
font-family: sans-serif;
font-size: larger;
}
#ending {
font-size: 15rem;
color: red;
font-family: sans-serif;
margin-top: 5rem;
display: none;
}
</style>
</head>
<body>
<div style="position: relative;">
<p><span>Score:</span> <span id="scoreElement">0</span></p>
<div id="ending">GAME OVER</div>
<canvas></canvas>
</div>
<script src="./index.js"></script>
</body>
</html>
Cosa fa questo codice?
Pensa a questo file come alla cornice del nostro gioco:
Pagina e stile (<html> e <style>): la struttura della pagina è nera come lo spazio. Abbiamo un posto per il punteggio e un grande messaggio rosso che dice "GAME OVER" quando il gioco finisce.
Canvas e JavaScript (<canvas> e <script>): canvas è dove disegneremo tutto, e il tag script collega il file JavaScript che scriveremo a breve. È qui che la vera azione avverrà!
4.2
Fase 1: creiamo la base del gioco
Impostazione del Canvas
È ora di iniziare a programmare il gioco vero e proprio! Apri index.js e inserisci questo codice:
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');
canvas.width = 1480;
canvas.height = 750;
Cosa fa questo codice?
Preparare il Canvas: document.querySelector('canvas') dice al browser di prendere il nostro canvas dal file HTML. Questo sarà lo spazio di gioco.
Context 2D: c è ciò che useremo per disegnare le nostre immagini e forme nel gioco.
Dimensioni del Canvas: impostiamo la larghezza e l'altezza del canvas, in modo che occupi una buona parte dello schermo.
Creazione della Classe Player
Adesso diamo vita al nostro protagonista: la navicella spaziale!
Aggiungi questo codice:
class Player {
constructor() {
this.velocity = {
x: 0,
y: 0
};
this.rotation = 0;
this.opacity = 1;
const image = new Image();
image.src = './img/spaceship.png';
image.onload = () => {
const scale = 0.15;
this.image = image;
this.width = image.width * scale;
this.height = image.height * scale;
this.position = {
x: canvas.width / 2 - this.width / 2,
y: canvas.height - this.height - 20
};
};
}
draw() {
c.save();
c.globalAlpha = this.opacity;
c.translate(
this.position.x + this.width / 2,
this.position.y + this.height / 2
);
c.rotate(this.rotation);
c.translate(
-this.position.x - this.width / 2,
-this.position.y - this.height / 2
);
c.drawImage(this.image, this.position.x, this.position.y, this.width, this.height);
c.restore();
}
update() {
if (this.image) {
this.draw();
this.position.x += this.velocity.x;
}
}
}
const player = new Player();
Cosa fa questo codice?
Creare il Giocatore (Player): qui stiamo definendo un modello per la nostra navicella. Ogni volta che vogliamo un nuovo giocatore, usiamo questo modello.
Velocità e Movimento: la velocity ci dice quanto velocemente si muove la navicella. All'inizio è ferma, ma presto cambierà!
Immagine della Navicella: carichiamo l'immagine della navicella e la ridimensioniamo in modo che si adatti bene allo schermo.
Disegnare la Navicella (draw): questa funzione si occupa di disegnare la navicella sullo schermo, applicando rotazioni e trasparenze se necessario.
Aggiornare la Posizione (update): questa funzione sposta la navicella ogni volta che aggiorniamo il gioco, in base alla sua velocità.
Gestione del Movimento del Giocatore
Adesso facciamo in modo che la navicella possa muoversi a sinistra e a destra usando le frecce della tastiera.
Aggiungi questo codice:
const keys = {
arrowLeft: {
pressed: false
},
arrowRight: {
pressed: false
}
};
window.addEventListener('keydown', ({ key }) => {
switch (key) {
case 'ArrowLeft':
keys.arrowLeft.pressed = true;
break;
case 'ArrowRight':
keys.arrowRight.pressed = true;
break;
}
});
window.addEventListener('keyup', ({ key }) => {
switch (key) {
case 'ArrowLeft':
keys.arrowLeft.pressed = false;
break;
case 'ArrowRight':
keys.arrowRight.pressed = false;
break;
}
});
function animate() {
requestAnimationFrame(animate);
c.fillStyle = 'black';
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
if (keys.arrowLeft.pressed && player.position.x >= 0) {
player.velocity.x = -10;
player.rotation = -0.15;
} else if (keys.arrowRight.pressed && player.position.x + player.width <= canvas.width) {
player.velocity.x = 10;
player.rotation = 0.15;
} else {
player.velocity.x = 0;
player.rotation = 0;
}
}
animate();
Cosa fa questo codice?
Tasti Premuti (keys): qui creiamo un oggetto che tiene traccia dei tasti premuti. All'inizio, nessun tasto è premuto.
Rilevare i Tasti (keydown e keyup): quando premi una freccia, diciamo al nostro gioco di muovere la navicella a sinistra o a destra. Quando rilasci il tasto, fermiamo il movimento.
Animare la Navicella (animate): questa funzione viene chiamata continuamente, e ogni volta aggiorna la posizione della navicella e la ridisegna sullo schermo. Se stai premendo una freccia, la navicella si muove; se rilasci il tasto, si ferma.
4.3
Fase 2: aggiungiamo i proiettili!
Creazione della Classe Projectile
Per far sì che la nostra navicella possa difendersi dagli alieni, dobbiamo darle la capacità di sparare proiettili. Per fare ciò, creeremo una classe Projectile, che rappresenta ogni singolo proiettile che la navicella può sparare.
Aggiungi questo codice al tuo file index.js:
class Projectile {
constructor({ position, velocity }) {
this.position = position;
this.velocity = velocity;
this.radius = 4; // La dimensione del proiettile
}
draw() {
c.beginPath(); // Inizia un nuovo disegno
c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2); // Disegna un cerchio (il proiettile)
c.fillStyle = 'red'; // Colore del proiettile
c.fill(); // Riempi il cerchio con il colore
c.closePath(); // Chiudi il disegno
}
update() {
this.draw(); // Disegna il proiettile
this.position.x += this.velocity.x; // Aggiorna la posizione del proiettile in orizzontale
this.position.y += this.velocity.y; // Aggiorna la posizione del proiettile in verticale
}
}
Cosa fa questo codice?
Creare un Proiettile (Projectile): ogni volta che la navicella spara, creiamo un nuovo proiettile usando questo modello. Il proiettile ha una posizione di partenza (da dove viene sparato) e una velocità (quanto velocemente si muove sullo schermo).
Disegnare il Proiettile (draw): questa funzione disegna un cerchio rosso sul canvas, che rappresenta il nostro proiettile.
Aggiornare la Posizione (update): questa funzione sposta il proiettile sullo schermo. Ogni volta che viene chiamata, il proiettile si sposta un po' più in alto.
Implementazione dello sparo
Ora che abbiamo i proiettili, dobbiamo fare in modo che la navicella possa effettivamente spararli quando premi la barra spaziatrice. Aggiungiamo il codice per gestire lo sparo:
Aggiungi questo codice al tuo file index.js:
const projectiles = []; // Array per tenere traccia di tutti i proiettili sparati
window.addEventListener('keydown', ({ key }) => {
if (key === ' ') { // Se premi la barra spaziatrice
projectiles.push(
new Projectile({
position: {
x: player.position.x + player.width / 2, // Posizione iniziale (centro della navicella)
y: player.position.y // Parte superiore della navicella
},
velocity: {
x: 0, // I proiettili si muovono solo verso l'alto
y: -20 // Velocità verso l'alto
}
})
);
}
});
Cosa fa questo codice?
Tenere Traccia dei Proiettili (projectiles): creiamo un array Javascript vuoto che conterrà tutti i proiettili sparati. Ogni volta che spariamo, aggiungiamo un nuovo proiettile a questo array.
Rilevare la Barra Spaziatrice (keydown): aggiungiamo un evento che rileva quando premi la barra spaziatrice. Al premere della barra spaziatrice, creiamo un nuovo proiettile e lo aggiungiamo all'array dei proiettili.
Gestione dell'Interazione con il Canvas
Ora dobbiamo fare in modo che i proiettili appaiano sullo schermo e si muovano verso l'alto. Modifica la funzione animate per aggiungere la gestione dei proiettili:
function animate() {
requestAnimationFrame(animate);
c.fillStyle = 'black';
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
projectiles.forEach((projectile, index) => {
if (projectile.position.y + projectile.radius <= 0) {
setTimeout(() => {
projectiles.splice(index, 1); // Rimuovi il proiettile dall'array se esce dallo schermo
}, 0);
} else {
projectile.update(); // Aggiorna la posizione del proiettile
}
});
if (keys.arrowLeft.pressed && player.position.x >= 0) {
player.velocity.x = -10;
player.rotation = -0.15;
} else if (keys.arrowRight.pressed && player.position.x + player.width <= canvas.width) {
player.velocity.x = 10;
player.rotation = 0.15;
} else {
player.velocity.x = 0;
player.rotation = 0;
}
}
animate();
Cosa fa questo codice?
Gestione dei Proiettili (projectiles.forEach): ora, ogni volta che chiamiamo animate, controlliamo dove si trovano tutti i proiettili sullo schermo. Se un proiettile supera la parte superiore dello schermo, lo rimuoviamo dall'array in modo che non continui a essere aggiornato.
Disegnare e Aggiornare i Proiettili (projectile.update()): per ogni proiettile, chiamiamo update, che lo sposta sullo schermo e lo ridisegna nella sua nuova posizione.
Adesso la tua navicella può sparare proiettili, e questi si muovono verso l'alto, esattamente come dovrebbero! Abbiamo fatto grandi progressi, ma il gioco non è ancora completo. Nel prossimo passo, inizieremo a lavorare sugli invaders che arriveranno dall'alto, pronti a dare del filo da torcere alla tua navicella. È il momento di dare vita agli invasori alieni, i nemici che dovrai abbattere nel nostro gioco. Vediamo come farlo nel prossimo step del tutorial!
4.4
Fase 3: creiamo gli invaders!
Creazione della Classe Invader
Per rappresentare gli invaders alieni, creeremo una nuova classe chiamata Invader. Questa classe sarà simile a quella del Player, ma con alcune differenze. Gli invadersi si muoveranno automaticamente da sinistra a destra e, occasionalmente, spareranno dei proiettili verso la tua navicella.
Aggiungi questo codice al tuo file index.js:
class Invader {
constructor({ position }) {
this.velocity = {
x: 0,
y: 0
};
const image = new Image();
image.src = './img/invader.png';
image.onload = () => {
const scale = 1;
this.image = image;
this.width = image.width * scale;
this.height = image.height * scale;
this.position = {
x: position.x,
y: position.y
};
};
}
draw() {
c.drawImage(this.image, this.position.x, this.position.y, this.width, this.height);
}
update({ velocity }) {
if (this.image) {
this.draw();
this.position.x += velocity.x;
this.position.y += velocity.y;
}
}
shoot(invaderProjectiles) {
invaderProjectiles.push(
new InvaderProjectile({
position: {
x: this.position.x + this.width / 2,
y: this.position.y + this.height
},
velocity: {
x: 0,
y: 5
}
})
);
}
}
Cosa fa questo codice?
Creare un Invader (Invader): ogni invader è un nuovo oggetto creato con questa classe. Ha una posizione iniziale e una velocità di movimento (che all'inizio è 0).
Caricare l'immagine dell'invader: carichiamo un'immagine che rappresenta l'invader, la ridimensioniamo e la posizioniamo sullo schermo.
Disegnare e Aggiornare l'Invader (draw e update): draw si occupa di disegnare l'invader sullo schermo, mentre update lo sposta in base alla sua velocità.
Sparare Proiettili (shoot): gli invaders possono sparare proiettili verso la tua navicella. Questa funzione crea un nuovo proiettile che si muove verso il basso.
Generazione degli Invaders sullo schermo
Ora che abbiamo una classe per gli invaders, dobbiamo creare un gruppo di invaders che appaiano sullo schermo e si muovano insieme. Per fare questo, creeremo una classe Grid (griglia) che gestisce un insieme di invaders.
Aggiungi questo codice al tuo file index.js:
class Grid {
constructor() {
this.position = {
x: 0,
y: 0
};
this.velocity = {
x: 3,
y: 0
};
this.invaders = [];
const columns = Math.floor(Math.random() * 10 + 5);
const rows = Math.floor(Math.random() * 5 + 2);
this.width = columns * 30;
for (let x = 0; x < columns; x++) {
for (let y = 0; y < rows; y++) {
this.invaders.push(
new Invader({
position: {
x: x * 30,
y: y * 30
}
})
);
}
}
}
update() {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.velocity.y = 0;
if (this.position.x + this.width > canvas.width || this.position.x <= 0) {
this.velocity.x = -this.velocity.x;
this.velocity.y = 30;
}
}
}
Cosa fa questo codice?
Creare una Griglia (Grid): la classe Grid crea una disposizione di invaders sullo schermo. Ogni griglia ha una posizione e una velocità.
Posizionare gli invaders (invaders): la griglia genera un numero casuale di colonne e righe di invaders. Ogni invader viene posizionato a una distanza regolare dagli altri, formando una griglia.
Muovere la Griglia (update): la griglia si muove orizzontalmente e cambia direzione quando raggiunge il bordo dello schermo. Inoltre, si sposta verso il basso ogni volta che cambia direzione.
Movimento e comportamento degli Invaders
Ora che abbiamo creato gli invaders e la griglia che li contiene, dobbiamo assicurarci che si muovano sullo schermo e interagiscano con la navicella del giocatore.
Modifica la funzione animate come segue:
const grids = [];
const invaderProjectiles = [];
function animate() {
requestAnimationFrame(animate);
c.fillStyle = 'black';
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
projectiles.forEach((projectile, index) => {
if (projectile.position.y + projectile.radius <= 0) {
setTimeout(() => {
projectiles.splice(index, 1);
}, 0);
} else {
projectile.update();
}
});
grids.forEach((grid, gridIndex) => {
grid.update();
if (frames % 100 === 0 && grid.invaders.length > 0) {
grid.invaders[Math.floor(Math.random() * grid.invaders.length)].shoot(invaderProjectiles);
}
grid.invaders.forEach((invader, i) => {
invader.update({ velocity: grid.velocity });
// Gestione delle collisioni tra proiettili del giocatore e invaders
projectiles.forEach((projectile, j) => {
if (
projectile.position.y - projectile.radius <= invader.position.y + invader.height &&
projectile.position.x + projectile.radius >= invader.position.x &&
projectile.position.x - projectile.radius <= invader.position.x + invader.width &&
projectile.position.y + projectile.radius >= invader.position.y
) {
setTimeout(() => {
const invaderFound = grid.invaders.find(invader2 => invader2 === invader);
const projectileFound = projectiles.find(projectile2 => projectile2 === projectile);
if (invaderFound && projectileFound) {
score += 100;
scoreElement.innerHTML = score;
createParticles({
object: invader,
fades: true
});
grid.invaders.splice(i, 1);
projectiles.splice(j, 1);
if (grid.invaders.length > 0) {
const firstInvader = grid.invaders[0];
const lastInvader = grid.invaders[grid.invaders.length - 1];
grid.width = lastInvader.position.x - firstInvader.position.x + lastInvader.width;
grid.position.x = firstInvader.position.x;
} else {
grids.splice(gridIndex, 1);
}
}
}, 0);
}
});
});
});
invaderProjectiles.forEach((invaderProjectile, idx) => {
if (invaderProjectile.position.y + invaderProjectile.height >= canvas.height) {
setTimeout(() => {
invaderProjectiles.splice(idx, 1);
}, 0);
} else {
invaderProjectile.update();
}
});
if (keys.arrowLeft.pressed && player.position.x >= 0) {
player.velocity.x = -10;
player.rotation = -0.15;
} else if (keys.arrowRight.pressed && player.position.x + player.width <= canvas.width) {
player.velocity.x = 10;
player.rotation = 0.15;
} else {
player.velocity.x = 0;
player.rotation = 0;
}
if (frames % randomInterval === 0) {
grids.push(new Grid());
randomInterval = Math.floor(Math.random() * 500 + 500);
frames = 0;
}
frames++;
}
animate();
Cosa fa questo codice?
Aggiungere griglie di invaders (grids): ogni tanto, una nuova griglia di invaders viene aggiunta al gioco, creando nuovi nemici.
Muovere e sparare: gli invaders si muovono e sparano proiettili verso la tua navicella. Se i proiettili degli invaders colpiscono il bordo inferiore dello schermo, vengono rimossi.
Gestire le collisioni: se un proiettile della tua navicella colpisce un invader, sia il proiettile che l'invader vengono rimossi dal gioco, e il punteggio aumenta. Viene anche creato un effetto visivo per simulare l'esplosione dell'invader.
Aggiornare il gioco di continuo (animate): questa funzione fa sì che il gioco continui a funzionare, aggiornando costantemente la posizione di tutti gli elementi e verificando le interazioni tra di essi.
Ora hai una flotta di invaders che si muovono sullo schermo, sparano alla tua navicella e possono essere abbattuti dai tuoi proiettili! Il gioco sta prendendo forma, ma ci sono ancora alcune cose da sistemare, come i proiettili degli invaders e la gestione della fine del gioco.
Manca poco al termine del nostro tutorial!
4.5
Fase 4: gestiamo i proiettili nemici
Creazione della Classe InvaderProjectile
Per far sì che gli invaders possano davvero attaccare la tua navicella, dobbiamo creare dei proiettili che gli invaders possano sparare verso il basso. Questa volta useremo una classe simile a quella dei proiettili del giocatore, ma con alcune differenze.
Aggiungi questo codice al tuo file index.js:
class InvaderProjectile {
constructor({ position, velocity }) {
this.position = position;
this.velocity = velocity;
this.width = 3; // Larghezza del proiettile
this.height = 10; // Altezza del proiettile
}
draw() {
c.fillStyle = 'white'; // Colore del proiettile nemico
c.fillRect(this.position.x, this.position.y, this.width, this.height); // Disegna il proiettile come un rettangolo
}
update() {
this.draw(); // Disegna il proiettile
this.position.x += this.velocity.x; // Sposta il proiettile in orizzontale (anche se non lo facciamo per ora)
this.position.y += this.velocity.y; // Sposta il proiettile verso il basso
}
}
Cosa fa questo codice?
Creare un proiettile nemico (InvaderProjectile): questa classe rappresenta i proiettili sparati dagli invaders. Ogni proiettile ha una posizione iniziale e una velocità.
Disegnare il oroiettile (draw): il proiettile viene disegnato come un piccolo rettangolo bianco sul canvas.
Aggiornare la posizione (update): la funzione update sposta il proiettile verso il basso, simulando il tiro degli invaders.
Permettere ai nemici di sparare
Ora che abbiamo una classe per i proiettili nemici, dobbiamo collegarla alla nostra griglia di invaders in modo che possano sparare. Questo è stato in parte fatto nella sezione precedente, ma ora andremo a completare il tutto.
Aggiornamento della Funzione animate
Assicuriamoci che i proiettili nemici siano generati e gestiti correttamente durante il gioco. Modifichiamo la funzione animate per gestire questo aspetto:
function animate() {
requestAnimationFrame(animate);
c.fillStyle = 'black';
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
projectiles.forEach((projectile, index) => {
if (projectile.position.y + projectile.radius <= 0) {
setTimeout(() => {
projectiles.splice(index, 1);
}, 0);
} else {
projectile.update();
}
});
invaderProjectiles.forEach((invaderProjectile, idx) => {
if (invaderProjectile.position.y + invaderProjectile.height >= canvas.height) {
setTimeout(() => {
invaderProjectiles.splice(idx, 1);
}, 0);
} else {
invaderProjectile.update();
}
// Verifica se il proiettile colpisce il giocatore
if (
invaderProjectile.position.y + invaderProjectile.height >= player.position.y &&
invaderProjectile.position.x + invaderProjectile.width >= player.position.x &&
invaderProjectile.position.x <= player.position.x + player.width
) {
setTimeout(() => {
invaderProjectiles.splice(idx, 1);
player.opacity = 0;
game.over = true;
}, 0);
setTimeout(() => {
game.active = false;
ending.style.display = 'block'; // Mostra il messaggio di Game Over
}, 2000);
createParticles({
object: player,
color: 'white',
fades: true
});
}
});
grids.forEach((grid, gridIndex) => {
grid.update();
if (frames % 100 === 0 && grid.invaders.length > 0) {
grid.invaders[Math.floor(Math.random() * grid.invaders.length)].shoot(invaderProjectiles);
}
grid.invaders.forEach((invader, i) => {
invader.update({ velocity: grid.velocity });
projectiles.forEach((projectile, j) => {
if (
projectile.position.y - projectile.radius <= invader.position.y + invader.height &&
projectile.position.x + projectile.radius >= invader.position.x &&
projectile.position.x - projectile.radius <= invader.position.x + invader.width &&
projectile.position.y + projectile.radius >= invader.position.y
) {
setTimeout(() => {
const invaderFound = grid.invaders.find(invader2 => invader2 === invader);
const projectileFound = projectiles.find(projectile2 => projectile2 === projectile);
if (invaderFound && projectileFound) {
score += 100;
scoreElement.innerHTML = score;
createParticles({
object: invader,
fades: true
});
grid.invaders.splice(i, 1);
projectiles.splice(j, 1);
if (grid.invaders.length > 0) {
const firstInvader = grid.invaders[0];
const lastInvader = grid.invaders[grid.invaders.length - 1];
grid.width = lastInvader.position.x - firstInvader.position.x + lastInvader.width;
grid.position.x = firstInvader.position.x;
} else {
grids.splice(gridIndex, 1);
}
}
}, 0);
}
});
});
});
if (keys.arrowLeft.pressed && player.position.x >= 0) {
player.velocity.x = -10;
player.rotation = -0.15;
} else if (keys.arrowRight.pressed && player.position.x + player.width <= canvas.width) {
player.velocity.x = 10;
player.rotation = 0.15;
} else {
player.velocity.x = 0;
player.rotation = 0;
}
if (frames % randomInterval === 0) {
grids.push(new Grid());
randomInterval = Math.floor(Math.random() * 500 + 500);
frames = 0;
}
frames++;
}
animate();
Cosa fa questo codice?
Gestione dei proiettili nemici (invaderProjectiles.forEach): ogni proiettile sparato dagli invaders viene controllato e aggiornato. Se un proiettile raggiunge il bordo inferiore dello schermo, viene rimosso dall'array.
Collisione con il giocatore: se un proiettile nemico colpisce la navicella del giocatore, l'opacità della navicella viene impostata a 0 (la navicella scompare), e il gioco finisce. Dopo un breve ritardo, viene visualizzato il messaggio "GAME OVER".
Sparare proiettili dagli invaders: ogni tanto, un invader casuale tra quelli presenti nella griglia spara un proiettile verso il giocatore.
4.6
Fase 5: implementiamo il sistema di punteggio e gli effetti grafici
Creazione del sistema di punteggio
Per rendere il gioco più coinvolgente, aggiungiamo un sistema di punteggio che aumenta ogni volta che un invader viene abbattuto. Il punteggio è già collegato nel codice che abbiamo scritto in precedenza, ma vediamo esattamente come funziona.
Il punteggio viene aggiornato ogni volta che un proiettile del giocatore colpisce un invader. Ogni volta che accade, il punteggio aumenta di 100 punti. Questo valore viene visualizzato sullo schermo attraverso l'elemento HTML con l'ID scoreElement.
Introduzione delle particelle per le esplosioni al colpo
Per dare al gioco una grafica più accattivante, aggiungiamo un effetto di esplosione sotto forma di particelle ogni volta che un invader viene abbattuto. Le particelle simuleranno i detriti dell'esplosione.
Aggiungi questo codice al tuo file index.js:
class Particle {
constructor({ position, velocity, radius, color, fades }) {
this.position = position;
this.velocity = velocity;
this.radius = radius;
this.color = color;
this.opacity = 1;
this.fades = fades;
}
draw() {
c.save();
c.globalAlpha = this.opacity;
c.beginPath();
c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;
c.fill();
c.closePath();
c.restore();
}
update() {
this.draw();
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
if (this.fades) this.opacity -= 0.01;
}
}
const particles = [];
function createParticles({ object, color, fades }) {
for (let i = 0; i < 15; i++) {
particles.push(
new Particle({
position: {
x: object.position.x + object.width / 2,
y: object.position.y + object.height / 2,
},
velocity: {
x: (Math.random() - 0.5) * 2,
y: (Math.random() - 0.5) * 2,
},
radius: Math.random() * 3,
color: color || '#baa0de',
fades,
})
);
}
}
Cosa fa questo codice?
Creare una particella (Particle): ogni particella è rappresentata da un piccolo cerchio che ha una posizione, una velocità, un raggio (dimensione), un colore e un'opacità.
Disegnare la particella (draw): questa funzione disegna la particella sul canvas. Se fades è vero, l'opacità della particella diminuisce nel tempo, facendola svanire.
Aggiornare la posizione (update): la particella si muove in base alla sua velocità. Se l'opacità della particella è inferiore a un certo valore, la particella svanisce gradualmente.
Creare un esplosione di particelle (createParticles): questa funzione crea un gruppo di particelle ogni volta che viene chiamata, simulando un'esplosione.
Integrazione delle particelle nella Funzione animate
Per far sì che le particelle appaiano quando un invader viene abbattuto, dobbiamo integrare le particelle nella funzione animate.
Modifica la funzione animate in questo modo:
requestAnimationFrame(animate);
c.fillStyle = 'black';
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
particles.forEach((particle, i) => {
if (particle.opacity <= 0) {
setTimeout(() => {
particles.splice(i, 1);
}, 0);
} else {
particle.update();
}
});
projectiles.forEach((projectile, index) => {
if (projectile.position.y + projectile.radius <= 0) {
setTimeout(() => {
projectiles.splice(index, 1);
}, 0);
} else {
projectile.update();
}
});
invaderProjectiles.forEach((invaderProjectile, idx) => {
if (invaderProjectile.position.y + invaderProjectile.height >= canvas.height) {
setTimeout(() => {
invaderProjectiles.splice(idx, 1);
}, 0);
} else {
invaderProjectile.update();
}
if (
invaderProjectile.position.y + invaderProjectile.height >= player.position.y &&
invaderProjectile.position.x + invaderProjectile.width >= player.position.x &&
invaderProjectile.position.x <= player.position.x + player.width
) {
setTimeout(() => {
invaderProjectiles.splice(idx, 1);
player.opacity = 0;
game.over = true;
}, 0);
setTimeout(() => {
game.active = false;
ending.style.display = 'block';
}, 2000);
createParticles({
object: player,
color: 'white',
fades: true,
});
}
});
grids.forEach((grid, gridIndex) => {
grid.update();
if (frames % 100 === 0 && grid.invaders.length > 0) {
grid.invaders[Math.floor(Math.random() * grid.invaders.length)].shoot(invaderProjectiles);
}
grid.invaders.forEach((invader, i) => {
invader.update({ velocity: grid.velocity });
projectiles.forEach((projectile, j) => {
if (
projectile.position.y - projectile.radius <= invader.position.y + invader.height &&
projectile.position.x + projectile.radius >= invader.position.x &&
projectile.position.x - projectile.radius <= invader.position.x + invader.width &&
projectile.position.y + projectile.radius >= invader.position.y
) {
setTimeout(() => {
const invaderFound = grid.invaders.find(invader2 => invader2 === invader);
const projectileFound = projectiles.find(projectile2 => projectile2 === projectile);
if (invaderFound && projectileFound) {
score += 100;
scoreElement.innerHTML = score;
createParticles({
object: invader,
fades: true,
});
grid.invaders.splice(i, 1);
projectiles.splice(j, 1);
if (grid.invaders.length > 0) {
const firstInvader = grid.invaders[0];
const lastInvader = grid.invaders[grid.invaders.length - 1];
grid.width = lastInvader.position.x - firstInvader.position.x + lastInvader.width;
grid.position.x = firstInvader.position.x;
} else {
grids.splice(gridIndex, 1);
}
}
}, 0);
}
});
});
});
if (keys.arrowLeft.pressed && player.position.x >= 0) {
player.velocity.x = -10;
player.rotation = -0.15;
} else if (keys.arrowRight.pressed && player.position.x + player.width <= canvas.width) {
player.velocity.x = 10;
player.rotation = 0.15;
} else {
player.velocity.x = 0;
player.rotation = 0;
}
if (frames % randomInterval === 0) {
grids.push(new Grid());
randomInterval = Math.floor(Math.random() * 500 + 500);
frames = 0;
}
frames++;
}
animate();
Cosa fa questo codice?
Gestione delle particelle (particles.forEach): Ogni particella viene aggiornata in base alla sua velocità. Se la sua opacità è scesa a zero, viene rimossa dall'array.
Esplosioni: Ogni volta che un invader viene abbattuto, viene creata un'esplosione di particelle. Queste particelle si muovono e svaniscono, creando un effetto visivo.
Fine del gioco: Se la navicella del giocatore viene colpita, viene generata un'esplosione di particelle bianche, e il gioco termina con il messaggio "GAME OVER".
Adesso il tuo Space Invaders è completo. Ma c’è ancora qualche dettaglio che possiamo rifinire per renderlo ancora più coinvolgente. Curioso? Corri al prossimo capitolo!
4.7
Fase 6: diamo gli ultimi ritocchi! Le chicche per perfezionare il gioco
Implementazione della logica di fine partita
Già nel passo precedente abbiamo introdotto la logica per la fine del gioco, ovvero il "Game Over" che si attiva quando la navicella del giocatore viene colpita da un proiettile nemico. Tuttavia, possiamo fare qualche piccolo miglioramento per rendere questa parte del gioco più interessante.
Possiamo, ad esempio, aggiungere un effetto sonoro quando il giocatore perde, oppure reimpostare il gioco per far sì che il giocatore possa ricominciare una nuova partita senza dover ricaricare la pagina.
Aggiungi il seguente codice al tuo file index.js:
function resetGame() {
player.opacity = 1;
player.position.x = canvas.width / 2 - player.width / 2;
invaderProjectiles.length = 0;
grids.length = 0;
particles.length = 0;
score = 0;
scoreElement.innerHTML = score;
game.active = true;
game.over = false;
ending.style.display = 'none';
animate();
}
window.addEventListener('keydown', ({ key }) => {
if (game.over && key === 'Enter') {
resetGame();
}
});
Cosa fa questo codice?
Funzione di Reset del gioco (resetGame): questa funzione ripristina lo stato del gioco per permettere al giocatore di iniziare una nuova partita. Resetta l'opacità del giocatore, la sua posizione, cancella tutti i proiettili e le griglie di invasori, azzera il punteggio, e nasconde il messaggio di "Game Over".
Ricominciare il gioco (keydown): se il gioco è finito (game.over) e il giocatore preme "Enter", il gioco viene resettato e si può iniziare una nuova partita.
Visualizzazione del messaggio di "Game Over"
Il messaggio di "Game Over" che abbiamo impostato nella struttura HTML viene visualizzato quando il giocatore perde. Tuttavia, potremmo voler migliorare questa parte aggiungendo un messaggio che spieghi come ricominciare a giocare.
Modifica il messaggio di "Game Over" nel file index.html:
<div id="ending">GAME OVER<br><span style="font-size: 2rem;">Press Enter to Restart</span></div>
Cosa fa questo codice?
Messaggio di Restart: abbiamo aggiunto una riga di testo che invita il giocatore a premere "Enter" per ricominciare il gioco. Questo rende il flusso del gioco più chiaro e user-friendly.
Ottimizzazioni finali e Debugging
Per concludere, vediamo alcune ottimizzazioni e verifiche che possiamo fare per assicurarci che il gioco funzioni al meglio:
Verifica delle collisioni: assicurati che tutte le collisioni (tra proiettili e invasori, tra proiettili nemici e il giocatore) funzionino correttamente. Testa il gioco e verifica che non ci siano situazioni in cui i proiettili attraversano gli invasori senza distruggerli, o che il giocatore non venga colpito correttamente.
Performance: se noti che il gioco diventa lento man mano che avanzano le griglie di invasori, considera di ottimizzare il codice rimuovendo elementi non più necessari dal DOM e riducendo la complessità delle animazioni.
Aggiunta di suoni: ouoi migliorare ulteriormente l'esperienza di gioco aggiungendo effetti sonori per gli spari, le esplosioni, e la fine del gioco. Puoi farlo utilizzando l'elemento <audio> di HTML5 o con librerie JavaScript come Howler.js.
Test Cross-Browser: testa il gioco su diversi browser (Chrome, Firefox, Edge) e su dispositivi mobili per assicurarti che funzioni correttamente ovunque. Alcune funzioni JavaScript potrebbero comportarsi diversamente su vari browser, quindi è importante verificare.
Complimenti! Hai portato a termine il tutorial e creato il tuo **Space Invaders** da zero, aggiungendo tutte quelle "chicche" finali che lo rendono unico. Ora sei ufficialmente un pro della manipolazione del DOM! Hai imparato a usare JavaScript per gestire elementi, eventi, contenuti dinamici e animazioni.
Continua a sperimentare e perfeziona il tuo gioco aggiungendo nuove funzionalità. E se vuoi approfondire ancora di più le tue abilità, dai un’occhiata alla nostra guida JavaScript per diventare un vero esperto!
CONTENUTI GRATUITI IN EVIDENZA
Guide per aspiranti programmatori 👨🏻🚀
Vuoi muovere i primi passi nel Digital e Tech? Abbiamo preparato alcune guide per aiutarti a orientarti negli ambiti più richiesti oggi.