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

Nel primo articolo su come realizzare questo gioco ho stabilito un elenco dei compiti che la griglia di gioco deve essere in grado di compiere, e ho iniziato proprio dal punto numero 1.

Proverò adesso a trovare una soluzione per il punto numero 2, cioè Gestire i gruppi di sfere uguali.

Intanto mi fermo un attimo per decidere quali colori utilizzare, me ne servono 8, tutti diversi:

8 colori diversi

8 colori diversi da utilizzare per le sfere del gioco.

Per adesso li mettiamo da parte, ci serviranno più tardi.

Le Sfere saranno tutte Istanze di una stessa Classe, in questo modo ognuna di esse potrà avere una sua Proprietà color e una sua Proprietà group.

La Proprietà color ci serve per stabilire il colore della Sfera, la Proprietà group ci dirà a quale gruppo di sfere appartiene ogni singola Istanza. Possiamo già creare questa la Classe Sphere e salvarla nella stessa cartella della precedente Classe BubbleGrid:

package pb {
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.events.Event;

    public class Sphere extends Sprite{
        public var color:String; /// Il colore della sfera
        public var diameter:uint; /// Il diametro della sfera
        public var group:uint = 0; /// Il gruppo cui la sfera appartiene
        public var _groupDisplay:TextField; /// Una casella di testo per mostrare il gruppo (provvisoria)

        public function Sphere(c:String = '000000', d:uint = 30) {
            color = c;
            diameter = d;
            this.graphics.beginFill(parseInt('0x'+color),1);
            this.graphics.drawCircle(0,0,diameter/2);
            _groupDisplay = new TextField();
            _groupDisplay.selectable = false;
            this.addChild(_groupDisplay);
            this.addEventListener(Event.ENTER_FRAME, displayGroup);
        }

        private function displayGroup(e:Event) {
            _groupDisplay.text = String(this.group);
        }
    }
}

Questo è tutto quello che serve per il momento alla Classe Sphere, la gestione dei gruppi avverrà all’esterno di essa, e sarà gestito dalla Classe BubbleGrid.

Quando una nuova Sfera viene aggiunta alla griglia sarà necessario “guardarsi intorno” per controllare se in una delle 6 posizioni possibili c’è già una Sfera dello stesso colore:

6 sfere

Intorno ad ogni sfera ci sono soltanto 6 posizioni in cui cercare.

A questo punto è necessario memorizzare in qualche modo la posizione x-y di ogni Sfera sulla griglia di gioco, in maniera da poter richiamarle una alla volta per controllare il valore delle Proprietà group e color.

Per comodità utilizzerò la Proprietà name degli Sprite, chiamando ogni sfera secondo questa convenzione: Sphere_[posizione x]_[posizione y]. Il posto giusto in cui scrivere questa cosa è nella Classe BubbleGrid, il Metodo addBubble diventa così:

public function addBubble(bx,by, bubble:Sphere = null) {
    if (! bubble) {
        bubble = new Sphere();
    }
    var position:Point = getGridCoords(new Point(bx,by));
    var bubbleName = 'Sphere_' + String(position.x) + '_' + String(position.y);
    if (! this.getChildByName(bubbleName)) { /// Se non esiste già una Sfera con lo stesso nome
        bubble.name = bubbleName;
        bubble.x = position.x;
        bubble.y = position.y;
        this.addChild(bubble);
        dispatchEvent(new Event('placedBubble'));
        _bubbleList.push(bubble);
        checkGroups(bubble); /// Controllo i Gruppi
    }
}

