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
      •  CerchioSprite.js
      •  game.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);
    }
}
  •