Realizzare Puzzle Bobble (Bust a Move) [1 di ?]

Ho immaginato che la prima parte da affrontare nella realizzazione di un gioco simile a Puzzle Bobble sia la gestione della griglia triangolare che si occupa del corretto posizionamento delle sfere.

Voglio evitare tutti gli errori più comuni (gli errori che ho riscontrato giocando ad altre versioni di questo gioco trovate in rete), quindi ho deciso che tutto il lavoro più difficile sarà affidato ad una singola Classe. Questa sarà la Classe più importante di tutto il progetto e sarà in grado di:

  1. Gestire la griglia di posizionamento delle sfere.
    Infatti le sfere non sono posizionate semplicemente una accanto all’altra, ma seguono una struttura triangolare.
  2. Gestire i gruppi di sfere uguali.
    Dovrà essere in grado di stabilire se si è formato un gruppo di tre o più sfere dello stesso colore, per permetterci di farle esplodere.
  3. Gestire la connessione tra le sfere e il “soffitto”.
    Anche se non fanno parte di un gruppo di sfere che esplode, dobbiamo essere in grado di rintracciare ed eliminare tutte le sfere che non hanno più un “sostegno”, così da farle precipitare.
  4. Gestire il “sovraccarico” di sfere.
    Quando la sfera più bassa della nostra struttura si trova oltre il limite stabilito il giocatore perde la partita.
  5. Gestire i colori in uso.
    Il gioco orginale evita di generare sfere di colore diverso da quelle ancora in gioco, così i colori disponibili si esauriscono poco alla volta.
  6. Gestire il movimento del gruppo di sfere.
    Col passare del tempo il gruppo di sfere in gioco si abbassa sempre di più, aumentando la difficoltà.
  7. Generare una “schermata iniziale” in base ad un modello.
    Il gioco originale presenta 30 quadri di gioco ed ognuno di questi ha un suo disegno caratteristico.
  8. Tenere il conto delle sfere in gioco.
    Per avere la possibilità di terminare il quadro quando si sono esaurite tutte le sfere.

Questi sono i compiti che affideremo a questa Classe, li ho scritti nell’ordine in cui mi sono venuti in mente e probabilmente sarà lo stesso ordine in cui affronterò i vari argomenti.

La Classe si chiamerà BubbleGrid, ed è giunto il momento di iniziare a lavorarci, altrimenti non la finirò mai!

Gestire la griglia di posizionamento delle sfere.

Non voglio che la griglia di posizionamento delle sfere sia legata in maniera indissolubile alla dimensione delle sfere, soprattutto perché non ho ancora deciso che dimensioni avranno queste sfere nel gioco finito!

Userò un valore variabile per definire il diametro delle sfere che la griglia deve utilizzare, in questo modo se dovessi decidere, più avanti, di modificarne la dimensione, potrei farlo cambiando un singolo valore.

Apro il mio editor di fiducia e scrivo le prime righe di questa Classe:

package pb{
    import flash.display.Sprite;

    public class BubbleGrid extends Sprite {
        private var _diameter:uint=60;

        public function BubbleGrid() {
        }

        /// GETTER / SETTER
        public function set diameter(n:Number) {
            if (! isNaN(n)) {
                _diameter = Math.round(Math.abs(n));
            } else {
                throw new Error('BubbleGrid:diameter deve essere un numero');
            }
        }
    }
}

La Proprietà _diameter è privata, questo perché voglio controllare i valori che vengono inseriti. Attraverso il Metodo set diameter sarò comunque in grado di impostare un valore dall’esterno, dopo averne controllato la validità.

Ho deciso che il valore di _diameter sarà sempre un numero intero, e anche i valori di x e y per il posizionamento delle bolle saranno dei numeri interi… credo che questo ci semplificherà il lavoro più avanti.