Al codice è stata aggiunta anche una chiamata al Metodo checkGroups, che non esiste ancora, che si occuperà di fare il controllo descritto dalla precedente immagine. Ecco il codice:

 private function checkGroups(b:Sphere) {
    var sameColorGroups:Array = new Array(); /// L'elenco dei gruppi che dobbiamo unificare
    var neighborsNames:Array = [ /// L'elenco dei nomi delle 6 sfere da richiamare
        /// A destra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) + _radius) + '_' + b.name.split('_')[2],
        /// In basso a destra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) + (_radius/2)) + '_' + Number(parseInt(b.name.split('_')[2]) +Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
        /// In basso a sinistra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) - (_radius/2)) + '_' + Number(parseInt(b.name.split('_')[2])+Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
        /// A sinistra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) - _radius) + '_' + b.name.split('_')[2],
        /// In alto a sinistra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) - (_radius/2)) + '_' + Number(parseInt(b.name.split('_')[2])-Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
        /// In alto a destra
        'Sphere_' + Number(parseInt(b.name.split('_')[1]) + (_radius/2)) + '_' + Number(parseInt(b.name.split('_')[2])-Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT))
    ];
    for (var i:uint = 0; i<neighborsNames.length; i++) { /// Ciclo che esegue il controllo
        var nBubble:Sphere = this.getChildByName(neighborsNames[i]) as Sphere;
        if (nBubble && nBubble.color == b.color) {
            if (sameColorGroups.indexOf(nBubble.group) < 0) {
                sameColorGroups.push(nBubble.group);
            }
        }
    }
    b.group = _lastGroup++;
    if (sameColorGroups.length > 0) {
        changeColorGroups(sameColorGroups, b.group);
        checkAndExplode(b.group);
    }
}

Una volta raccolti tutti i gruppi da riunire il Metodo changeColorGroups si occupa di assegnare ad ognuna delle Sfere coinvolte lo stesso gruppo, con un semplice ciclo:

private function changeColorGroups(groups:Array, newGroup:uint) {
    for (var e:uint = 0; e<this.numChildren; e++) {
        var bub:Sphere = this.getChildAt(e) as Sphere;
        if (groups.indexOf(bub.group) >= 0) {
            bub.group = newGroup;
        }
    }
}

Infine l’ultimo Metodo di oggi si occupa di contare le Sfere contenute in ogni gruppo e di eliminare le sfere che appartengono a gruppi composti da almeno tre Istanze:

private function checkAndExplode(groupNum) {
    var bubbleList:Array = new Array();
    for (var e:uint = 0; e<this.numChildren; e++) {
        var bub:Sphere = this.getChildAt(e) as Sphere;
        if(bub.group == groupNum) {
            bubbleList.push(bub);
        }
    }
    if (bubbleList.length >= _minGroupForExplosion) {
        for (var i:uint = 0; i < bubbleList.length; i++) {
            this.removeChild(bubbleList[i])
        }
    }
}

Conclusione

All’inizio di questo articolo ho scelto otto colori e non li ho ancora utilizzati… In realtà non so ancora come sarà gestita questa cosa quando il gioco sarà ultimato, quindi per il momento il posto in cui coservare questi colori è la Classe Main, che serve da contenitore per la Griglia e per le Sfere… Non si può concludere una lezione senza mostrare almeno in minima parte lo stato delle cose… ecco la Classe Main di oggi:

package pb {
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.events.Event;

    public class Main extends Sprite {
        private var _grid:BubbleGrid;
        private var _b:Sphere; /// La sfera in movimento
        private var _diameter:uint = 30;
        private var _colors:Array = ['0099CC', 'CCFF33', 'FFCC00', 'FF6600', '990033', '330099', '333333', 'CCCCCC']; /// Tutti i colori del gioco

        public function Main() {
            _grid = new BubbleGrid(_diameter);
            stage.addChild(_grid);
            _grid.addEventListener('placedBubble', newBubble);
            createBubble();
            stage.addEventListener(MouseEvent.MOUSE_MOVE, moveSphere);
            stage.addEventListener(MouseEvent.CLICK, placeSphere);
        }
        private function createBubble() {
            var myColor:String = _colors[Math.floor(Math.random()*_colors.length)];
            _b = new Sphere(myColor,_diameter);
            stage.addChild(_b);
        }
        private function moveSphere(e:MouseEvent) {
            if (_b) {
                _b.x = stage.mouseX;
                _b.y = stage.mouseY;
            }
        }
        private function placeSphere(e:MouseEvent) {
            _grid.addBubble(stage.mouseX, stage.mouseY, _b);
        }
        private function newBubble(e:Event) {
            createBubble();
        }
    }
}

