parent
4d8441886e
commit
3e2a36a3ab
@ -0,0 +1,190 @@
|
||||
# Costruire un Gioco Spaziale parte 5: Punteggio e Vite
|
||||
|
||||
## Quiz Pre-Lezione
|
||||
|
||||
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/37)
|
||||
|
||||
In questa lezione si imparerà come aggiungere punteggi a una partita e calcolare le vite.
|
||||
|
||||
## Disegnare testo sullo schermo
|
||||
|
||||
Per poter visualizzare il punteggio di una partita sullo schermo, serve sapere come posizionare il testo sullo schermo. La risposta è usando il metodo `fillText()` sull'oggetto canvas. Si possono anche controllare altri aspetti come il tipo di carattere da usare, il colore del testo e anche il suo allineamento (sinistra, destra, centro). Di seguito è riportato del codice che disegna testo sullo schermo.
|
||||
|
||||
```javascript
|
||||
ctx.font = "30px Arial";
|
||||
ctx.fillStyle = "red";
|
||||
ctx.textAlign = "right";
|
||||
ctx.fillText("show this on the screen", 0, 0);
|
||||
```
|
||||
|
||||
✅ Si legga di più su [come aggiungere testo a un oggetto canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text), ci si senta liberi di far sembrare il proprio più elaborato!
|
||||
|
||||
## La vita, come concetto di gioco
|
||||
|
||||
Il concetto di avere una vita in un gioco è solo un numero. Nel contesto di un gioco spaziale è comune assegnare una serie di vite che vengono detratte una per una quando la propria astronave subisce danni. Sarebbe bello se si potesse mostrare una rappresentazione grafica di questo come mini astronavi o cuori invece di un numero.
|
||||
|
||||
## Cosa costruire
|
||||
|
||||
Si aggiunge quanto segue al gioco:
|
||||
|
||||
- **Punteggio del gioco**: per ogni astronave nemica che viene distrutta, l'eroe dovrebbe ricevere alcuni punti, si suggerisce 100 punti per astronave. Il punteggio del gioco dovrebbe essere mostrato in basso a sinistra.
|
||||
- **Vita**: la propria astronave ha tre vite. Si perde una vita ogni volta che una astronave nemica si scontra con la propria. Un punteggio di vita dovrebbe essere visualizzato in basso a destra ed essere ricavato dalla seguente immagine ![immagine grafica di una vita](../solution/assets/life.png).
|
||||
|
||||
## Passaggi consigliati
|
||||
|
||||
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
|
||||
|
||||
```bash
|
||||
-| assets
|
||||
-| enemyShip.png
|
||||
-| player.png
|
||||
-| laserRed.png
|
||||
-| index.html
|
||||
-| app.js
|
||||
-| package.json
|
||||
```
|
||||
|
||||
Si fa partire il progetto dalla cartella `your_work` digitando:
|
||||
|
||||
```bash
|
||||
cd your-work
|
||||
npm start
|
||||
```
|
||||
|
||||
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici, e premendo le frecce sinistra e destra, si fa muovere l'eroe che può abbattere i nemici.
|
||||
|
||||
### Aggiungere codice
|
||||
|
||||
1. **Copiare le risorse necessarie** dalla cartella `solution/assets` nella cartella `your-work`; aggiungere una risorsa `life.png`. Aggiungere lifeImg alla funzione window.onload:
|
||||
|
||||
```javascript
|
||||
lifeImg = await loadTexture("assets/life.png");
|
||||
```
|
||||
|
||||
1. Aggiungere `lifeImg` all'elenco delle risorse:
|
||||
|
||||
```javascript
|
||||
let heroImg,
|
||||
...
|
||||
lifeImg,
|
||||
...
|
||||
eventEmitter = new EventEmitter();
|
||||
```
|
||||
|
||||
2. **Aggiungere variabili**. Aggiungere il codice che rappresenta il punteggio totale (0) e le vite rimaste (3), visualizzare questi punteggi sullo schermo.
|
||||
|
||||
3. **Estendere la funzione `updateGameObjects()`** . Estendere la funzione `updateGameObjects()` per gestire le collisioni con il nemico:
|
||||
|
||||
```javascript
|
||||
enemies.forEach(enemy => {
|
||||
const heroRect = hero.rectFromGameObject();
|
||||
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
|
||||
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
4. **Aggiungere vita (`life`) e punti (`points`)**.
|
||||
1. **Inizializzare le variabili**. Sotto `this.cooldown = 0` nella classe `Hero`, impostare la vita e i punti:
|
||||
|
||||
```javascript
|
||||
this.life = 3;
|
||||
this.points = 0;
|
||||
```
|
||||
|
||||
1. **Disegnare variabili sullo schermo**. Disegnare questi valori sullo schermo:
|
||||
|
||||
```javascript
|
||||
function drawLife() {
|
||||
// TODO, 35, 27
|
||||
const START_POS = canvas.width - 180;
|
||||
for(let i=0; i < hero.life; i++ ) {
|
||||
ctx.drawImage(
|
||||
lifeImg,
|
||||
START_POS + (45 * (i+1) ),
|
||||
canvas.height - 37);
|
||||
}
|
||||
}
|
||||
|
||||
function drawPoints() {
|
||||
ctx.font = "30px Arial";
|
||||
ctx.fillStyle = "red";
|
||||
ctx.textAlign = "left";
|
||||
drawText("Points: " + hero.points, 10, canvas.height-20);
|
||||
}
|
||||
|
||||
function drawText(message, x, y) {
|
||||
ctx.fillText(message, x, y);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
1. **Aggiungere metodi al ciclo di gioco**. Assicurarsi di aggiungere queste funzioni alla funzione window.onload sotto `updateGameObjects()`:
|
||||
|
||||
```javascript
|
||||
drawPoints();
|
||||
drawLife();
|
||||
```
|
||||
|
||||
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
|
||||
|
||||
1. **Per ogni collisione di eroi e nemici**, togliere una vita.
|
||||
|
||||
Estendere la classe `Hero` per eseguire questa sottrazione:
|
||||
|
||||
```javascript
|
||||
decrementLife() {
|
||||
this.life--;
|
||||
if (this.life === 0) {
|
||||
this.dead = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Per ogni laser che colpisce un nemico**, aumentare il punteggio del gioco di 100 punti.
|
||||
|
||||
Estendere la classe Hero per fare questo incremento:
|
||||
|
||||
```javascript
|
||||
incrementPoints() {
|
||||
this.points += 100;
|
||||
}
|
||||
```
|
||||
|
||||
Aggiungere queste funzioni agli event listener di Collision:
|
||||
|
||||
```javascript
|
||||
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
|
||||
first.dead = true;
|
||||
second.dead = true;
|
||||
hero.incrementPoints();
|
||||
})
|
||||
|
||||
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
|
||||
enemy.dead = true;
|
||||
hero.decrementLife();
|
||||
});
|
||||
```
|
||||
|
||||
✅ Fare una piccola ricerca per scoprire altri giochi creati utilizzando JavaScript/Canvas. Quali sono i loro tratti comuni?
|
||||
|
||||
Alla fine di questo lavoro, si dovrebbero vedere le piccole astronavi che rappresentano le vite in basso a destra, i punti in basso a sinistra, e si dovrebbe vedere il numero di vite diminuire quando si entra in collisione con i nemici e i punti aumentare quando si colpiscono i nemici. Ottimo lavoro! Il gioco è quasi completo.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Sfida
|
||||
|
||||
Il codice è quasi completo. Si riescono a immaginare i prossimi passi?
|
||||
|
||||
## Quiz Post-Lezione
|
||||
|
||||
[
|
||||
Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/38)
|
||||
|
||||
## Revisione e Auto Apprendimento
|
||||
|
||||
Cercare alcuni modi per aumentare e diminuire i punteggi e le vite del gioco. Ci sono alcuni motori di gioco interessanti come [PlayFab](https://playfab.com). In che modo l'utilizzo di uno di questi potrebbe migliorare il proprio gioco?
|
||||
|
||||
## Compito
|
||||
|
||||
[Costruire un Gioco di Punteggio](assignment.it.md)
|
@ -0,0 +1,11 @@
|
||||
# Costruire un Gioco di Punteggio
|
||||
|
||||
## Istruzioni
|
||||
|
||||
Creare un gioco in cui si mostrano vite e punti in modo creativo. Un suggerimento è quello di mostrare le vite come cuori e i punti come un numero grande nella parte centrale in basso dello schermo. Dare un'occhiata qui per [risorse di gioco gratuite](https://www.kenney.nl/)
|
||||
|
||||
# Rubrica
|
||||
|
||||
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
|
||||
| -------- | ---------------------- | --------------------------- | -------------------------- |
|
||||
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |
|
@ -0,0 +1,223 @@
|
||||
# Costruire un Gioco Spaziale Parte 6: Fine e Riavvio
|
||||
|
||||
## Quiz Pre-Lezione
|
||||
|
||||
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/39)
|
||||
|
||||
Esistono diversi modi per esprimere una *condizione di fine gioco*. Spetta al creatore del gioco dire perché il gioco è finito. Ecco alcuni motivi, si supponga di parlare del gioco spaziale costruito finora:
|
||||
|
||||
- Un **numero `N` di astronavi nemiche sono state distrutte**: è abbastanza comune se il gioco viene diviso in diversi livelli che si debba distruggere `N` astronavi nemiche per completare un livello
|
||||
- **La propria nave è stata distrutta**: ci sono sicuramente giochi in si perde la partita se la propria astronave viene distrutta. Un altro approccio comune è il concetto di vite. Ogni volta che una propria astronave viene distrutta, sottrae una vita. Una volta perse tutte le vite, si perde la partita.
|
||||
- **Sono stati raccolti `N` punti**: un'altra condizione finale comune è che il giocatore raccolga punti. Il modo in cui ottenere punti dipende dallo sviluppatore, ma è abbastanza comune assegnare punti a varie attività come distruggere una astronave nemica o forse raccogliere elementi che vengono *rilasciati* quando gli oggetti vengono distrutti.
|
||||
- **Completare un livello**: questo potrebbe coinvolgere diverse condizioni come `X` astronavi nemiche distrutte, `Y` punti raccolti o forse che è stato raccolto un oggetto specifico.
|
||||
|
||||
## Riavvio
|
||||
|
||||
Se le persone apprezzano il gioco, è probabile che vogliano rigiocarlo. Una volta che il gioco finisce per qualsiasi motivo, si dovrebbe offrire un'alternativa per il riavvio.
|
||||
|
||||
✅ Si pensi alle condizioni per le quali si ritiene che un gioco finisca e poi a come viene chiesto di riavviare
|
||||
|
||||
## Cosa costruire
|
||||
|
||||
Aggiungere queste regole al gioco:
|
||||
|
||||
1. **Vincere il gioco**. Una volta che tutte le navi nemiche sono state distrutte, si vince la partita. Mostrare inoltre una sorta di messaggio di vittoria.
|
||||
1. **Riavvio**. Una volta perse tutte le vite o si è vinto il gioco, si dovrebbe offrire un modo per riavviare il gioco. Ricordare! Si dovrà reinizializzare il gioco e lo stato del gioco precedente dovrebbe essere cancellato.
|
||||
|
||||
## Passaggi consigliati
|
||||
|
||||
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
|
||||
|
||||
```bash
|
||||
-| assets
|
||||
-| enemyShip.png
|
||||
-| player.png
|
||||
-| laserRed.png
|
||||
-| life.png
|
||||
-| index.html
|
||||
-| app.js
|
||||
-| package.json
|
||||
```
|
||||
|
||||
Si fa partire il progetto dalla cartella `your_work` digitando:
|
||||
|
||||
```bash
|
||||
cd your-work
|
||||
npm start
|
||||
```
|
||||
|
||||
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo. Il gioco dovrebbe essere in uno stato giocabile.
|
||||
|
||||
> suggerimento: per evitare avvertimenti in Visual Studio Code, modificare la funzione `window.onload` per chiamare `gameLoopId` così com'è (senza `let`) e dichiarare gameLoopId all'inizio del file, indipendentemente: `let gameLoopId;`
|
||||
|
||||
### Aggiungere codice
|
||||
|
||||
1. **Tracciare la condizione di fine**. Aggiungere codice che tenga traccia del numero di nemici o se l'astronave dell'eroe è stata distrutta aggiungendo queste due funzioni:
|
||||
|
||||
```javascript
|
||||
function isHeroDead() {
|
||||
return hero.life <= 0;
|
||||
}
|
||||
|
||||
function isEnemiesDead() {
|
||||
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
|
||||
return enemies.length === 0;
|
||||
}
|
||||
```
|
||||
|
||||
1. **Aggiungere logica ai gestori di messaggi**. Modificare `eventEmitter` per gestire queste condizioni:
|
||||
|
||||
```javascript
|
||||
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
|
||||
first.dead = true;
|
||||
second.dead = true;
|
||||
hero.incrementPoints();
|
||||
|
||||
if (isEnemiesDead()) {
|
||||
eventEmitter.emit(Messages.GAME_END_WIN);
|
||||
}
|
||||
});
|
||||
|
||||
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
|
||||
enemy.dead = true;
|
||||
hero.decrementLife();
|
||||
if (isHeroDead()) {
|
||||
eventEmitter.emit(Messages.GAME_END_LOSS);
|
||||
return; // loss before victory
|
||||
}
|
||||
if (isEnemiesDead()) {
|
||||
eventEmitter.emit(Messages.GAME_END_WIN);
|
||||
}
|
||||
});
|
||||
|
||||
eventEmitter.on(Messages.GAME_END_WIN, () => {
|
||||
endGame(true);
|
||||
});
|
||||
|
||||
eventEmitter.on(Messages.GAME_END_LOSS, () => {
|
||||
endGame(false);
|
||||
});
|
||||
```
|
||||
|
||||
1. **Aggiungere nuovi tipi di messaggi**. Aggiungere questi messaggi alle costanti assegnate agli oggetti:
|
||||
|
||||
```javascript
|
||||
GAME_END_LOSS: "GAME_END_LOSS",
|
||||
GAME_END_WIN: "GAME_END_WIN",
|
||||
```
|
||||
|
||||
2. **Aggiungere il codice di riavvio** che fa ripartire il gioco con la semplice pressione di un pulsante selezionato.
|
||||
|
||||
1. **Mettersi in ascolto per la pressione del tasto `Invio`**. Modificare l'eventListener di window per ascoltare questa pressione
|
||||
|
||||
```javascript
|
||||
else if(evt.key === "Enter") {
|
||||
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
|
||||
}
|
||||
```
|
||||
|
||||
1. **Aggiungere messaggio di riavvio**. Aggiungere questo messaggio alle costanti in Messages:
|
||||
|
||||
```javascript
|
||||
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
|
||||
```
|
||||
|
||||
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
|
||||
|
||||
1. **Condizione di vittoria del giocatore**. Quando tutte le astronavi nemiche vengono distrutte, mostrare un messaggio di vittoria.
|
||||
|
||||
1. Innanzitutto, creare una funzione `displayMessage()` :
|
||||
|
||||
```javascript
|
||||
function displayMessage(message, color = "red") {
|
||||
ctx.font = "30px Arial";
|
||||
ctx.fillStyle = color;
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
|
||||
}
|
||||
```
|
||||
|
||||
1. Creare una funzione `endGame()` :
|
||||
|
||||
```javascript
|
||||
function endGame(win) {
|
||||
clearInterval(gameLoopId);
|
||||
|
||||
// imposta un ritardo per assicurarsi che tutte sia stato disegnato
|
||||
setTimeout(() => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
if (win) {
|
||||
displayMessage(
|
||||
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
|
||||
"green"
|
||||
);
|
||||
} else {
|
||||
displayMessage(
|
||||
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
|
||||
);
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
```
|
||||
|
||||
1. **Logica di riavvio**. Quando tutte le vite sono perse o il giocatore ha vinto la partita, mostrare che il gioco può essere riavviato. Inoltre, riavviare il gioco quando viene premuto il tasto di *riavvio* (si può decidere quale tasto deve essere mappato per il riavvio).
|
||||
|
||||
1. Creare la funzione `resetGame()` :
|
||||
|
||||
```javascript
|
||||
function resetGame() {
|
||||
if (gameLoopId) {
|
||||
clearInterval(gameLoopId);
|
||||
eventEmitter.clear();
|
||||
initGame();
|
||||
gameLoopId = setInterval(() => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
drawPoints();
|
||||
drawLife();
|
||||
updateGameObjects();
|
||||
drawGameObjects(ctx);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Aggiungere una chiamata a `eventEmitter` per riazzerare il gioco in `initGame()`:
|
||||
|
||||
```javascript
|
||||
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
|
||||
resetGame();
|
||||
});
|
||||
```
|
||||
|
||||
1. Aggiungere una funzione `clear()` a EventEmitter:
|
||||
|
||||
```javascript
|
||||
clear() {
|
||||
this.listeners = {};
|
||||
}
|
||||
```
|
||||
|
||||
👽 💥 🚀 Congratulazioni, Capitano! Il gioco è completo! Ottimo lavoro! 🚀 💥 👽
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Sfida
|
||||
|
||||
Aggiungere un suono! Si può aggiungere un suono per migliorare il gioco, magari quando un colpo di laser va a segno, o l'eroe muore o vince? Si dia un'occhiata a questo [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) per imparare a riprodurre il suono utilizzando JavaScript
|
||||
|
||||
## Quiz Post-Lezione
|
||||
|
||||
[
|
||||
Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/40)
|
||||
|
||||
## Revisione e Auto Apprendimento
|
||||
|
||||
Il compito è creare un nuovo gioco di esempio, quindi esplorare alcuni dei giochi interessanti esistenti per vedere che tipo di gioco si potrebbe costruire.
|
||||
|
||||
## Compito
|
||||
|
||||
[Creare un Gioco di Esempio](assignment.it.md)
|
@ -0,0 +1,19 @@
|
||||
# Creare un Gioco di Esempio
|
||||
|
||||
## Istruzioni
|
||||
|
||||
Provare a costruire un piccolo gioco in cui esercitarsi in diverse condizioni di fine. Variare tra ottenere un numero di punti, l'eroe perde tutte le vite o tutti i mostri vengono sconfitti. Costruire qualcosa di semplice come un gioco di avventura basato su console. Usare il seguente flusso di gioco come ispirazione:
|
||||
|
||||
```
|
||||
Eroe> Colpisce con lo spadone - l'orco riceve 3 punti di danno
|
||||
Orco> Colpisce con la mazza - l'eroe riceve 2 punti di danno
|
||||
Eroe> Calcia - l'orco takes 1 punto di danno
|
||||
Gioco> l'orco è sconfitto - L'eroe guadagna 2 monete
|
||||
Gioco> ****Non ci sono più mostri, la fortezza del male è stata conquistata****
|
||||
```
|
||||
|
||||
## Rubrica
|
||||
|
||||
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
|
||||
| -------- | ---------------------- | --------------------------- | -------------------------- |
|
||||
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |
|
Loading…
Reference in new issue