Dopo aver salvato il file con il nome BubbleGrid.as all’interno di una cartella chiamata pb mi fermo, vado a cercare una qualunque versione di Puzzle Bobble e osservo attentamente le sfere per decidere la maniera più veloce di gestirne la posizione.

Studio della distanza tra le bolle nel gioco Puzzle Bobble

Studio della distanza tra le bolle nel gioco Puzzle Bobble

  • Le sfere sono disposte su righe orizzontali.
  • Il numero di sfere non è uguale per ogni riga.
  1. La distanza tra due sfere contigue sulla stessa riga è uguale al diametro di una singola sfera.
  2. La differenza di posizione tra le righe pari e le righe dispari è uguale alla metà del diametro di una singola sfera.
  3. La distanza tra una riga e l’altra non è pari al diametro: è un po’ di meno.
  4. La misura esatta di questa distanza la possiamo ottenere dalla proporzione che esiste tra il lato di un triangolo equilatero e la sua altezza.

La prima cosa da fare, quindi, è calcolare l’altezza di un triangolo equilatero con base 1 e conservare questo valore in una Costante. Una Costante rappresenta un valore che non può essere modificato, e questo valore è senza dubbio tra quelli che non avremo mai bisogno di modificare.

Per convenzione, le Costanti hanno dei nomi scritti tutti in maiuscolo, credo che sia perché vogliono gridare al mondo quanto valgono. Aggiungo al file creato precedentemente un rigo che dichiara la Costante e ne calcola il valore:

        public const EQUILATERAL_TRIANGLE_HEIGHT:Number = Math.sqrt(3)/2;

Radice quadrata di 3, diviso 2… questa è la formula più semplice che ho trovato per calcolare la lunghezza del cateto maggiore in un triangolo rettangolo avente il cateto minore pari a 0,5 e l’ipotenusa pari a 2.

Questa è l’altezza del triangolo che stavamo cercando, gli ho anche dato un nome lungo tutto scritto in maiuscolo e l’ho dichiarato Pubblico, così se qualche altra Classe del progetto avesse bisogno di usarlo potrebbe prenderlo da qui.

Convertire le coordinate generiche in coordinate valide sulla griglia

Avendo a disposizione un valore per la distanza orizzontale tra le sfere (_diameter) e un valore per la distanza verticale (EQUILATERAL_TRIANGLE_HEIGHT) non ci rimane che scrivere un Metodo che “arrotondi” una coppia di coordinate x e y generiche in una coppia di coordinate valide all’interno della nostra griglia.

Ho deciso di utilizzare un Oggetto di tipo Point come parametro per questo Metodo perché in questo modo posso convertire agevolmente un punto del sistema di coordinate Globali (preso sulla Timeline Principale) in un punto del sistema di coordinate Locali (interno allo Sprite rappresentato dalla Classe BubbleGrid). Questo è molto importante perché ci permetterà, in futuro, di spostare e ruotare a piacimento la griglia e questa sarà sempre in grado di rintracciare le coordinate esatte.

Ecco il Metodo getGridCoords:

private function getGridCoords(p:Point):Point {
    p = globalToLocal(p); // Dal sistema di coordinate Globali al sistema di coordinate Locali
    var bx = p.x;
    var by = p.y;
    var yUnit:uint = _diameter * EQUILATERAL_TRIANGLE_HEIGHT; // La distanza verticale in pixel
    by = Math.round(by/yUnit)*yUnit; // La posizione y sulla griglia in pixel
    if (Math.round(by / yUnit) % 2 == 0) { // Solo per le righe pari
        bx -=  _diameter / 2; // La coordinata x viene spostata a sinistra per la metà del valore di diametro
        bx = (Math.round(bx/_diameter)*_diameter)+_diameter/2;
    } else { // Solo per le righe dispari
        bx = Math.round(bx / _diameter) * _diameter;
    }
    return new Point(bx,by);
}

Direi che come primo giorno può bastare, abbiamo già un sistema di griglia che utilizzeremo per il gioco vero e proprio.

