Un piccolo game fatto di cerchi
Qui trovate tutti i codici utilizzati per il game. Sono tutti ampiamente spigati e commentati. Fate attenzione perchè i collegamenti ai file dipendono da dove sono collocati; Struttura che ho utilizzato io:
- cerchio-game
- css
- bootstrap-reset.css
- stili.css
- js
- index.html
index.html
<!DOCTYPE html>
<html lang="it">
<!--
========================================================================
----------------------------- IMPORTANTE -------------------------------
========================================================================
Ho fatto alcuni miglioramenti rispetto a quanto scritto in classe per
rendere il progetto più coerente e leggibile:
1. Siccome di fatto le classi di bootstrap non vengono usate ho
eliminato bootstrap completo (bootstrap.css) e ho inserito
bootstrap-reboot.css che semplicemente inizializza gli stili del
browser di base senza creare classi. Se avete paura di confondervi
potete continuare ad usare bootstrap.css
2. O creato un foglio di stile 'stili.css' in cui ho spostato le regole
di stile che avevamo inserito nel tag style e ho aggiunto dei
commenti che spiegano gli stili
3. Ho dato un nuovo aspetto al bottone #test (ho mantenuto l'id per
non confondervi). Le classi che utilizzo sono definite in
stili.css
4. Ho aggiunto commenti di spiegazione al codice Javascript
5. Ho aggiunto una schermata 'game over'
=========================================================================
-->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Cerchio Game</title>
<!-- Carico gli stili -->
<link rel="stylesheet" href="css/bootstrap-reboot.css">
<link rel="stylesheet" href="css/stili.css">
<!-- Carico la definizionw della classe CerchiSprite -->
<script src="js/CerchioSprite.js"></script>
</head>
<body>
<div id="game-field"></div>
<button id="test" class="btn">Start</button>
<div id="over" class="game-over">
<span>Game Over</span>
</div>
<script src="js/game.js"></script>
<script>
document.getElementById('test').addEventListener('click', function () {
startGame();
this.style.display = "none";
});
</script>
</body>
</html>
css/stili.css
Ho raccolto qui gli stili utilizzati nel game. Non ho incluso bootstrap.css
completo, ma solo bootstrap-reset.css
che da solo le impostazione basi di bootstrap senza creare alcuna classe.
/* Campo di gioco largo come lo schermo */
#game-field {
width: 100vw;
height: 100vh;
/* position: relative fa in modo che le coordinate degli oggetti
posizionate in modo assoluto all'interno del contenitore (cioè
i cerchi) siano relative al contenitore stesso e non al documento. */
position: relative;
}
/* Il bottone start viene collocato al centro della viewport */
#test {
/* Il bottone viene collocato in un punto fisso
indipendente dal documento. Le coordinate sono
relative alla viewport */
position: fixed;
/* L'angolo in alto a sinistra del bottone è collocata al centro
della viewport (50% dell'altezza e 50% della larghezza) */
top: 50%;
left: 50%;
/* Per collocare il bottone al centro dello schermo l'angolo
in alto a sinistra vene spostato a sinistra del 50% della
larghezza, e in alto del 50% dell'altezza */
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
/* Per avere la certezza che il bottone sia davanti ad ogni
altro elemento*/
z-index: 2;
}
.btn {
/* Vengono definiti gli stili del bottone start */
border:none;
padding: 0.5em 1em;
font-size: 2em;
background-color: #666666;
color: #eee;
font-weight: 200;
}
/* La div#over all'inizio è nascosta (display:none) il
background è semitrasparente e la posizione fixed. L'oggetto
copre l'intero schermo*/
.game-over {
position: fixed;
top:0;
left:0;
right: 0;
bottom: 0;
display: none;
background-color: rgba(255,255,255,.6);
}
/* Elemento span contenuto in div#over */
.game-over span {
font-size: 2em;
font-weight: 200;
/* Quando attribuisco a div#over 'display: flex' l'oggetto con
margini auto si colloco al centro del contenitore */
margin: auto;
}
js/CerchioSprite.js
/*
===============================================
Classe CerchioSprite
===============================================
PROPRIETÀ
elemento: elemento HTML che è il cerchi stesso
contenitore: elemento HTML a cui il cerchio viene
aggiunto come figlio
colori: array di stringhe che contiene i colori
(secondo la sintassi css) che può assumere
il cerchio
indiceColore: numero intero che indica quale colore tra
quelli contenuti nell'array colori verrà
utilizzato
raggio: numero intero che indica in pixel il raggio
del cerchio
posizione: oggetto che ha due proprietà 'top' e 'left'
che indicano in quale posizione verrà disegnato
il cerchio
METODI
getColor(): restituisce una stringa che rappresenta il
colore da utilizzare per disegnare il cerchio
disegna(): il cerchio viene disegnato sullo schermo
assegnandogli le appropriate proprietà di stile
e aggiungendo come figlio a contenitore
PROPRIETÀ <<STATICA>>
Le proprietà statiche di una classe sono proprietà che la classe
ha in quanto tale. Non fanno parte del prototype e quindi non
ne vengono create copie per ogni oggetto creato. Nel nostro caso
la proprietà 'CerchioSprite.court è un numero intero che viene
incrementato automaticamente ogni volta che creo un nuovo cerchio.
*/
var CerchioSprite = function(cont) {
/*
Il parametro 'cont' è obbligatorio e DEVE essere un elemento HTML.
Per controllare se è un elemento HTML devo controllare se esiste la
proprietà tagName. Se però l'elemento è null o undefined o non è
un oggetto il test sulla proprietà tagName darebbe un errore di
sintassi bloccando lo script.
1. Per prima cosa se cont è undefined, null o non è un oggetto
(se cioè è una stringa, un numero, ecc.) metto in this.contenitore
un oggetto vuoto ({}).
*/
if (cont == undefined || cont == null || typeof cont != "object") {
this.contenitore = {};
} else {
this.contenitore = cont;
};
/*
2. Poi controllo se in this.contenitore esiste la proprietà tagName */
if (!this.contenitore.tagName) {
// Se la proprietà tagName non esiste lancio un errore...
throw("Campo di gioco non definito.");
// e non crea il cerchio
return;
}
// Se il controllo è positivo ->
// Creo l'elemento HTML
this.elemento = document.createElement('div');
// Do al raggio un valore casuale tra 20 e 120
this.raggio = Math.floor(Math.random() * 100 + 20);
// Creo l'oggetto posizione
this.posizione = {
// top vale un numero tra 0 e l'altezza del contenitore diminuita del diametro del cerchio
top: Math.floor(Math.random() * (this.contenitore.offsetHeight - 2 * this.raggio)),
// left vale un numero tra 0 e la larghezza del contenitore diminuita del diametro del cerchio
left: Math.floor(Math.random() * (this.contenitore.offsetWidth - 2 * this.raggio))
}
// Incremento il contatore dei cerchi creati
CerchioSprite.count++;
};
// Proprietà statica. Contatore dei cerchi creati
CerchioSprite.count = 0;
// Prototipo delle proprietà. Alla creazione il cerchio avrà queste
// proprietà con questi valori INIZIALI.
CerchioSprite.prototype = {
elemento: null,
contenitore: null,
colori: ["#000", "#F00", "#0f0", "#00f", "#ff0", "#f0f", "#0ff"],
indiceColore: -1,
raggio: 0,
posizione: null,
getColor: function() {
if (this.indiceColore == -1) {
this.indiceColore = Math.floor(Math.random() * this.colori.length);
}
return this.colori[this.indiceColore];
},
disegna: function () {
var c = this.getColor();
this.elemento.style.width = (this.raggio * 2) + 'px';
this.elemento.style.height = (this.raggio * 2) + 'px';
this.elemento.style.borderRadius = "50%";
this.elemento.style.position = "absolute";
this.elemento.style.top = this.posizione.top + 'px';
this.elemento.style.left = this.posizione.left + 'px';
this.elemento.style.backgroundColor = c;
this.elemento.style.opacity = Math.random() * 0.8 + 0.2;
this.contenitore.appendChild(this.elemento);
this.elemento.addEventListener('click', function() {
this.parentNode.removeChild(this);
}, { once:true });
}
};
js/game.js
/*
========================================================
GAME
========================================================
Il gioco crea cerchi automaticamente ad intervalli
che progressivamente diminuiscono.
L'obbiettivo del giocatore è eliminare più cerchi
possibile cliccandoli o toccandoli. Se i cerchi sullo
schermo superano i dieci la partita finisce.
La funzione principale è startGame che:
1. Crea un cerchio
2. Lo disegna nel campo di gioco
3. Controlla quanti cerchi sono stati creati
- Se i cerchi sono più di venti azzera il contatore
e diminuisce il timeOut aumentando la difficoltà
del gioco.
4. Controlla quanti cerchi sono presenti nel campo di
di gioco.
- Se sono più di dieci la partita viene
interrotta.
5. Se la partita non è stata interrotta richiama la
la funzione startGame dopo timeOut millisecondi.
Possibili miglioramenti:
- Aggiungere il conteggio dei punti.
*/
// Campo di gioco: (elemento div#game-field)
var campoGioco = document.getElementById('game-field');
// Time-out iniziale (1000 millisecondi)
var timeOut = 1000;
// Il gioco continua o è game over
var continua = true;
// Element div#over che all'inizio è nascosto e appare
// quando la partita è finita
var gameover = document.getElementById('over');
// L'evento click viene bloccato
gameover.addEventListener('click', function(e) {
// Impedisce all'evento click di risalire
// l'albero del DOM ed essere eventualmente
// intercettato dall'elemento parent o da
// un ascendente
e.stopPropagation();
})
//
var startGame = function() {
// Viene creato un nuovo cerchio
var cerchio = new CerchioSprite(campoGioco);
// che viene disegnato sullo schermo aggiungendolo
// a campo di gioco
cerchio.disegna();
// La proprietà CerchioSprite.count viene
// incrementata ogni volta che si crea un nuovo
// cerchio
if (CerchioSprite.count > 20) {
// Quando arrivo al ventesimo cerchio creato
// diminuisco il valore di timeOut
timeOut = timeOut * 0.75;
// e azzero il contatore dei cerchi
CerchioSprite.count = 0;
}
if(campoGioco.children.length > 10) {
// Se il numero dei cerchi aggiunti a
// campo di gioco e non eliminati dai
// click è superiore a 10 blobbo il gioco
continua = false;
// E faccio apparire l'elemento div#over
gameover.style.display = 'flex';
}
if (continua) {
// Se il gioco non è bloccato (continua è true)
// rilancio la funzione startGame dopo
// timeOut millisecondi
setTimeout(startGame, timeOut);
}
}