Qui sotto vedete il risultato, utilizzate il mouse per posizionare le sfere e constatare che i gruppi di tre o più sfere dello stesso colore vengono eliminati dalla griglia:

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.

Potrebbero interessarti anche...

3 Risposte

  1. Marco ha detto:

    Ciao, tutorial ben fatto!!; davvero utile!

    solo che non capisco questo passaggio:

    private function checkGroups(b:Sphere) {
    var sameColorGroups:Array = new Array(); /// L’elenco dei gruppi che dobbiamo unificare
    var neighborsNames:Array = [ /// L’elenco dei nomi delle 6 sfere da richiamare
    /// A destra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) + _radius) + ‘_’ + b.name.split(‘_’)[2],
    /// In basso a destra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) + (_radius/2)) + ‘_’ + Number(parseInt(b.name.split(‘_’)[2]) +Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
    /// In basso a sinistra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) – (_radius/2)) + ‘_’ + Number(parseInt(b.name.split(‘_’)[2])+Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
    /// A sinistra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) – _radius) + ‘_’ + b.name.split(‘_’)[2],
    /// In alto a sinistra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) – (_radius/2)) + ‘_’ + Number(parseInt(b.name.split(‘_’)[2])-Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT)),
    /// In alto a destra
    ‘Sphere_’ + Number(parseInt(b.name.split(‘_’)[1]) + (_radius/2)) + ‘_’ + Number(parseInt(b.name.split(‘_’)[2])-Math.floor(_radius*EQUILATERAL_TRIANGLE_HEIGHT))
    ];

    esattamente come fai a trovare le palline intorno, e se dovessi percorrere una tabella quadrata =??..

    faccio la stessa cosa solo per 4 volte? sopra, sotto, dx e sx?

    Marco

    • Daniele Alessandra ha detto:

      Ciao Marco, grazie per l’apprezzamento.

      La porzione di codice che riporti serve a chiamare per nome le sfere che ci interessano, nel precedente articolo avevo stabilito che ogni sfera contenesse indicazioni sulla sua posizione nel suo stesso nome, quindi ogni sfera è un MovieClip che ha un nome simile a questo:

      Sphere_3_15

      Dove “3” è la posizione x e “15” è la posizione y.

      Questi due valori mi dicono dove si trova esattamente la sfera corrente,b.name.split(‘_’)[1] mi dice il primo numero (ad esempio “3”) e b.name.split(‘_’)[2] mi dice il secondo numero (ad esempio “15”); applicando poi delle semplici formule utilizzo questi due numeri per ottenere le coppie di numeri corrispondenti alle 6 sfere che circondano quella attuale.

      Se utilizzi un sistema simile con una griglia a base quadrata i calcoli saranno molto più semplici di così, visto che ognuna della 4 sfere che sta in posizione destra, sinistra, sopra o sotto avrà sempre uno di questo due numeri in comune con la sfera originale.

      Supponi di avere ad esempio la sfera chiamata Sphere_5_7, alla sua destra ci sarà la Sphere_6_7 e alla sua sinistra ci sarà la Sphere_4_7; tutte e tre stanno ad altezza 7 ma la loro posizione orizzontale varia da 4 a 6.

      Sempre partendo dalla posizione Sphere_5_7 troverai la sfera che sta sotto con il nome Sphere_5_8 e quella che sta sopra come Sphere_5_6.

      In questo esempio i valori si incrementano di 1 in 1 ma è possibile che nel tuo reale caso la differenza di coordinata tra una sfera e la successiva sarà pari al diametro della sfera, quindi, con un diametro d’esempio di 30 pixel, ti sposteresti da 5 a 35 a 65 a 95, eccetera…

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.