Potrei lasciarvi qui e darvi appuntamento alla prossima puntata ma non posso chiedervi di credere che questa cosa funzionerà senza mostrarvi qualcosa, quindi ho deciso di aggiungere un Metodo provvisorio addBubble che ci permetterà di aggiungere un cerchio nella giusta posizione sulla griglia, e uno Sprite chiamato _p che serve a tenere traccia della più vicina posizione (x, y).

Questo è il testo completo della Classe per i test:

package pb {
    import flash.display.Sprite;
    import flash.geom.Point;

    public class BubbleGrid extends Sprite {
        public var _diameter:uint = 60;
        public var _p:Sprite;/// Solo per la fase di sviluppo

        private const EQUILATERAL_TRIANGLE_HEIGHT = Math.sqrt(3)/2;

        public function BubbleGrid() {
            _p = new Sprite();
            this.addChild(_p);
            _p.graphics.beginFill(0xFF6699,1);
            _p.graphics.drawCircle(0,0,6);
            _p.graphics.endFill();
        }
        public function addBubble(bx,by) {
            var position:Point = getGridCoords(new Point(bx,by));
            this.graphics.beginFill(0x006699, 1);
            this.graphics.drawCircle(position.x,position.y,_diameter/2);
            this.graphics.endFill();
        }
        public function view(bx,by) {
            var position:Point = getGridCoords(new Point(bx,by));
            _p.x = position.x;
            _p.y = position.y;
        }
        private function getGridCoords(p:Point):Point {
            p = globalToLocal(p); // Dal sistema di coordinate Globali al sistema di coordinate Locali
            var bx = p.x;
            var by = p.y;
            var yUnit:uint = _diameter * EQUILATERAL_TRIANGLE_HEIGHT; // La distanza verticale in pixel
            by = Math.round(by/yUnit)*yUnit; // La posizione y sulla griglia in pixel
            if (Math.round(by / yUnit) % 2 == 0) { // Solo per le righe pari
                bx -=  _diameter / 2; // La coordinata x viene spostata a sinistra per la metà del valore di diametro
                bx = (Math.round(bx/_diameter)*_diameter)+_diameter/2;
            } else { // Solo per le righe dispari
                bx = Math.round(bx / _diameter) * _diameter;
            }
            return new Point(bx,by);
        }
        public function set diameter(n:Number) {
            if (! isNaN(n)) {
                this.graphics.clear();
                _diameter = Math.round(Math.abs(n));
            } else {
                throw new Error('BubbleGrid:diameter deve essere un numero');
            }
        }
    }
}

Qui sotto un esempio di funzionamento, utilizzate lo slider per modificare il valore del diametro e il pulsante per attivare e disattivare la rotazione:

No flash, please!

Purtroppo Adobe Flash Player non è più benvoluto come una volta, per questo motivo ho disabilitato tutte le applicazioni Flash presenti sul sito. Se vuoi puoi comunque scaricare il file SWF che si trovava qui, clicca qui per far partire il download

Inserisci nome e indirizzo email per iscriverti alla mia newsletter e ricevere il file immediatamente.
In breve: i dati inseriti in questo modulo saranno utilizzati per inviarti il link per scaricare il file che desideri, saranno conservati da un servizio esterno che si chiama MailChimp e in qualsiasi momento potrai cancellare la tua iscrizione al seguente link: https://danielealessandra.us7.list-manage.com/unsubscribe?u=546bebc381e525372d2120083&id=326af7d230.

Puoi leggere l'informativa completa cliccando sul link Privacy Policy che trovi dovunque su questa pagina, e comunque visitando in qualsiasi momento l'indirizzo https://www.danielealessandra.com/privacy-policy/
Ho letto e accetto l’informativa sulla privacy.

Presto arriverà una seconda parte di questo tutorial, nel frattempo sono ansioso di leggere qualche commento, suggerimento e insulto… è gratis, perché aspettare?

Potrebbero interessarti anche...

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.