Un Gioco di Memoria con Flixel 2.0 – Parte 7 – Aggiunta di Suoni e Musiche
giu 25
Finalmente siamo arrivati all’ultima parte di quest’ultima guida. Sarà una cosa piuttosto veloce e facile, per cui non vi preoccupate, andrà tutto liscio
In questo articolo capiremo velocemente come aggiungere dei suoni e la musica al nostro gioco con Flixel. Vedremo il caricamento e quindi la riproduzione dei file audio.
Aggiungero dell’audio solamente in due dei tre stati del gioco: nel menù principale (sMainMenu) e nella schermata di gioco (sGameState).
Nel menù principale aggiungerò solamente un effetto sonoro da riprodurre alla pressione del tasto invio: iniziamo da qui.
Caricare un file audio con Flixel è molto semplice, e la procedura è molto simile a quella del caricamento di un’immagine. Vediamo per esempio il codice (commentato dove necessario) che ho aggiunto alla classe sMainMenu:
package
{
// non dimentichiamo la direttiva di import
import org.flixel.FlxSound;
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxG;
public class sMainMenu extends FlxState
{
[Embed(source='../graphics/main_menu.jpg')] private var main_bg:Class;
private var main_bg_sp:FlxSprite;
// qui definisco il suono caricandolo con un oggetto di tipo class.
[Embed(source = '../sounds/beep2.mp3')] private var sound1:Class;
// e qui dichiaro un oggetto di tipo FlxSound che useremo
private var sound11:FlxSound;
private var cur1:gMenuCursor;
override public function create():void
{
main_bg_sp = new FlxSprite(0, 0, main_bg);
add(main_bg_sp);
// ecco l'inizializzazione
sound11 = new FlxSound();
// e quindi il caricamento... il primo parametro di loadEmbedded è l'oggetto che rappresenta il suono
// mentre il secondo specifica se il suono deve essere riprodotto continuamente oppure una sola volta
sound11.loadEmbedded(sound1, false);
cur1 = new gMenuCursor();
cur1.x = 280;
cur1.y = 363;
add(cur1);
FlxG.flash.start(0xff000000, 1);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
// il richiamo del metodo play permette di riprodurre il suono
sound11.play();
FlxG.fade.start(0xff000000, 1, Change);
}
super.update();
}
private function Change():void
{
FlxG.state = new sGameState();
}
}
}
Come avete visto è tutto molto intuitivo. Ecco cosa ho fatto:
-
Memorizzo i dati della canzone in un oggetto generico di tipo Class;
-
Dichiaro un oggetto di tipo FlxSound;
-
Carico la traccia tramite l’istruzione loadEmbedded;
-
Gestisco la traccia tramite l’oggetto FlxSound;
Spero non sia difficile da capire come meccanismo, in realtà è tutto qua e c’è poco ancora da sapere! Quello che vi consiglio vivamente è di “giocare” con i vari membri della classe FlxSound per imparare a gestire il volume ed aspetti simili. Ma ripeto, è sempre tutto estremamente facile.
Passiamo ora alla classe sGameState. Anche qui le cose non cambiano, le istruzioni sono sempre le stesse.
La direttiva di Import:
import org.flixel.data.FlxAnim; import org.flixel.FlxSound; import org.flixel.FlxSprite; import org.flixel.FlxState; import org.flixel.FlxG; import org.flixel.FlxText;
Creazione degli oggetti necessari:
[Embed(source = '../sounds/song1.mp3')] private var sound:Class; private var backsound:FlxSound; [Embed(source = '../sounds/cardswoop.mp3')] private var eff1:Class; private var swoopsound:FlxSound; [Embed(source = '../sounds/beep.mp3')] private var eff2:Class; private var beepsound:FlxSound; [Embed(source = '../sounds/beep2.mp3')] private var eff3:Class; private var beepsound2:FlxSound; [Embed(source = '../sounds/applause.mp3')] private var eff4:Class; private var applause_sound:FlxSound; [Embed(source = '../sounds/boo.mp3')] private var eff5:Class; private var boo_sound:FlxSound;
Inizializzazione degli oggetti:
backsound = new FlxSound(); backsound.loadEmbedded(sound, true); backsound.volume = 0.5; backsound.play(); swoopsound = new FlxSound(); swoopsound.loadEmbedded(eff1, false); beepsound = new FlxSound(); beepsound.loadEmbedded(eff2, false); beepsound2 = new FlxSound(); beepsound2.loadEmbedded(eff3, false); applause_sound = new FlxSound(); applause_sound.loadEmbedded(eff4, false); boo_sound = new FlxSound(); boo_sound.loadEmbedded(eff5, false);
Nota: per l’oggetto backsound (la canzone di sottofondo che ho utilizzato) ho modificato la proprietà “volume” per impostare un volume inferiore al normale. In questo modo anche gli effetti sonori saranno ben percepibili
Ed infine, un’istruzione di esempio con l’uso di uno di questi oggetti:
if (WinControl())
{
applause_sound.play();
intstate = 5;
}
non riporterò gli altri per un motivo ben preciso: vorrei che foste voi ad individuarli nel codice (scaricabile da questo sito) in modo tale da poter capire in prima persona come funzionano le cose, senza imboccarvi riga per riga
Detto questo penso non ci sia altro da dire, con l’aggiunta dei suoni abbiamo terminato anche il progetto di questo semplice gioco, e di conseguenza questa mini-linea di guide dedicate a Flixel. Le potenzialità di questo framework sono davvero immense, spero che qualcuno di voi continui a giocarci e a sperimentare. E magari mi faccia sapere pure se ha fatto qualche progresso!
Grazie a tutti per aver seguito la guida, alla prossima
Un Gioco di Memoria con Flixel 2.0 – Parte 6 – Lo Stato sGameState
giu 24
Il grosso è qui. Eh si, abbiamo creato le due classi delle schermate iniziali e del menù principale, abbiamo messo a punto qualche classe extra e abbiamo ovviamente dato un’occhiata rapida anche alla classe principale e al suo preloader. Adesso però diamo la botta finale al gioco.
In questo articolo daremo un’occhiata approfondita alla schermata di gioco vera e propria, alla sua struttura e alle due classi di “supporto”. Per prima cosa, dato che probabilmente sarà un articolo piuttosto lungo, un breve sommario. Ecco cosa faremo:
- Overview generica della classe, con la spiegazione delle dinamiche del gioco;
- Realizzazione delle due classi di supporto, gCardBack e cCard;
- Implementazione della classe sGameState e conclusione.
Iniziamo subito.
Overview Generica
È sempre bene avere le idee chiare prima di buttarci a scrivere del codice. Ecco quello che ho pensato per la schermata di gioco:
Innanzitutto, avrà uno sfondo realizzato in Photoshop. Conterà sei tessere sul tabellone (le cui posizioni sono state scelte prima tramite Photoshop) e il giocatore avrà a disposizione cinque vite prima del Game Over. Ogni accoppiamento sbagliato significa una vita in meno.
Ogni partita ovviamente inizierà da zero punti per il giocatore. Inoltre, si può dire che la schermata attuale sarà divisa in varie “sotto schermate”: verranno mostrate le istruzioni, quindi alla pressione di invio si passerà al gioco vero e proprio, al Game Over si avrà una schermata differente, che alla pressione del tasto invio porterà ad iniziare una nuova partita.
Istruzioni
Schermata di Gioco
Game Over
Ora che abbiamo le idee più chiare (vi consiglio di giocare al gioco per averle ancora di più) sappiamo che:
-
dobbiamo creare una classe per il retro della tessera del tabellone (considerando inoltre che sarà animata);
-
dobbiamo creare una classe per rappresentare la carta in se, con tutte le informazioni logiche necessarie al gioco.
Per questo motivo, diamo uno sguardo alle due classi di supporto: gCardBack e cCard.
Classi di Supporto
Iniziamo con gCardBack, di sicuro la più semplice delle due. Lo scopo di questa classe è fornire il “retro” della tessera del tabellone, con tanto di animazione (come potrete vedere dal gioco vero e proprio). Per questo motivo ecco di seguito lo SpriteSheet dell’animazione:
Clicca per Ingrandire
Ora che abbiamo lo spritesheet non dobbiamo fare altro che generare la classe di conseguenza, proprio come abbiamo fatto prima per l’animazione del menù principale.
package
{
import org.flixel.FlxSprite;
public class gCardBack extends FlxSprite
{
[Embed(source = '../graphics/arrow2.png.png')]
private var img1:Class;
private var card_back:FlxSprite;
public function gCardBack(x1:int, y1:int)
{
loadGraphic(img1, true, false, 300, 113);
addAnimation("fadein", [0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25], 30, false);
addAnimation("empty", [0]);
addAnimation("full", [25]);
this.x = x1;
this.y = y1;
}
override public function update():void
{
super.update();
}
}
}
Il codice è molto facile da capire: le differenze rispetto a prima sono minime. L’unica cosa da tenere d’occhio è l’insieme dei parametri passati al costruttore (x1 ed y1) per specificare la posizione dell’oggetto sullo schermo (dato che ogni tessera del tabellone avrà una posizione differente) e la creazione di tre animazioni: empty, ovvero l’assenza della tessera, full, quando la tessera c’è e quella più importante, fadein, l’animazione vera e propria.
Da notare, infatti, che le altre due “animazioni” sono in realtà composte da un solo frame.
Il metodo update invece non fa altro che eseguire l’update dello sprite, per gestire correttamente tutte le fasi dell’animazione.
Ora passiamo al codice della classe cCard:
package
{
import org.flixel.FlxSprite;
import org.flixel.FlxState;
public class cCard extends FlxSprite
{
public var code:int;
public var occupato:Boolean;
public var scoperta:Boolean;
public function cCard(c:int, imgobj:Class)
{
loadGraphic(imgobj, false, false, 150,
113);
code = c;
scoperta = false;
}
}
}
Qui le cose sono ancora più semplici, dato che questa classe è essenzialmente logica, nonostante estenda FlxSprite.
Abbiamo due variabili di tipo Boolean (vero o falso, per capirci): occupato e scoperta. La prima servirà nella fase di “estrazione” delle varie tessere sul tabellone, l’altra invece servirà a determinare quando il giocatore abbia scoperto quella tessera (girata sul tabellone, per capirci) oppure no.
Il metodo costruttore prende in input il codice della carta (identificato da un numero) e un oggetto di tipo Class contenente i dati dell’immagine scelta.
Il codice della carta è decisamente importante, in quanto a livello logico useremo questa variabile per controllare se il giocatore ha indovinato la coppia oppure no
Quindi, se nella fase di controllo le due carte girate dal giocatore avranno, per esempio, il valore 2 nella variabile code, allora la mossa sarà valida. Altrimenti verrà segnalato l’errore e il giocatore perderà una possibilità.
Viste le due classi di supporto non ci rimane altro che la classe sGameState. Creiamo un nuovo file .as ed iniziamo a scrivere
La Classe sGameState
Come potete ben immaginare, la classe sGameState sarà piuttosto lunga rispetto alle altre classi di questo progetto: non solo implementerà la schermata di gioco, ma si occuperà anche di mostrare al giocatore le istruzioni, inizialmente, ed infine la schermata di Game Over, nel caso le cose vadano male.
Per prima cosa ho deciso di spararvi un po’ di materiale:
Schermata di Gioco
Layer nero dove mettere le carte (pensate ad un foglio di lucido sovrapposto alla schermata principale)
Layer delle istruzioni (ho scritto tutto il testo con Photoshop… i know, sono pigro)
Layer del Game Over (si, sono decisamente pigro)
E non mancano ovviamente le tessere…
Con questo abbiamo tutto il materiale che ci serve. Ora, facciamo un po’ di mente locale per capire come usare queste schermate.
Ho deciso che ci sarà una schermata sempre presente: la prima che vi ho mostrato. Quella verrà disegnata prima di tutto e le altre verranno sovrapposte. Quindi, dopo aver sovrapposto la seconda, dovremmo suddividere in “stati” anche questo stato.
Esatto, una suddivisione in “sotto-stati” nello stato presente. Volendo fare una cosa veloce, in questa schermata userò una variabile di tipo int per gestire lo stato attuale. Nel metodo update, quindi, in base al valore di questa variabile, deciderò cosa fare.
Ecco un sommario veloce dei possibili sotto-stati:
- Stato 0: il secondo layer è ancora trasparente. Ad ogni frame, fin quando non diventa ben visibile (valore di alpha = 1), aumentare il valore del canale alpha di 0.01. Questo permetterà di ottenere un effetto di dissolvenza in entrata dell’immagine. Una volta completato l’effetto, si passa allo stato 1.
- Stato 1: Vengono mostrate a schermo le istruzioni e la situazione rimane tale finché non viene premuto il tasto Invio. Premuto il tasto invio si passa allo stato 2.
- Stato 2: Viene eseguita la procedura di preparazione del tabellone, dopodiché si passa allo stato 3.
- Stato 3: Lo stato del gioco vero e proprio. Viene controllato l’input da parte dell’utente e viene eseguita tutta la logica del gioco vero e proprio. Nel caso si vinca la partita attuale si passa allo stato 5, se invece il giocatore perde le sue vite allora si passa allo stato 4.
- Stato 4: Lo stato del Game Over. Viene mostrata la schermata di Game Over aumentandone il canale dell’alpha e tutto rimane così fin quando non viene premuto il tasto Invio, per ricominciare la partita.
- Stato 5: La partita viene vinta dal giocatore, viene resettato il gioco e parte un nuovo tabellone di sei tessere, memorizzando però il punteggio ed il numero delle vite.
Come potete capire, tutto ciò non è molto complesso. Infondo, si tratta semplicemente di un blocco switch che controlla lo stato attuale del gioco, ed esegue delle istruzioni (oppure richiama delle funzioni) in base a questo stato.
Iniziamo quindi come al solito, creando lo “scheletro” del nostro stato dichiarando gli oggetti da usare ed incorporando i vari media.
package
{
import org.flixel.data.FlxAnim;
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxG;
import org.flixel.FlxText;
public class sGameState extends FlxState
{
[Embed(source = '../graphics/game_screen.jpg')]
private var img:Class;
private var background:FlxSprite;
[Embed(source =
'../graphics/game_screen_layer.png')] private var img2:Class;
private var background_layer:FlxSprite;
[Embed(source = '../graphics/instructions.png')]
private var img3:Class;
private var instruction_layer:FlxSprite;
[Embed(source = '../graphics/gameover.png')]
private var img4:Class;
private var gameover_layer:FlxSprite;
// carte usate nel gioco
[Embed(source = '../graphics/cards/1.jpg')]
private var card1:Class;
[Embed(source = '../graphics/cards/2.jpg')]
private var card2:Class;
[Embed(source = '../graphics/cards/3.jpg')]
private var card3:Class;
[Embed(source = '../graphics/cards/4.jpg')]
private var card4:Class;
[Embed(source = '../graphics/cards/5.jpg')]
private var card5:Class;
[Embed(source = '../graphics/cards/6.jpg')]
private var card6:Class;
[Embed(source = '../graphics/cards/7.jpg')]
private var card7:Class;
[Embed(source = '../graphics/cards/8.jpg')]
private var card8:Class;
[Embed(source = '../graphics/cards/9.jpg')]
private var card9:Class;
[Embed(source = '../graphics/cards/10.jpg')]
private var card10:Class;
[Embed(source = '../graphics/cards/11.jpg')]
private var card11:Class;
[Embed(source = '../graphics/cards/12.jpg')]
private var card12:Class;
// variabile del sotto-stato
private var intstate:int;
// oggetti grafici
private var backArray:Array;
private var cardArray:Array;
private var suppArray:Array;
// variabili logiche della partita
private var partita:Boolean;
private var punteggio:int;
private var seconda:Boolean;
private var memoria:int;
// testi
private var pts_int:int;
private var pts_text:FlxText;
private var vit_int:int;
private var vit_text:FlxText;
// test
private var text:FlxText;
}
}
Anche in questo caso c’è poco da spiegare. Per le schermate che ho riportato sopra non ho fatto altro che incorporarle tramite la solita procedura, dichiarando per ognuna anche un oggetto FlxSprite da usare.
Ho importato quindi la grafica delle varie tessere senza però dichiarare l’FlxSprite corrispondente, in quanto, come avete potuto notare prima, l’oggetto necessario si trova nella classe cCard, che useremo di volta in volta per ogni tessera.
Sono quindi arrivati i vari oggetti (logici e non) del gioco:
-
intstate: la variabile che memorizzerà il valore del “sotto-stato” attuale;
-
backArray, cardArray, suppArray: tre array con tre compiti diversi. Il primo gestirà il retro delle varie tessere (conterrà oggetti gCardBack), il secondo gestirà il tabellone vero e proprio (oggetti cCard), il terzo invece caricherà tutti gli oggetti di tipo Class contenenti i dati delle immagini. In questo modo sarà più facile estrarre a caso le immagini da usare sul tabellone
-
partita: booleana che serve a capire se la partita è in corso oppure no;
-
seconda: questa booleana assume il valore true se la prima tessera della mano attuale è stata girata. Serve a determinare se alla prossima tessera girata bisogna effettuare il controllo di uguaglianza tra le due oppure no;
-
pts_int e pts_text: intero e testo per rappresentare il punteggio della partita;
-
vit_int e vit_text: intero e testo per rappresentare il numero di vite del giocatore;
-
text: un testo di prova che ho usato per visualizzare alcune informazioni. Non è essenziale al fine del nostro lavoro, è solo una comodità
Possiamo quindi procedere, aggiungendo alla classe il suo metodo create.
override public function create():void
{
FlxG.mouse.show();
intstate = 0;
background = new FlxSprite(0, 0, img);
add(background);
background_layer = new FlxSprite(0, 0,
img2);
background_layer.alpha = 0;
add(background_layer);
instruction_layer = new FlxSprite(0, 0,
img3);
instruction_layer.alpha = 0;
add(instruction_layer);
gameover_layer = new FlxSprite(0, 0,
img4);
gameover_layer.alpha = 0;
add(gameover_layer);
backArray = new Array();
var c:int = 0;
cardArray = new Array();
backArray[0] = new gCardBack(10, 60);
backArray[1] = new gCardBack(175, 60);
backArray[2] = new gCardBack(340, 60);
backArray[3] = new gCardBack(10, 185);
backArray[4] = new gCardBack(175, 185);
backArray[5] = new gCardBack(340, 185);
text = new FlxText(5, 5, 400, "");
add(text);
pts_int = 0;
pts_text = new FlxText(5, 300, 400,
"Punti: 0");
pts_text.alpha = 0;
pts_text.size = 12;
add(pts_text);
vit_int = 5;
vit_text = new FlxText(5, 315, 400, "Vite:
<3 <3 <3 <3 <3");
vit_text.alpha = 0;
vit_text.size = 12;
add(vit_text);
for (c = 0; c < backArray.length; c++)
{
add(backArray[c]);
backArray[c].alpha = 0;
}
suppArray = new Array();
suppArray.push(card1);
suppArray.push(card2);
suppArray.push(card3);
suppArray.push(card4);
suppArray.push(card5);
suppArray.push(card6);
suppArray.push(card7);
suppArray.push(card8);
suppArray.push(card9);
suppArray.push(card10);
suppArray.push(card11);
suppArray.push(card12);
FlxG.flash.start(0xff000000, 1);
}
Il codice è molto chiaro. I primi passaggi servono ad inizializzare i vari oggetti necessari: essenzialmente quelli grafici, presentati poco fa. Da notare l’istruzione “alpha = 0”, in quanto all’inizio tutti i vari oggetti devono essere trasparenti, tranne ovviamente il background principale.
Successivamente passiamo alle istruzioni inerenti gli array. Inizializziamo backArray e cardArray, per poi tornare a backArray inizializzando i vari singoli elementi al suo interno, tramite le istruzioni del tipo
backArray[x] = new gCardBack(x, y);
e ripetiamo questa operazione per ogni singolo elemento, con le coordinate già calcolate, come avevo specificato precedentemente.
Provvedo, infine, a gestire le variabili dedicate al punteggio e alle vite. Sono sempre operazioni fondamentali sui testi, che abbiamo già visto. Azzeramento, inizializzazione, definizione delle coordinate passandole come parametro dal costruttore: non dovreste avere problemi. Nel caso li abbiate ci sono sempre i commenti e il codice
Rimangono ancora poche cose da fare: è necessario infatti eseguire add() per ogni carta del backArray, altrimenti questa non sarà visibile.
for (c = 0; c < backArray.length; c++)
{
add(backArray[c]);
backArray[c].alpha = 0;
}
Dopodiché il passo finale: aggiungiamo le immagini di ogni tessera nell’array suppArray ed introduciamo la schermata con un effetto di fade in, tramite l’istruzione
FlxG.flash.start(0xff000000, 1);
E per il costruttore abbiamo finito. Passiamo al metodo update:
override public function update():void
{
switch(intstate)
{
case 0:
// caso in cui il layer scuro è ancora
trasparente
if (background_layer.alpha < 1)
background_layer.alpha += 0.01; else intstate = 1;
break;
case 1:
// mostrare le istruzioni ed attendere
if (instruction_layer.alpha < 1)
instruction_layer.alpha = 1;
if (FlxG.keys.justPressed("ENTER"))
{
instruction_layer.alpha = 0;
intstate = 2;
}
break;
case 2:
Prepare();
break;
case 3:
InputControl();
break;
case 4:
if (partita == true) ClearGame();
if (gameover_layer.alpha < 1)
gameover_layer.alpha = 1;
if (FlxG.keys.justPressed("ENTER"))
{
gameover_layer.alpha = 0;
intstate = 2;
}
break;
case 5:
if (partita == true) ClearGame();
Prepare();
break;
}
super.update();
}
Come potete notare, il metodo update consiste essenzialmente in un grosso blocco switch, che prende in esame il valore di intstate. In base a questo valore esegue determinate operazioni di conseguenza, come abbiamo spiegato (a livello logico) qualche decina di righe più in alto.
Ci sono dei metodi che vengono richiamati. Vediamo quali sono, spieghiamo cosa fanno e quindi analizziamoli nel dettaglio
-
Prepare() : il metodo Prepare è piuttosto corto e semplice, non fa altro che preparare le carte dell’array backArray (il retro delle tessere) posizionandole sul tabellone di gioco. Richiamo quindi il metodo CreateBoard() e cambia intstate ponendolo uguale a 3.
-
CreateBoard() : il metodo CreateBoard è senza dubbio fondamentale alla preparazione del tabellone di gioco vero e proprio, rappresentato dall’oggetto cardArray. Tramite questo vengono prese tre carte a caso dall’array suppArray. I dati di queste tre carte, come vedremo, verranno usati per popolare l’array cardArray. Ne scegliamo tre perché abbiamo bisogno di tre coppie, per cui una volta presi i primi tre valori basterà duplicarli per ottenere la coppia. Altra cosa importante, in questo metodo viene anche “mischiato” l’array, in modo tale da poter mettere a caso nel tabellone le varie caselle ad ogni giro.
-
InputControl() : al contrario di quanto il nome voglia far credere, InputControl non si occupa esclusivamente dell’input. Ciò che fa è infatti controllare se il mouse si trova su una delle caselle del tabellone, controlla se è occupata e se ci si trova a scoprire la seconda tessera della mano oppure la prima. Di conseguenza quindi controlla se bisogna assegnare dei punti o togliere delle vite.
-
WinControl() : questo metodo (che ritorna un valore boolean) permette di sapere al volo se la partita è stata vinta oppure no. Come viene verificato il tutto è semplice: se tutte le carte sono scoperte sul tabellone vuol dire che la partita è stata vinta, quindi per ogni carta viene controllato il valore “scoperta”. Nel caso anche una sola carta sia coperta, allora la funzione ritorna false ed il gioco quindi continua.
-
ClearGame() : qua si tratta essenzialmente di un metodo di comodità, ma di sicuro necessario. Serve infatti ad effettuare tutte le operazioni di “pulizia” dopo la partita, preparando il campo per una nuova esecuzione del metodo Prepare.
Ora che abbiamo spiegato un po’ di cose, è tempo di codice! Si parte con il metodo Prepare():
private function Prepare():void
{
partita = true;
var c:int = 0;
for (c = 0; c < backArray.length; c++)
{
if (backArray[c].y < 0) backArray[c].y += 500;
backArray[c].alpha = 1;
backArray[c].play("empty");
backArray[c].play("fadein");
}
CreateBoard();
intstate = 3;
}
Qui c’è veramente poco da dire. C’è un ciclo per posizionare il retro delle carte sul tabellone e poi c’è un richiamo a CreateBoard(), oltre ovviamente al cambiamento di intstate come già detto.
Vediamo quindi CreateBoard():
private function CreateBoard():void
{
pts_text.alpha = 1;
vit_text.alpha = 1;
// svuotiamo l'array se già popolato da
prima
if (cardArray.length != 0)
cardArray.splice(0, cardArray.length);
var ar1:Array = new Array(0, 0, 0);
while ((ar1[0] == ar1[1]) || (ar1[1] ==
ar1[2]) || (ar1[0] == ar1[2]))
{
ar1[0] = Math.floor(Math.random()
* 12);
ar1[1] = Math.floor(Math.random()
* 12);
ar1[2] = Math.floor(Math.random()
* 12);
}
ar1[3] = ar1[0];
ar1[4] = ar1[1];
ar1[5] = ar1[2];
var r:int;
var c1:cCard;
while (ar1.length > 0)
{
r = Math.floor(Math.random() *
ar1.length);
c1 = new cCard(ar1[r], suppArray
[ar1[r]]);
cardArray.push(c1);
ar1.splice(r, 1);
}
//text.text = cardArray[0].code + " - " +
cardArray[1].code + " - " + cardArray[2].code + " - " + cardArray
[3].code + " - " + cardArray[4].code + " - " + cardArray[5].code;
for (r = 0; r < cardArray.length; r++)
{
cardArray[r].alpha = 0;
add(cardArray[r]);
}
cardArray[0].x = 10;
cardArray[0].y = 60;
cardArray[1].x = 175;
cardArray[1].y = 60;
cardArray[2].x = 340;
cardArray[2].y = 60;
cardArray[3].x = 10;
cardArray[3].y = 185;
cardArray[4].x = 175;
cardArray[4].y = 185;
cardArray[5].x = 340;
cardArray[5].y = 185;
}
In CreateBoard le cose sono giusto un po’ più complesse. Per prima cosa portiamo l’alpha delle due caselle di testo ad 1, in modo tale da renderle visibili. Nel caso l’array delle carte sia pieno (quindi veniamo da una partita precedente) lo svuotiamo dato che dobbiamo fare spazio ad altre carte.
Creiamo quindi un “array di supporto”: ar1, nel quale mettiamo tre zeri, per iniziare. Conterrà degli interi, che saranno gli indici delle carte estratte a caso. Come fare quindi per estrarre tre carte a caso?
Tramite un ciclo e Math.random, ovviamente. Ecco l’istruzione specifica.
while ((ar1[0] == ar1[1]) || (ar1[1] == ar1[2]) || (ar1[0] == ar1[2]))
{
ar1[0] = Math.floor(Math.random() * 12);
ar1[1] = Math.floor(Math.random() * 12);
ar1[2] = Math.floor(Math.random() * 12);
}
In poche parole, fin quando i tre valori estratti non saranno diversi (tre tessere sempre diverse quindi) il ciclo si ripeterà ed estrarrà un valore casuale tra zero e dodici. Dopo aver duplicato i valori ottenuti, quindi, bisogna mischiare le tessere, in modo tale da avere un elemento di sfida valido.
La soluzione per questo problema è semplice: si estrae un valore a caso dall’array fin quando l’array è popolato e lo si usa per popolare l’array cardArray. Ecco il codice:
var r:int;
var c1:cCard;
while (ar1.length > 0)
{
r = Math.floor(Math.random() * ar1.length);
c1 = new cCard(ar1[r], suppArray[ar1[r]]);
cardArray.push(c1);
ar1.splice(r, 1);
}
La variabile r, di tipo intero, sarà il numero a caso estratto di volta in volta per determinare l’indice dell’array da usare. c1 sarà un oggetto di tipo cCard, che inseriremo nell’array di volta in volta tramite il metodo push.
Il while ha come condizione di persistenza la lunghezza dell’array ar1, che deve essere maggiore di zero. Non scordate quindi l’istruzione
ar1.splice(r, 1);
per evitare di ritrovarvi in un ciclo infinito
Tramite l’ultimo ciclo eseguiamo l’add delle carte sul tabellone e definiamo la loro posizione, una alla volta. Per CreateBoard abbiamo concluso, passiamo ad InputControl():
private function InputControl():void
{
var c:int = 0;
for (c = 0; c < cardArray.length; c++)
{
if (FlxG.mouse.justPressed())
{
if ((FlxG.mouse.x > cardArray[c].x)
&& (FlxG.mouse.x < cardArray[c].x + cardArray[c].width)
&& (FlxG.mouse.y > cardArray[c].y)
&& (FlxG.mouse.y < cardArray[c].y + cardArray[c].height))
{
if (cardArray[c].scoperta == false)
{
if (seconda)
{
if (cardArray[memoria].code == cardArray[c].code)
{
cardArray[c].alpha = 1;
cardArray[c].scoperta = true;
seconda = false;
pts_int += 10;
if (WinControl())
{
intstate = 5;
}
}
else
{
vit_int--;
cardArray[memoria].scoperta = false;
cardArray[c].scoperta = false;
cardArray[memoria].alpha = 0;
cardArray[c].alpha = 0;
seconda = false;
if (vit_int == 0)
{
intstate = 4;
}
}
}
else
{
cardArray[c].alpha = 1;
cardArray[c].scoperta = true;
seconda = true;
memoria = c;
}
pts_text.text = "Punti: " + pts_int.toString();
var x:int = 0;
vit_text.text = "Vite: ";
for (x = 0; x < vit_int; x++) vit_text.text += "<3 ";
}
}
}
}
}
Si può dire che questo metodo sia un grande blocco if, eseguito tante volte quante sono le carte sul tabellone. Per ogni carta, infatti, questo metodo esegue le seguenti operazioni:
-
Viene controllato se l’utente clicca sulla carta presa in esame;
-
Nel caso non ci sia nessun click allora non succede niente;
-
Altrimenti viene controllata la carta, se è coperta oppure no.
-
Se coperta ed è la prima della mano (la prima della coppia scelta dal giocatore), allora viene mostrata ed il gioco continua
-
Se coperta ed è la seconda della mano allora viene controllato se le due carte hanno lo stesso codice.
-
Nel caso il codice è lo stesso, il gioco continua aggiungendo 10 punti e viene controllata l’eventuale vincita del giocatore. Nel caso vinca, intstate viene impostato a 5.
-
Nel caso il codice sia diverso, il gioco continua togliendo una vita al giocatore. Nel caso le vite siano arrivate a zero allora si passa al gameover, ovvero intstate = 4.
-
-
Per concludere vengono eseguite delle operazioni fisse ad ogni turno, come l’assegnamento dei valori giusti alle due caselle di testo.
Non c’è codice particolarmente specifico, quindi non credo ci sia bisogno di altre spiegazioni.
Passiamo al più semplice metodo WinControl(). Stavolta bisogna solo controllare se tutte le carte sono state scoperte.
private function WinControl():Boolean
{
var i:int = 0;
var res:Boolean;
res = true;
for (i = 0; i < cardArray.length; i++)
{
if (cardArray[i].scoperta == false) res = false;
}
return res;
}
L’ultimo metodo da prendere in considerazione è ClearGame() che come ho già detto prima non fa altro che liberare il tabellone togliendo le cose inutili della partita già effettuata, preparando il terreno per la partita successiva.
private function ClearGame():void
{
partita = false;
var c:int;
// gestione backarray
for (c = 0; c < backArray.length; c++)
{
backArray[c].y -= 500;
cardArray[c].alpha = 0;
}
if (intstate == 4)
{
pts_text.alpha = 0;
vit_text.alpha = 0;
pts_int = 0;
vit_int = 5;
pts_text.text = "Punti: 0";
vit_text.text = "Vite: <3 <3 <3 <3 <3";
}
// gestione carte
for (c = 0; c < cardArray.length; c++)
{
removeChild(cardArray[c]);
}
cardArray.splice(0, cardArray.length);
seconda = false;
}
Questo metodo esegue poche operazioni, tutte necessarie ed importanti.
-
Vengono spostate le tessere del backArray in su, per nasconderle alla vista del giocatore;
-
Nel caso ci sia un Game Over (intstate = 4) viene azzerato il contatore dei punti e resettato a cinque il conteggio delle vite;
-
Vengono rimosse dalla scena le carte del cardArray tramite removeChild();
-
Vengono rimossi gli oggetti del cardArray tramite il metodo splice (i due parametri sono rispettivamente il punto di partenza, con zero come primo elemento, e la lunghezza dell’array che rappresenta il numero degli elementi da cancellare) (esatto, tutti quanti, appunto);
-
Viene resettato su false, per sicurezza, il valore di “seconda”.
Dopo questa procedura, infatti, il gioco è pronto per chiamare il metodo Prepare() e predisporre una nuova partita.
E così, dopo più di duemilasettecento parole, abbiamo terminato anche questa parte del nostro tutorial, probabilmente la più importante. Nella prossima ed ultima parte vedremo invece di aggiungere dei suoni.
Buon Lavoro!
Un Gioco di Memoria con Flixel 2.0 – Parte 5 – Lo Stato sMainMenu
giu 22
Ciao a tutti! In questa parte della nostra guida aggiungeremo qualcosa di nuovo rispetto al passato: per la prima volta, infatti, impareremo come usare una sprite animata nel nostro gioco. Importazione e codice, non lasceremo niente indietro.
Raggiungeremo il nostro obiettivo focalizzandoci su due classi. La prima, sMainMenu, che conterrà i dettagli dello stato del Menù Principale del nostro gioco. Eviterò di calare nei dettagli più minuziosi, in quanto a questo punto delle cose dovreste sapere come scrivere una classe del genere.
Mi concentrerò, invece, più sulla seconda classe che useremo, gMenuCursor, e sulla sua integrazione nell’altra classe prima specificata.
Bene, iniziamo! Per prima cosa vi riporto qui di seguito il codice “base” della schermata: tutto quel codice che voi già conoscete perfettamente.
package
{
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxG;
public class sMainMenu extends FlxState
{
[Embed(source='../graphics/main_menu.jpg')] private var main_bg:Class;
private var main_bg_sp:FlxSprite;
override public function create():void
{
main_bg_sp = new FlxSprite(0, 0, main_bg);
add(main_bg_sp);
FlxG.flash.start(0xff000000, 1);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.fade.start(0xff000000, 1, Change);
}
super.update();
}
private function Change():void
{
FlxG.state = new sGameState();
}
}
}
Due parole però ce le spendo ugualmente: tutto quello che facciamo qui è caricare un’immagine di background, mostrarla a schermo e controllare l’eventuale pressione del tasto Invio. Nel caso venga premuto si passa quindi allo stato sGameState, ovvero la schermata di gioco vera e propria. Ecco come apparirebbe al momento la nostra schermata:
Se ci dovessero essere problemi, ci sono i commenti
Ora che abbiamo la base della nostra schermata passiamo alla fase successiva. Se giocate al gioco noterete una sorta di cursore animato nella schermata principale, proprio affianco alla scritta “Nuovo Gioco”. Qualcosa del genere:
Come ottenere l’effetto animato per il nostro cursore? Beh, sappiate che Flixel è decisamente utile per questo genere di situazioni e viene incontro all’utente agevolando di molto il suo lavoro. Nel mio caso, per l’animazione ho usato uno SpriteSheet, ovvero un file PNG con tutta l’animazione disegnata fotogramma per fotogramma, uno accanto all’altro.
Avete presente le pellicole dei film? Esattamente così.
Ecco il png dell’animazione che ho usato io:
Clicca per Ingrandire
Il passo successivo è creare una classe appositamente per questa animazione: chiameremo questa classe gMenuCursor, e sarà un estensione della classe FlxSprite. Qui di seguito il codice, con successiva spiegazione.
package
{
import org.flixel.FlxSprite;
public class gMenuCursor extends FlxSprite
{
[Embed(source = '../graphics/arrow.png')] private var graph:Class;
public function gMenuCursor()
{
loadGraphic(graph, true, false, 50, 30);
addAnimation("normal", [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19], 30, true);
}
override public function update():void
{
play("normal");
super.update();
}
}
}
Molti passaggi del codice sono semplici da capire: li abbiamo usati un sacco di volte e riguardano essenzialmente l’importazione del file incorporato in un oggetto di tipo Class, l’uso di un FlxSprite e così via. Sarebbe il caso però di spendere due parole su loadGraphic.
Come avete potuto notare, ci sono due cose “cambiate” o “strane”:
-
fate attenzione al secondo parametro, ora impostato su true: questo vuol dire che stiamo dicendo a Flixel che la nostra sprite sarà animata;
-
le dimensioni passate (50 e 30) – come vi ho già spiegato precedentemente, stiamo usando un file png piuttosto largo, da considerare come una “pellicola”. Le dimensioni che dobbiamo passare come parametri, tuttavia, sono la grandezza di ogni singolo “fotogramma” della nostra “pellicola”. Flixel quindi calcolerà automaticamente tutti i fotogrammi dell’animazione e ci permetterà di definire le animazioni necessarie tramite il codice.
Il passo successivo è proprio questo: tramite l’istruzione
addAnimation(“normal”, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19], 30, true);
abbiamo appena definito un’animazione. Cosa vuol dire tutto questo? Vuol dire che stiamo dicendo a Flixel di usare determinati fotogrammi (uno dopo l’altro) ad una velocità costante per dare l’idea del movimento. Esattamente come al cinema.
La funzione addAnimation serve proprio a questo. I suoi parametri sono i seguenti:
-
un nome per l’animazione da creare: successivamente, nel codice, sarà possibile richiamare l’animazione semplicemente digitando “play(“nome_animazione”);”.
-
un array di fotogrammi da utilizzare: racchiusi da parentesi quadre metteremo i numeri dei fotogrammi da usare nella nostra animazione, in sequenza.
-
il framerate da usare: il numero di frame per secondo della nostra animazione. Giocando con questo parametro aumentiamo o diminuiamo la velocità.
-
il quarto parametro serve a definire se l’animazione deve essere ripetuta o no. Nel nostro caso, stavolta, ho messo true, in quanto sarà effettivamente da mandare ciclicamente.
Dopo aver richiamato questo metodo, in memoria saranno memorizzati i dati dell’animazione “normal” con il numero di frame da utilizzare, il framerate e così via.
Il metodo update ha ancora meno istruzioni: appena creato l’oggetto provvede a riprodurre l’animazione prima specificata e quindi richiamare il metodo super.update(), necessario affinché lo sprite si aggiorni con il valore attuale dell’animazione.
Completata questa classe possiamo tornare alla nostra classe sMainMenu, per aggiungere la nostra animazione.
A livello di codice, tutto questo si traduce in quattro semplici istruzioni da inserire nel metodo “create”, subito dopo la creazione del background:
cur1 = new gMenuCursor(); cur1.x = 280; cur1.y = 363; add(cur1);
- inizializzazione del nuovo oggetto (non scordate di aggiungere la dichiarazioni tra le variabili della classe;
- definizione delle coordinate x ed y
- aggiunta tramite il metodo add()
Ed ecco come appare il codice della nostra classe una volta terminata:
package
{
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxG;
public class sMainMenu extends FlxState
{
[Embed(source='../graphics/main_menu.jpg')] private var main_bg:Class;
private var main_bg_sp:FlxSprite;
override public function create():void
{
main_bg_sp = new FlxSprite(0, 0, main_bg);
add(main_bg_sp);
cur1 = new gMenuCursor();
cur1.x = 280;
cur1.y = 363;
add(cur1);
FlxG.flash.start(0xff000000, 1);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.fade.start(0xff000000, 1, Change);
}
super.update();
}
private function Change():void
{
FlxG.state = new sGameState();
}
}
}
Non mi resta che augurarvi un buon proseguimento e magari una riletta a tutto per avere chiari i concetti, fino al prossimo articolo della guida
Un Gioco di Memoria con Flixel 2.0 – Parte 4 – Lo Stato sFadeIntroState
giu 21
Eccoci qui, in questa nuova lezione dedicata allo sviluppo di un gioco di memoria con Flixel 2.0. Le precedenti lezioni sono state di preparazione, mentre l’ultima è stata semplicemente di ripasso di alcuni concetti. Stavolta invece iniziamo a vedere un po’ qualcosa di nuovo.
Come già vi avevo detto precedentemente, infatti, una delle features del gioco sarà la presenza di schermate iniziali che si susseguiranno in fade, in dissolvenza, prima di arrivare al menù principale vero e proprio. Per passare di schermata in schermata useremo il pulsante invio.
Avete presente quelle schermate di presentazione, generalmente usate dalle case produttrici per mostrare il loro logo? (oggi molto spesso sono realizzate mandando in esecuzione dei filmati, anziché semplici immagini)
Ecco, penso abbiate capito di cosa parlo. Per l’occasione, in Photoshop, ho preparato quattro schermate:
- la prima, con il mio nome ed il nome del sito (un po’ di autocelebrazione ci sta, dai)
-
la seconda invece mostra il nome di Anastasia, la ragazza che mi ha permesso di usare le sue foto per il gioco
-
la terza invece l’ho creata per dare il giusto credito anche al caro Flixel (il logo l’ho preso dal sito ufficiale, è possibile scaricare dei badges da usare come si vuole)
-
ed infine un credit anche per la canzone di sottofondo, considerando che è coperta da copyright. Spero di non avere futuri problemi, l’ho messa in bassissima qualità e l’ho usata più che altro per scopi didattici. Ricordate: non usate contenuto coperto da copyright.
Bene, ora abbiamo le nostre schermate pronte. Ovviamente sono state create apposta della stessa risoluzione che avevamo deciso all’inizio, 500 x 400 pixel. Tutto quello che dobbiamo fare, adesso, è creare uno stato che faccia le seguenti cose:
-
Mostri la prima schermata;
-
Alla pressione di invio, faccia scomparire con un effetto di dissolvenza in uscita (fade out) la schermata attuale;
-
Faccia comparire quindi la schermata successiva con un effetto di dissolvenza in entrata (fade in);
-
Ripeta i passi 2 e 3 fino all’esaurimento di tutte le schermate;
-
Dopo l’ultima schermata faccia il passaggio allo stato successivo, quello del menù principale del gioco.
Nel nostro progetto in FlashDevelop, quindi, creiamo il codice per il nostro nuovo stato. Ecco lo scheletro di partenza (tipico per qualsiasi stato):
package
{
import org.flixel.FlxState;
public class sFadeIntroState extends FlxState
{
override public function create():void
{
// codice create
}
override public function update():void
{
// codice update
}
}
}
Come vedete non cambia molto dal solito: abbiamo il metodo create (override) e il metodo update (sempre override). La direttiva di import utilizzata è quella classica da usare per qualsiasi FlxState. Il prossimo passo è aggiungere i file immagine delle schermate al progetto.
Vi ricordate come si fa, vero?
(Create una nuova cartella nel vostro progetto, chiamatela “graphics”. Cliccate con il pulsante destro del mouse sulla nuova cartella appena creata e selezionate “Add” e quindi “Existing File”. In questo modo potrete selezionare i file immagine che avete creato e questi verranno aggiunti al progetto)
Et voilà, pronti all’uso.
Ora possiamo andare avanti. La prossima cosa da fare è caricare nel gioco i file appena importati. Questo si fa ovviamente includendo il file ed associandolo ad un oggetto di tipo “Class”. Non vi spaventate, è solo una linea di codice (e dovreste anche ricordarlo).
Ecco come appare ora la nostra classe sFadeIntroState:
package
{
import org.flixel.FlxState;
public class sFadeIntroState extends FlxState
{
[Embed(source = '../graphics/intro_slide_1.jpg')] private var slide1:Class;
[Embed(source = '../graphics/intro_slide_2.jpg')] private var slide2:Class;
[Embed(source = '../graphics/intro_slide_3.jpg')] private var slide3:Class;
[Embed(source = '../graphics/intro_slide_4.jpg')] private var slide4:Class;
private var imgs:Array;
private var coun:int;
override public function create():void
{
// codice create
}
override public function update():void
{
// codice update
}
}
}
Caricati i file notiamo altre due istruzioni: la dichiarazione di un nuovo array e di un intero, rispettivamente imgs e coun.
A cosa servono? Dunque, l’array imgs si occuperà di mantenere tutti i dati relativi alle immagini. La variabile coun invece fungerà da indice per scandire, elemento per elemento, tutto l’array. Ecco infatti come funzionerà il meccanismo dal punto di vista puramente logico:
- coun parte dal valore 0 (primo elemento dell’array = prima immagine da mostrare);
- effetto di fade in con l’immagina imgs[count];
- alla pressione di invio effetto di fade out;
- una volta che la schermata è nera al 100% il valore di coun viene incrementato di 1;
- si ritorna al punto 2 se coun è minore della grandezza dell’array, altrimenti si passa ad un nuovo stato.
Spero di essere stato chiaro. Adesso, perché non analizziamo il codice del metodo “create”? Aggiungiamolo al codice già presente, all’interno della classe, subito dopo le ultime dichiarazioni:
override public function create():void
{
coun = 0;
imgs = new Array();
imgs[0] = new FlxSprite(0, 0, slide1);
imgs[1] = new FlxSprite(0, 0, slide2);
imgs[2] = new FlxSprite(0, 0, slide3);
imgs[3] = new FlxSprite(0, 0, slide4);
add(imgs[0]);
FlxG.flash.start(0xff000000, 1);
}
Qui le cose sono molto facili da capire.
Come già detto prima, il valore di coun viene posto uguale a zero, in quanto si parte dal primo elemento dell’array. Successivamente quindi provvediamo a popolare l’array delle immagini, inizializzando, per ogni immagine importata, un oggetto di tipo FlxSprite.
I parametri passati sono tre:
-
la coordinata X;
-
la coordinata Y;
-
l’oggetto dell’immagine da usare (di tipo Class).
Tramite l’istruzione add(imgs[0]) aggiungiamo la prima immagine alla scena, permettendo di visualizzarla. L’ultima istruzione è FlxG.Flash.Start(), che useremo in modo diverso dal solito per simulare l’effetto di fade in.
Qualcuno di voi avrà notato, infatti, che esiste FlxG.fade. Quest’oggetto, tuttavia, non permette l’effetto di fade in, ma solamente quello di fade out. Per il primo, quindi, useremo in questo modo lo strumento Flash:
FlxG.flash.start(0xff000000, 1);
Invece di usare il bianco, colore classico del flash, abbiamo usato il nero con un opacità totale (vi ricordo che i primi due caratteri dopo lo 0x sono usati per l’alpha, quindi c’è red, green e blue. Il nero sarà logicamente 0xff000000. Il successivo parametro è il tempo di durata dell’effetto: un secondo.
Passiamo ora la metodo “update”. Questo è ancora più corto:
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.fade.start(0xff000000, 1, Change);
}
}
Il codice qui presentato non fa altro che controllare la pressione del tasto Invio. Nel caso la condizione si verifichi, si richiama l’effetto di fade out tramite lo strumento fade:
FlxG.fade.start(0xff000000, 1, Change);
I primi due parametri già li conoscete: il terzo è semplicemente il nome del metodo che volete richiamare al termine dell’effetto. Utile vero? Il metodo Change servirà a cambiare immagine, quindi questo è il posto migliore per usarlo.
Ecco il codice del metodo Change:
private function Change():void
{
if (coun < imgs.length - 1)
{
coun++;
add(imgs[coun]);
}
else
{
FlxG.state = new sMainMenu();
}
FlxG.fade.stop();
FlxG.flash.start(0xff000000, 1);
}
Anche qui il tutto è molto semplice da capire: per prima cosa si controlla l’indice coun. Se questo non è ancora arrivato al termine dell’array allora viene incrementato, e tramite il metodo add aggiungiamo la nuova schermata da mostrare.
Se invece abbiamo raggiunto il termine dell’array allora non facciamo altro che passare allo stato successivo (che dobbiamo ancora creare eh). Infine vengono eseguite le istruzioni di stop dell’effetto di fade out e viene reimpostato il fade in per ripartire. Nulla di più, nulla di meno, e la nostra schermata iniziale è pronta.
Data la struttura che gli abbiamo dato, inoltre, nel caso dovessimo avere necessità di aggiungere nuove schermate basterà semplicemente importare il file, embeddarlo normalmente ed inserire un altro elemento nell’array, senza stravolgere altro.
Ecco il codice completo di tutta la schermata:
package
{
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxG;
public class sFadeIntroState extends FlxState
{
[Embed(source = '../graphics/intro_slide_1.jpg')] private var slide1:Class;
[Embed(source = '../graphics/intro_slide_2.jpg')] private var slide2:Class;
[Embed(source = '../graphics/intro_slide_3.jpg')] private var slide3:Class;
[Embed(source = '../graphics/intro_slide_4.jpg')] private var slide4:Class;
private var imgs:Array;
private var coun:int;
override public function create():void
{
coun = 0;
imgs = new Array();
imgs[0] = new FlxSprite(0, 0, slide1);
imgs[1] = new FlxSprite(0, 0, slide2);
imgs[2] = new FlxSprite(0, 0, slide3);
imgs[3] = new FlxSprite(0, 0, slide4);
add(imgs[0]);
FlxG.flash.start(0xff000000, 1);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.fade.start(0xff000000, 1, Change);
}
}
private function Change():void
{
if (coun < imgs.length - 1)
{
coun++;
add(imgs[coun]);
}
else
{
FlxG.state = new sMainMenu();
}
FlxG.fade.stop();
FlxG.flash.start(0xff000000, 1);
}
}
}
Ed anche questa è fatta. Buon proseguimento, a presto con nuovi aggiornamenti
Actionscript 3 (AS3) – Mischiare a caso gli elementi di un Array (Shuffle)
giu 20
Ciao a tutti, scrivo questo post tecnico veloce per segnalarvi una possibile soluzione ad un problema che ho incontrato spesso ultimamente, nell’ambito dello sviluppo in Actionscript3. Come mischiare a caso gli elementi di un array?
Dal lato pratico è una cosa piuttosto comune: basti pensare ad un mazzo di carte e al fatto che in un qualsiasi gioco si debba mischiare. Come fare? Ecco una soluzione.
Il trucco sta nell’usare questa funzione che riporto di seguito:
public function Shuffle(a:Array):Array
{
var r:int;
var res:Array = new Array();
while (a.length > 0)
{
r = Math.floor(Math.random() * a.length);
res.push(a[r]);
a.splice(r, 1);
}
return res;
}
Il codice è semplice da spiegare: viene preso in input come argomento l’array da ordinare. Prendendo un numero a caso da zero alla lunghezza dell’array stesso, si usa questo numero come indice da usare per l’operazione di push nell’array risultante.
Fatto questo n volte, dove n è la grandezza iniziale dell’array, la lunghezza dell’array iniziale è 0, quindi si esce dal ciclo per restituire l’array ordinato in return. Ovviamente non si deve dimenticare l’operazione di Splice, per evitare di rimanere all’infinito nel ciclo.
Buon Lavoro!
Un Gioco di Memoria con Flixel 2.0 – Parte 3 – La Classe Principale ed il Preloader
giu 19
Bene, prima di mettere le mani in pasta diamo un’occhiata, giusto per riscaldarci un po’, alle due classi che già conosciamo molto, molto bene: la classe di gioco principale e la classe del preloader. Per chi non si ricordasse di loro deve sapere che già le abbiamo spiegate abbondantemente nei nostri tutorial precedenti, per cui eviterò di parlarne in maniera approfondita qui, ancora.
Per correttezza e completezza, tuttavia, le riporterò lo stesso.
Partiamo quindi con la classe principale del nostro progetto (vi ricordate come si crea, vero?), che ho chiamato M3Game.as.
package
{
// direttive di import per il framework Flixel
import org.flixel.FlxGame;
// informazioni sul file swf da creare
[SWF(width = "500", height = "400", backgroundColor = "#000000")]
[Frame(factoryClass="M3Preloader")]
public class M3Game extends FlxGame
{
public function M3Game():void
{
// uso di super per inizializzare il sistema
super(500, 400, sFadeIntroState, 1);
}
}
}
Mi sembra tutto piuttosto chiaro e senza troppe complicazioni. sFadeIntroState è ovviamente lo stato di inizio del gioco, che non ancora creiamo
Passiamo ora al codice del preloader, contenuto in M3Preloader.as:
package
{
import org.flixel.FlxPreloader;
public class M3Preloader extends FlxPreloader
{
public function M3Preloader():void
{
className = "M3Game";
super();
}
}
}
anche qui tutto liscio come l’olio. In className non facciamo altro che memorizzare il nome della classe principale precedentemente creata.
Direi che può bastare per stavolta. Alla prossima!
Un Gioco di Memoria con Flixel 2.0 – Parte 2 – Come Lavoreremo
giu 19
Benvenuti nella seconda parte del nostro tutorial dedicato alla creazione di un gioco di memoria in Flash, con l’ausilio delle librerie Flixel 2.0. Questo articolo ha una doppia funzionalità: per voi sarà una overview di quello che andrete a realizzare, una visione generale di tutto.
E vi assicuro, sarà molto utile.
Sarà funzionale anche per me, in quanto mi aiuterà a strutturare i miei “appunti di sviluppo” in qualcosa di più simile ad una serie di lezione: e certe volte, ve l’assicuro, non è per niente facile.
Oltre ad essere funzionale sarà anche necessario: a differenza degli altri due giochi presentati, infatti, questo è leggermente più complesso: ci sono più schermate da gestire, svariate tecniche utilizzate e ciò si traduce in:
- più codice
- più grafica
- più fronzoli
ed i fronzoli sono ricollegabili ad un mix di grafica e codice.
Per cui, ecco una struttura della nostra guida:
- La Classe Principale ed il Preloader: questa prima parte della guida proporrà il codice essenziale del file principale del nostro gioco e del Preloader (utilizzeremo quello classico di Flixel);
- Lo Stato sFadeIntroState: nella seconda parte della guida impareremo a realizzare delle schermate in fade partendo da immagini jpg importate nel nostro progetto, utilizzando gli strumenti che il framework mette a disposizione;
- Lo Stato sMainMenu: in questa parte creeremo una schermata “di mezzo” prima della schermata di gioco vera e propria;
- Una Classe di Supporto: gMenuCursor: spiegazione della classe gMenuCursor, primo esperimento di animazione utilizzando Flixel;
- Lo Stato sGameState: in questa parte della guida realizzeremo lo stato sGameState, ovvero la finestra di gioco vera e propria;
- La Classe gCardBack: secondo esempio di animazione, questa classe verrà utilizzata in sGameState
- La Classe cCard: in questa parte spiegheremo la classe incaricata di rappresentare, a livello logico, una tessera del tabellone di gioco.
- Aggiunta di Suoni e Musiche: il titolo parla da se. Si vedranno alcuni esempi di utilizzo di file audio importati nel progetto, completando quindi del tutto il gioco.
- Conclusioni: valutazioni finali e ringraziamenti.
Come al solito, spero di non risultare molto complicato nella mia esposizione e soprattutto di essere utile per tutti coloro che si vogliono avvicinare alla programmazione di giochi
Grazie per l’ascolto, a presto!
Un Gioco di Memoria con Flixel 2.0 – Parte 1 – Introduzione
giu 19
Finalmente, dopo essermi tolto vari impegni, posso iniziare in pace la scrittura di questa terza ed ultima guida relativa al filone dei tutorials per Flixel 2.0. Come avrete sicuramente capito, l’oggetto di questo nostro tutorial sarà un gioco di memoria dei più classici: un tabellone con sei tessere, con lo scopo di accoppiarle a due a due. Niente di più, niente di meno.
Questa volta, però, invece di realizzare qualcosa di molto basilare ho deciso di ampliare un po’ il numero delle caratteristiche del “prodotto finito”, se così vogliamo chiamarlo. Seguendo questa guida infatti potrete imparare qualcosa in più su quelle piccole (a volte anche grandi) cose che si possono trovare in un gioco oggi.
In M3, infatti, vedremo:
- schermate iniziali prima del menù (le schermate di presentazione, per intenderci)
- uso delle animazioni
- utilizzo dei controlli (sia mouse che tastiera)
- uso del comparto audio (sia effetti sonori che musiche di sottofondo)
e soprattutto, cosa importante, vedremo come mettere insieme tutto questo. Vi lascio quindi qualche schermata del gioco che andremo a creare, augurandovi una buona navigazione
La schermata di caricamento.
Una delle schermate introduttive.
La schermata delle istruzioni.
La schermata di gioco in azione.
A Presto!
M3 – MyMassMemory
giu 17
Ciao a tutti!
Iniziando col ringraziare tutti quelli che hanno commentato nell’ultimo periodo, scrivo stasera per annunciare che finalmente è pronto l’ultimo gioco dedicato alla serie di tutorials per Flixel 2.0. A differenza degli altri due giochi, tuttavia, ho deciso di usare qualcosa di più “particolare” per la realizzazione.
Clicca sull’immagine per giocare ad M3 – MyMassMemory
A fare capolino nel gioco saranno infatti le foto di Anastasia Massone, della quale consiglio vivamente di visitare il Flickr. La ragazza ha talento!
A presto, ovviamente con i tutorial sulla creazione del gioco
Trovato grazie a Flixel.
giu 14
Approfitto, prima di partire in mattinata, per fare un annuncio su una fesseria che però mi ha fatto molto piacere. Come avete potuto vedere in questo ultimo periodo ho portato avanti un bel po’ di tutorials su Flixel (no, non mi sono scordato dell’ultimo, sto solo facendo qualcosa di più serio da proporvi).
Bene, stamattina cercando su Google la parola chiave “Flixel” ho scoperto che siamo in prima pagina, subito dopo flixel.org e il repository su github.
Click per Ingrandire – Thanks Google!
Meglio di così




Ultimi Commenti