Flash
Snake con Flixel – Parte 4 – Lo Stato “GameState”
giu 9
Bene, siamo giunti allo sviluppo dello stato principale, il nostro GameState. Prima di cominciare creiamo quindi un nuovo file Actionscript e chiamiamolo “sGameState.as”. L’aspetto che avrà il nostro gioco sarà il seguente:
Avremo il tabellone di gioco, il serpente, il cibo da raccogliere (rappresentato sempre dallo stesso quadratino usato per il serpente) e una zona in basso per scrivere il punteggio (quindi, un blocco di testo).
Detto questo partiamo dal codice. Stavolta eviterò di riportarlo tutto insieme ma ne analizzerò direttamente un poco alla volta.
Partiamo, come al solito, dall’inizio:
package
{
import org.flixel.FlxState;
import org.flixel.FlxG;
import org.flixel.FlxText;
public class sGameState extends FlxState
{
private var cur_frame:int;
// oggetti grafici
private var backG:gBackground;
private var foodG:gSnake;
private var snakC:cSnake;
private var text:FlxText;
Dopo le solite import quello che facciamo è molto semplice: dichiariamo la variabile di tipo intero “cur_frame”, il cui utilizzo sarà lo stesso di “counter” nella schermata introduttiva. Un contatore come riferimento per l’effetto di “scatto”
Successivamente dichiariamo l’oggetto del background, backG, quello del cibo, foodG, ed ovviamente l’oggetto snakC, il nostro serpente. Oltre a questi c’è anche l’oggetto text, di tipo FlxText, incaricato di segnare il punteggio sullo schermo.
override public function create():void
{
FlxG.framerate = 60;
cur_frame = 0;
backG = new gBackground();
backG.x = backG.y = 0;
add(backG);
snakC = new cSnake(this);
snakC.direction = 2;
text = new FlxText(5, 360, 200, "Points: 0");
add(text);
foodG = new gSnake();
foodG.x = Math.floor(Math.random() * 34) * 10;
foodG.y = Math.floor(Math.random() * 34) * 10;
add(foodG);
}
Il metodo “create” non fa niente di eccezionale, oltre ad inizializzare i vari componenti nel modo corretto. Il framerate viene impostato ad un valore fisso di 60 e la variabile cur_frame viene azzerata.
Le altre istruzioni sono facili da capire, mi soffermerò solo sulle ultime righe:
foodG = new gSnake(); foodG.x = Math.floor(Math.random() * 34) * 10; foodG.y = Math.floor(Math.random() * 34) * 10; add(foodG);
Queste righe infatti servono ad inizializzare il quadratino che rappresenta il cibo per il nostro serpente. Una volta che viene mangiato il serpente aumenta in lunghezza. Tuttavia, dobbiamo tenere da conto che la posizione del cibo nella nostra schermata deve essere del tutto casuale.
Utilizziamo quindi la funzione Math.random() e Math.floor(). In che modo questa genera a caso il valore che ci serve?
Dunque, consideriamo che la nostra area di gioco ha una larghezza di 350×350 pixel. Il cibo ha una larghezza di 10×10 pixel e ovviamente le posizioni che potrà avere andranno di 10 in 10. Per esempio, x = 10 ed y = 50 è una posizione valida, mentre x = 24 ed y = 50 non è una posizione valida.
Da qui l’istruzione
Math.floor(Math.random() * 34) * 10;
che in poche parole dice:
- Genera un numero a caso tra 0 ed 1 (Math.random())
- Moltiplicalo per 34 (tramite il * 34)
- Arrotondalo per difetto (con Math.floor()) in modo tale da ottenere dei numeri interi
- Moltiplica il tutto per 10 (tramite il * 10) per ottenere dei numeri casuali tra 0 e 340
spero di essere stato chiaro. Questa operazione viene effettuata sia per la coordinata x del nostro cibo, sia per la coordinata y.
Passiamo avanti adesso
override public function update():void
{
// qui le azioni da fare ad ogni frame
snakC.Update();
text.text = "Points: " + String(snakC.snaketail.length * 100)
if (cur_frame == 3)
{
// qui vanno le azioni da fare ogni x frames
snakC.Move();
if (snakC.snakegraph.overlaps(foodG))
{
if (((snakC.snaketail.length % 9) == 0)&&(snakC.snaketail.length != 0))
{
FlxG.quake.start(0.001, 0.25);
FlxG.flash.start(0xffffffff, 0.25);
}
snakC.AddToTail(this);
foodG.x = Math.floor(Math.random() * 34) * 10;
foodG.y = Math.floor(Math.random() * 34) * 10;
if (snakC.snaketail.length == 60) FlxG.state = new sIntroState();
}
var c:int;
for (c = 0; c < snakC.snaketail.length; c++)
{
if (snakC.snakegraph.overlaps(snakC.snaketail[c]))
{
FlxG.state = new sIntroState();
}
}
cur_frame = 0;
}
else
{
cur_frame++;
}
}
Il codice che vedete di sopra è il codice del metodo “update” della classe sGameState. Vediamo come possiamo gestire tutto il nostro gioco da qui. Considerate che in questo metodo vi saranno due tipi di “azioni” che il programma eseguirà.
Alcune, come il movimento del serpente, verranno eseguite solo ogni tot frame, sempre per il discorso dell’effetto “scatto” che dobbiamo ottenere. Altre, come il controllo dell’input, verranno eseguite ad ogni frame per avere i giusti valori di direzione sempre aggiornati.
Ed infatti, prima ancora di iniziare il controllo della variabile “cur_frame” abbiamo quelle azioni eseguite sempre:
// qui le azioni da fare ad ogni frame snakC.Update(); text.text = "Points: " + String(snakC.snaketail.length * 100);
per prima cosa gestiamo l’input dell’utente (il metodo “Update” del serpente richiama l’altro metodo “Controls”, ricordate?) ed alla casella di testo assegnamo il valore del punteggio attuale.
Da notare che per ricavare il punteggio ho deciso di fare un calcolo sulla lunghezza della coda del serpente: se avrà 3 segmenti, allora il punteggio sarà 300 (3 * 100 insomma).
Dopodiché possiamo osservare l’ultimo pezzo di codice, che consiste nel controllo del frame esatto (stavolta ho fatto muovere il serpente ogni 3 frame) e l’esecuzione delle azioni di cui abbiamo bisogno:
if (cur_frame == 3)
{
// qui vanno le azioni da fare ogni x frames
snakC.Move();
if (snakC.snakegraph.overlaps(foodG))
{
if (((snakC.snaketail.length % 9) == 0)&&(snakC.snaketail.length != 0))
{
FlxG.quake.start(0.001, 0.25);
FlxG.flash.start(0xffffffff, 0.25);
}
snakC.AddToTail(this);
foodG.x = Math.floor(Math.random() * 34) * 10;
foodG.y = Math.floor(Math.random() * 34) * 10;
if (snakC.snaketail.length == 60) FlxG.state = new sIntroState();
}
var c:int;
for (c = 0; c < snakC.snaketail.length; c++)
{
if (snakC.snakegraph.overlaps(snakC.snaketail[c]))
{
FlxG.state = new sIntroState();
}
}
cur_frame = 0;
}
else
{
cur_frame++;
}
La prima cosa che viene fatta, senza dubbio, è muovere il serpente. Il richiamo della funzione snakC.Move() provvede a tutti gli spostamenti in base alla direzione specificata. Il controllo successivo che vediamo, realizzato con l’istruzione
if (snakC.snakegraph.overlaps(foodG))
serve a controllare se la testa del nostro serpente è entrata in contatto con il cibo. Nel caso entri in contatto con il cibo dobbiamo eseguire determinate operazioni:
- aggiungere un segmento alla coda
- generare delle nuove coordinate a caso per il cibo
Oltre questo, tramite il controllo
if (((snakC.snaketail.length % 9) == 0)&&(snakC.snaketail.length != 0))
{
FlxG.quake.start(0.001, 0.25);
FlxG.flash.start(0xffffffff, 0.25);
}
ho fatto in modo che, ad ogni mille punti, ci sia un flash sullo schermo insieme ad una piccola scossa (l’oggetto flash è stato già visto nell’altro tutorial). Giusto come abbellimento.
Infine, tramite l’istruzione
if (snakC.snaketail.length == 60) FlxG.state = new sIntroState();
controlliamo se il serpente è arrivato ad avere sessanta segmenti di coda. In tal caso torniamo alla schermata introduttiva, una sorta di conclusione del gioco.
Ovviamente dobbiamo gestire anche il caso del GameOver, nell’eventualità che l’utente vada a sbattere con la testa del serpente contro la sua coda. Il tutto viene realizzato con un ciclo for:
var c:int;
for (c = 0; c < snakC.snaketail.length; c++)
{
if (snakC.snakegraph.overlaps(snakC.snaketail[c]))
{
FlxG.state = new sIntroState();
}
}
cur_frame = 0;
Partiamo da zero fino ad arrivare alla lunghezza della coda, e per ogni segmento di coda controlliamo un eventuale collisione della testa. Nel caso questa avvenga veniamo riportati alla schermata principale. Ovviamente viene azzerato anche il contatore di frame, dato che altrimenti non si riparte più
Niente di complesso come vedete
Salvate tutto e testate il codice… anche per quanto riguarda Snake ce l’abbiamo fatta!
Snake con Flixel – Parte 3 – Lo Stato “IntroState”
giu 9
Possiamo proseguire nella nostra guida parlando del primo stato, quello della schermata iniziale del gioco: “IntroState”. Sarà una schermata piuttosto semplice, con poche aggiunte rispetto alla schermata che avevamo creato per il nostro TicTacToe.
Ecco l’aspetto che avrà la nostra schermata:
Abbiamo tre oggetti di testo, quello più grande con il titolo del gioco, quello più piccolo di seguito con i credits ed infine, più in basso, quello dedicato alle istruzioni del gioco. In aggiunta al semplice testo ho voluto inserire anche un serpentello in movimento, da sinistra verso destra.
Vediamo ora il nostro codice ed impariamo ad implementare tutto questo. Il file del codice per questo stato si chiama “sIntroState.as”, ovviamente.
package
{
import org.flixel.FlxState;
import org.flixel.FlxText;
import org.flixel.FlxG;
public class sIntroState extends FlxState
{
// text per informazioni, titoli e.c.c.
private var text1:FlxText;
private var text2:FlxText;
private var text3:FlxText;
private var sn:cSnake;
private var counter:int;
override public function create():void
{
FlxG.framerate = 60;
counter = 0;
text1 = new FlxText(20, 20, 310, "FlxSnake");
text1.size = 50;
add(text1);
text2 = new FlxText(20, 80, 310, "Created by Francesco Malatesta on www.francescomalatesta.net");
text2.size = 12;
add(text2);
text3 = new FlxText(20, 200, 310, "Controls:\n\nArrows -> Move the Snake\nEnter -> Start Game\n\nHave Fun!!!");
text3.size = 12;
add(text3);
sn = new cSnake(this);
sn.snakegraph.x = 0;
sn.snakegraph.y = 130;
sn.direction = 2;
sn.AddToTail(this);
sn.AddToTail(this);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.state = new sGameState();
}
if (counter < 5)
{
counter++;
}
else
{
counter = 0;
sn.Move();
}
}
}
}
Come ogni stato che si rispetti, le import adeguate: mi sembra inutile ribadire la loro necessità, per cui andrò avanti. Potete inoltre notare l’utilizzo dei tre oggetti di testo come avevo detto, dell’oggetto “sn” di tipo cSnake ed infine di una variabile counter.
A cosa serve questa variabile counter? Dunque, il nostro gioco va ad una velocità di 60 frame per secondo. Questo vuol dire che, se eseguissimo ad ogni frame l’operazione di movimento del serpente avremmo innanzitutto un movimento piuttosto veloce, ma soprattutto troppo fluido! Per questo motivo ho voluto trovare un metodo semplice e veloce per emulare l’effetto “scatto” tipico del gioco.
La soluzione? Muovere il serpente ogni 5 frame. In questo modo l’effetto scatto sarà molto più evidente. La variabile counter servirà proprio a questo: ad ogni chiamata del metodo Update verrà incrementata e, una volta arrivata al valore di 5, verrà mosso il serpente e quindi azzerata per ritornare all’inizio della procedura.
In questo modo, il nostro serpente verrà mosso solo ai frame 5, 10, 15, 20 … e così via. Claro?
Analizziamo nello specifico il codice del metodo “create”:
override public function create():void
{
FlxG.framerate = 60;
counter = 0;
text1 = new FlxText(20, 20, 310, "FlxSnake");
text1.size = 50;
add(text1);
text2 = new FlxText(20, 80, 310, "Created by Francesco Malatesta on www.francescomalatesta.net");
text2.size = 12;
add(text2);
text3 = new FlxText(20, 200, 310, "Controls:\n\nArrows -> Move the Snake\nEnter -> Start Game\n\nHave Fun!!!");
text3.size = 12;
add(text3);
sn = new cSnake(this);
sn.snakegraph.x = 0;
sn.snakegraph.y = 130;
sn.direction = 2;
sn.AddToTail(this);
sn.AddToTail(this);
}
Da notare per prima cosa l’istruzione
FlxG.framerate = 60;
usata per impostare il numero di frame per secondo della schermata. Dopo aver azzerato la variabile counter procediamo all’inizializzazione dei vari componenti: il testo e il nostro caro serpentello. Evitando di analizzare il testo (dato che lo conoscete bene ormai), vediamo il codice del nostro serpentello:
sn = new cSnake(this); sn.snakegraph.x = 0; sn.snakegraph.y = 130; sn.direction = 2;
Come vedete, dopo l’inizializzazione l’ho spostato un po’ più in basso e gli ho dato la direzione 2, ovvero da sinistra verso destra. Il movimento vero e proprio, come potete immaginare, verrà eseguito nel metodo “update”. Vediamolo insieme.
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.state = new sGameState();
}
if (counter < 5)
{
counter++;
}
else
{
counter = 0;
sn.Move();
}
}
Pochissime istruzioni!
Innanzitutto c’è il controllo per la pressione del tasto Invio. Se questo viene premuto passiamo allo stato di gioco, il nostro GameState che tratteremo nella prossima lezione. Dopodiché ecco che ritroviamo il codice dedicato al movimento del nostro serpente. Tenetelo bene in mente, perché lo useremo tale e quale (anzi, giusto un po’ più complicato) nella schermata di gioco!
Viene controllato il valore di counter e, se minore di 5, viene incrementato di 1. Una volta arrivato a 5 viene azzerato e il serpente viene mosso tramite la chiamata a Move(). Niente di più.
Prima di continuare, se non avete ben chiaro l’utilizzo di counter, vi consiglio di smanettare un po’ col valore nell’istruzione “if(counter < 5)” per comprendere al meglio la dinamica del tutto.
Una volta che siete sicuri proseguiamo, è tempo di creare la schermata di gioco!
Snake con Flixel – Parte 2 – Prime Classi di Partenza
giu 8
In questa parte del nostro tutorial discuteremo delle varie classi “di supporto” utilizzate per realizzare il nostro clone di Snake. L’approccio che userò, come per il TicTacToe, consisterà nel creare uno stato per ogni schermata.
Queste due schermate avranno un nome preciso:
- IntroState (nel codice sIntroState);
- GameState (nel codice sGameState);
Nota: come mai le “s” davanti al nome di ogni stato? Non è molto professionale, lo so, ma ho voluto utilizzare una sorta di “notazione ungherese” per distinguere le classi legate agli stati, alla logica e alla grafica. Per esempio, il file .as che si occupa di gestire la grafica del nostro caro serpentello, come potete notare, si chiama gSnake. La controparte logica che gestisce il serpente nel suo insieme si chiamerà invece cSnake.
Comunque sia, torniamo a noi. Oltre alle due classi dedicate agli stati avremo la classe principale. La riporto qui di seguito:
package
{
import org.flixel.FlxGame;
[SWF(width = "350", height = "400", backgroundColor = "#ffffff")]
[Frame(factoryClass="FlxSnakePreloader")]
public class FlxSnakeGame extends FlxGame
{
public function FlxSnakeGame()
{
super(350, 400, sIntroState, 1);
}
}
}
Importiamo “org.flixel.FlxGame” che è quello che ci serve per questo file e definiamo la finestra di gioco: 350×400 pixel (l’area di gioco sarà quadrata e avrà un piccolo spazio nero sotto per scrivere i punti).
Come per TicTacToe specifichiamo la classe del Preloader: stavolta l’ho chiamata FlxSnakePreloader. Il resto viene da se. La funzione super() nel costruttore della classe richiama, come stato principale, quello che definiremo nella prossima lezione, ovvero l’IntroState.
Ora il codice del Preloader, nel file FlxSnakePreloader.as, che eviterò di commentare ulteriormente in quanto dovreste averlo capito.
package
{
import org.flixel.FlxPreloader;
public class FlxSnakePreloader extends FlxPreloader
{
public function FlxSnakePreloader():void
{
className = "FlxSnakeGame";
super();
}
}
}
Ora che abbiamo definito queste prime classi possiamo pensare ad integrare la grafica del nostro gioco. Oltre ai testi, generabili tranquillamente via codice, dobbiamo considerare la necessità di due files grafici:
- il background di gioco
- un pezzo di serpente (prendetela come un unità, che cloneremo via codice man mano che il serpente si allungherà)
Ecco la grafica che ho preparato io:
Una sorta di “scacchiera” come background e un quadratino colorato che si noti sullo sfondo è tutto quello che ci serve. Ovviamente le dimensioni non sono date a caso. Per il mio gioco ho deciso che ogni quadratino avrà una dimensione di 10×10 pixel.
Se è la prima volta che provate a realizzare un gioco del genere e non vi sentite molto sicuri allora utilizzate anche voi queste dimensioni per la vostra grafica, oppure usate direttamente i file che metto a disposizione. Il motivo risiede nel fatto che la grandezza dei file è strettamente legata al codice e viceversa.
Ovviamente, dopo aver importato nel nostro progetto i due files grafici (li ho messi in una cartella a parte chiamata “graphics” per fare un po’ di ordine) è tempo di scrivere il codice delle classi dedicate.
Per quanto riguarda il file gBackground (lo sfondo):
package
{
import org.flixel.FlxSprite;
public class gBackground extends FlxSprite
{
[Embed(source = '../graphics/background.jpg')]
private var imggraph:Class;
public function gBackground():void
{
loadGraphic(imggraph, false, false, 350, 350, true);
}
}
}
Per quanto riguarda il file gSnake (la “sezione” di serpentello):
package
{
import org.flixel.FlxSprite;
public class gSnake extends FlxSprite
{
[Embed(source = '../graphics/snake.jpg')]
private var imggraph:Class;
public function gSnake():void
{
loadGraphic(imggraph, false, false, 10, 10, false);
}
}
}
e per quanto riguarda il codice dedicato alla grafica direi che ci siamo. Ovviamente è solo una piccola parte, ma ce la siamo tolta piuttosto velocemente. Adesso è tempo di complicare un po’ le cose, dato che dobbiamo creare la classe dedicata al protagonista del gioco.
Prima di sparare il codice, parliamone un po’. Il nostro serpentello sarà fatto quindi da questi quadratini arancioni. Avrà una testa, che servirà a guidare il resto del corpo. Dato che il corpo diventerà sempre più lungo, useremo un array per gestire la coda del serpente.
Ogni pezzo della coda seguirà i movimenti dalla testa in maniera precisa. Come realizzeremo questo?
Partiamo dal presupposto che ad ogni frame del nostro gioco le cose cambiano. La testa si muoverà in base alle nostre direzioni in un verso stabilito. Supponiamo di aver premuto la freccia destra, quindi il nostro serpente si muove verso destra.
Muovendosi verso destra, la testa aumenta la propria coordinata X. Dato che la coda segue la testa in maniera precisa, ad ogni frame calcoleremo il vecchio valore X della testa e al primo pezzo della coda verrà assegnato proprio quello.
Ricordandoci che ogni quadratino ha una larghezza di 10 pixel, immaginiamo un caso di esempio:
Il nostro serpente ha un solo pezzo nella coda, ciò vuol dire che in totale ne ha due: testa più un pezzo di coda.
Nel primo istante, la testa del serpente si trova alle coordinate x = 50. Nel secondo istante invece si trova alla coordinata x = 60. Imitando la testa, l’unico pezzo di corpo presente prende il valore che aveva la testa nell’istante precedente.
Quindi, per la testa con la x = 60 il pezzo di corpo avrà una x = 50, e così via fin quando si andrà verso destra. Quando analizzeremo il codice potrete capire ancora meglio tutto il funzionamento.
Prima di spararvi il codice quindi concludiamo le nostre osservazioni: dobbiamo ricordarci che non esiste il movimento in diagonale, ma solo sull’asse x ed y. Il serpente, inoltre, si muoverà di 10 in 10 pixel. Non si muoverà ad ogni frame, ma ad ogni 3 frame per dare un effetto di “scatto” più coerente col tipo di gioco che stiamo realizzando. Infine, il nostro serpente non potrà toccare con la testa le altre parti del corpo, altrimenti si andrà in Game Over, tornando alla schermata introduttiva. Arrivati a 60 sezioni di coda (6000 punti, dato che ogni pezzo di coda ne da 100) il gioco finisce e si torna alla schermata introduttiva.
Detto questo, a voi il codice del nostro serpente.
package
{
import org.flixel.FlxG;
import org.flixel.FlxState;
public class cSnake
{
// testa del serpente
public var snakegraph:gSnake;
// corpo del serpente con variabile di conteggio
public var snaketail:Array;
private var snakecoun:int;
// 1. Su
// 2. Destra
// 3. Giù
// 4. Sinistra
public var direction:int;
public function cSnake(state:FlxState)
{
snakegraph = new gSnake();
state.add(snakegraph);
snaketail = new Array();
snakecoun = 0;
}
public function Update():void
{
Controls();
}
private function Controls():void
{
if (FlxG.keys.justPressed("UP"))
{
if(direction != 3) direction = 1;
}
if (FlxG.keys.justPressed("RIGHT"))
{
if(direction != 4) direction = 2;
}
if (FlxG.keys.justPressed("DOWN"))
{
if(direction != 1) direction = 3;
}
if (FlxG.keys.justPressed("LEFT"))
{
if(direction != 2) direction = 4;
}
}
public function Move():void
{
var lastx:int;
var lasty:int;
lastx = snakegraph.x;
lasty = snakegraph.y;
switch(direction)
{
case 1:
snakegraph.y = snakegraph.y - 10;
if (snakegraph.y < 0) snakegraph.y = 340;
break;
case 2:
snakegraph.x = snakegraph.x + 10;
if (snakegraph.x > 340) snakegraph.x = 0;
break;
case 3:
snakegraph.y = snakegraph.y + 10;
if (snakegraph.y > 340) snakegraph.y = 0;
break;
case 4:
snakegraph.x = snakegraph.x - 10;
if (snakegraph.x < 0) snakegraph.x = 340;
break;
}
if (snaketail.length > 0)
{
for (var i:int = snaketail.length -1; i >= 0; i--)
{
if (i == 0)
{
snaketail[0].x = lastx;
snaketail[0].y = lasty;
}
else
{
snaketail[i].x = snaketail[i - 1].x;
snaketail[i].y = snaketail[i - 1].y;
}
}
}
}
public function AddToTail(state:FlxState):void
{
var s:gSnake = new gSnake();
switch(direction)
{
case 1:
s.x = snakegraph.x;
s.y = snakegraph.y + 10;
break;
case 2:
s.x = snakegraph.x - 10;
s.y = snakegraph.y;
break;
case 3:
s.x = snakegraph.x;
s.y = snakegraph.y - 10;
break;
case 4:
s.x = snakegraph.x + 10;
s.y = snakegraph.y;
break;
}
snaketail[snakecoun] = s;
state.add(snaketail[snakecoun]);
snakecoun++;
}
}
}
Innanzitutto, come al solito, partiamo dall’inizio per poi scendere in giù. Tra le importazioni c’è la classe FlxG, che utilizzeremo per gestire sopratutto l’input dell’utente. La classe FlxState servirà dopo, quando dovremo utilizzare il metodo add() per disegnare su schermo il serpente.
Le variabili e gli oggetti che utilizziamo sono solamente quattro: la grafica della testa del serpente, identificata con “snakegraph”, la coda del serpente come “snaketail”, un contatore per tenere traccia del numero di sezioni del corpo del serpente, “snakecoun”. Infine abbiamo “direction”, un intero che potrà assumere i valori 1,2,3 o 4 in base alla direzione da prendere.
Passiamo subito al codice del metodo costruttore, analizzandolo nel dettaglio:
public function cSnake(state:FlxState)
{
snakegraph = new gSnake();
state.add(snakegraph);
snaketail = new Array();
snakecoun = 0;
}
Tutte cose molto semplici. Inizializziamo l’oggetto snakegraph, oltre ovviamente all’array della coda del serpente. Azzeriamo anche il contatore della coda, in quanto all’inizio del gioco abbiamo solo la testa.
Da notare che passiamo un parametro, “state” di tipo FlxState. Bene, questo ci servirà per l’istruzione “state.add(snakegraph)”, in quanto senza passare lo stato dove ci troviamo come parametro non potremmo mai disegnare su schermo il nostro serpente.
Con quest’ultima osservazione il nostro costruttore è pronto. Abbiamo quindi un metodo “Update” che ne richiama un altro, “Controls”. Eccoli:
public function Update():void
{
Controls();
}
private function Controls():void
{
if (FlxG.keys.justPressed("UP"))
{
if(direction != 3) direction = 1;
}
if (FlxG.keys.justPressed("RIGHT"))
{
if(direction != 4) direction = 2;
}
if (FlxG.keys.justPressed("DOWN"))
{
if(direction != 1) direction = 3;
}
if (FlxG.keys.justPressed("LEFT"))
{
if(direction != 2) direction = 4;
}
}
La funzione Controls si occupa di controllare eventuali tasti premuti sulla tastiera, nello specifico le frecce direzionali. Se una di queste viene premuta allora viene modificata la direzione con il valore corrispondente.
Per esempio, la direzione “SU” è identificata con il numero 1. Da qui il codice
if (FlxG.keys.justPressed("UP"))
{
if(direction != 3) direction = 1;
}
Il controllo “if(direction != 3” serve ad evitare che il serpentello ritorni indietro su se stesso, portando quindi al Game Over. Il resto viene da se, è tutto uguale a quest ultimo esempio. Ora possiamo passare tranquillamente alla funzione Move, che si occupa di muovere il nostro protagonista.
public function Move():void
{
var lastx:int;
var lasty:int;
lastx = snakegraph.x;
lasty = snakegraph.y;
switch(direction)
{
case 1:
snakegraph.y = snakegraph.y - 10;
if (snakegraph.y < 0) snakegraph.y = 340;
break;
case 2:
snakegraph.x = snakegraph.x + 10;
if (snakegraph.x > 340) snakegraph.x = 0;
break;
case 3:
snakegraph.y = snakegraph.y + 10;
if (snakegraph.y > 340) snakegraph.y = 0;
break;
case 4:
snakegraph.x = snakegraph.x - 10;
if (snakegraph.x < 0) snakegraph.x = 340;
break;
}
if (snaketail.length > 0)
{
for (var i:int = snaketail.length -1; i >= 0; i--)
{
if (i == 0)
{
snaketail[0].x = lastx;
snaketail[0].y = lasty;
}
else
{
snaketail[i].x = snaketail[i - 1].x;
snaketail[i].y = snaketail[i - 1].y;
}
}
}
}
Le operazioni che facciamo qui sono, nell’ordine, le seguenti:
- memorizziamo i vecchi valori x ed y della testa del serpente;
- muoviamo la testa del serpente in base alla direzione definita dall’utente
- se il serpente ha una coda (snaketail.lenght > 0) allora con un ciclo apposito, partendo dalla fine della coda fino ad arrivare all’inizio, diamo all’elemento preso in considerazione la posizione del precedente. Una volta arrivati al primo pezzo, quindi, vengono usate le vecchie coordinate della testa.
Cosa vuol dire tutto questo? Pensate di avere quattro pezzi nella vostra coda. Il ciclo prenderà quindi in esame il quarto pezzo e gli darà la posizione del terzo, poi prenderà il terzo pezzo per dargli la posizione del secondo e così via, fino al primo. Arrivato al primo pezzo, quindi, che non ha un precedente (è l’elemento zero dell’array) gli diamo il valore vecchio della testa, memorizzato in lastx e lasty.
Più facile di così non potevo spiegarlo
Bene, adesso dobbiamo definire solo l’ultima funzione: AddToTail. Quel metodo che, regolandosi con la direzione del serpente, aggiunge alla coda un nuovo pezzo. Et voilà il codice:
public function AddToTail(state:FlxState):void
{
var s:gSnake = new gSnake();
switch(direction)
{
case 1:
s.x = snakegraph.x;
s.y = snakegraph.y + 10;
break;
case 2:
s.x = snakegraph.x - 10;
s.y = snakegraph.y;
break;
case 3:
s.x = snakegraph.x;
s.y = snakegraph.y - 10;
break;
case 4:
s.x = snakegraph.x + 10;
s.y = snakegraph.y;
break;
}
snaketail[snakecoun] = s;
state.add(snaketail[snakecoun]);
snakecoun++;
}
Per prima cosa, questo metodo crea un oggetto d’appoggio s, di tipo gSnake. In poche parole, crea il nostro quadratino arancione che verrà poi attaccato alla coda. Subito dopo, nel nostro codice, viene infatti calcolata la posizione del nuovo pezzo in base alla direzione del nostro serpente.
Per esempio, supponendo che il nostro personaggio vada verso destra, ecco il codice corrispondente:
case 2: s.x = snakegraph.x - 10; s.y = snakegraph.y; break;
In poche parole, andando verso destra deve ritrovarsi il nuovo pezzo alla sua sinistra: esattamente 10 pixel indietro verso sinistra.
Le ultime tre operazioni servono quindi ad inserire il pezzo di coda nella coda vera e propria e ad aggiornare il contatore. Come avvenuto precedentemente, in questo metodo passiamo come parametro lo stato attuale, per poter disegnare correttamente il nuovo segmento del serpente tramite il metodo add.
snaketail[snakecoun] = s; state.add(snaketail[snakecoun]); snakecoun++;
Con queste tre ultime righe di codice abbiamo concluso tutta la sezione della guida dedicata alle classi extra. Nelle prossime lezioni potremo concentrarci esclusivamente sulle due schermate da implementare e il nostro FlxSnake sarà pronto.
Buon Proseguimento!
Snake con Flixel – Parte 1 – Introduzione e Raccolta di Idee
giu 8
Ciao a tutti! Vi do il benvenuto in questo nuovo tutorial della linea dedicata a Flixel 2.0. Stupendo Framework. Cosa faremo stavolta? Ho in mente, come avevo anticipato qualche giorno fa, di lavorare ad un altro gioco di quelli da “realizzare in meno di un giorno di lavoro”.
L’altra volta è stato il turno di TicTacToe (preparato in due ore scarse), stavolta invece impareremo a realizzare un nostro gioco simile a Snake (fatto in qualcosa come quattro ore). Chi non lo conosce? E’ uno dei più famosi al mondo nelle sue infinite versioni su infinite piattaforme.
Dato che stiamo studiando Flixel, il nostro Snake non sarà molto complesso e la sua realizzazione rimarrà fedele alla nostra linea “semplice per imparare dei concetti nuovi”. Sarà composto da due schermate: una schermata introduttiva (con il nostro serpentello animato, giusto per metterci qualcosa di nuovo) e la schermata di gioco vera e propria, con il tabellone e una piccola zona in basso contenente un segnapunti.
A differenza della volta precedente eviterò di soffermarmi in tutti i dettagli possibili ed immaginabili, ma porterò avanti solamente gli argomenti che mi sembrano più importanti. Giusto per fare un esempio, non spiegherò come incorporare un’immagine nel nostro gioco, dato che l’ho fatto già, ma riporterò la classe per intero spiegando per sommi capi a cosa serve. Insomma, non vi lascerò mai del tutto da soli.
E poi ci sono i commenti! Per cui, per qualsiasi motivo, mi potete trovare lì, cercherò di rispondervi il prima possibile.
A presto per la prima lezione
TicTacToe con Flixel – PDF e Codice Sorgente a Disposizione
giu 7
Ciao a tutti, aggiorno al volo per segnalarvi che il codice sorgente (tutto il progetto FlashDevelop) del gioco TicTacToe con Flixel è disponibile per il download. Inoltre, per semplificare la vita di qualcuno, ho deciso di mettere a disposizione la guida anche in formato PDF.
Potete trovare il materiale sulla pagina del tutorial:
http://francescomalatesta.net/tutorials/flixel-2-0/
Ciao!
TicTacToe con Flixel – Parte 6 – Conclusioni
giu 6
Dopo qualche ora passata a capire e a leggere questi tutorials, dovreste essere capaci di creare il vostro clone di TicTacToe. Non credo sia stato difficile, vero? Spero di essere stato chiaro e abbastanza esaustivo! Considerate inoltre una cosa molto importante: alcuni dei concetti legati alla programmazione presenti in questo tutorial sono stati già ampiamente trattati nella guida di Michael James Williams, tradotta da me e già presente su questo sito.
Quello che potete fare ora è continuare a sperimentare, provare nuove cose e magari fare delle piccole aggiunte: da un semplice contatore segnapunti al miglioramento dell’intelligenza artificiale del nostro programma.
Le variazioni che potete fare sul tema sono praticamente infinite… scatenate la fantasia!
Un saluto e una buonanotte, speriamo di rivederci presto per il prossimo tutorial
TicTacToe con Flixel – Parte 5 – GameState
giu 6
Siamo finalmente giunti al GameState. Mi raccomando, voglio la massima attenzione perché è in questo file che implementeremo il codice del gioco vero e proprio: partendo dall’input dell’utente per arrivare al calcolo dei risultati, passando per l’algoritmo di gestione dell’avversario comandato dalla CPU.
Il codice è piuttosto lungo rispetto allo standard di questo tutorial. Nonostante questo ho deciso di riproporre il listato per intero per poi commentarlo, metodo per metodo. Tanto, su questo blog avete visto articoli ben più lunghi
Bando alle ciance, ecco il codice.
package
{
import org.flixel.FlxState;
import org.flixel.FlxText;
import org.flixel.FlxG;
public class GameState extends FlxState
{
public var tabellone:Array;
// 0. In Corso - 1. Giocatore Vince - 2. Computer Vince
public var partita:int;
public var turno_giocatore:Boolean;
public var tab_bg:TabelloneGraphic;
public var info_text:FlxText;
public var subinfo_text:FlxText;
override public function create():void
{
FlxG.mouse.show();
tabellone = new Array();
tab_bg = new TabelloneGraphic();
add(tab_bg);
info_text = new FlxText(5, 250, 280, "Partita In Corso");
info_text.size = 20;
add(info_text);
subinfo_text = new FlxText(5, 275, 280, "");
subinfo_text.size = 10;
add(subinfo_text);
// VALORI TABELLONE:
// 0. Vuoto
// 1. X
// 2. O
// prima riga
tabellone[0] = new CasellaTabellone(20, 20, 0);
tabellone[1] = new CasellaTabellone(100, 20, 0);
tabellone[2] = new CasellaTabellone(180, 20, 0);
// seconda riga
tabellone[3] = new CasellaTabellone(20, 100, 0);
tabellone[4] = new CasellaTabellone(100, 100, 0);
tabellone[5] = new CasellaTabellone(180, 100, 0);
// terza riga
tabellone[6] = new CasellaTabellone(20, 180, 0);
tabellone[7] = new CasellaTabellone(100, 180, 0);
tabellone[8] = new CasellaTabellone(180, 180, 0);
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
add(tabellone[i].text);
}
turno_giocatore = true;
}
override public function update():void
{
// se è il turno del giocatore
if (partita == 0)
{
if (turno_giocatore)
{
if (FlxG.mouse.justPressed())
{
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
if ((FlxG.mouse.x > tabellone[i].rect.x) && (FlxG.mouse.x < (tabellone[i].rect.x + tabellone[i].rect.width)))
{
if ((FlxG.mouse.y > tabellone[i].rect.y) && (FlxG.mouse.y < (tabellone[i].rect.y + tabellone[i].rect.height)))
{
if (tabellone[i].stato == 0)
{
tabellone[i].stato = 1;
tabellone[i].text.text = "X";
turno_giocatore = false;
Controllo();
}
}
}
}
}
}
else
{
// qui viene implementato un algoritmo molto semplice per gestire l'avversario. si punta a controllare
// la casella centrale, quindi quelle diagonali ed infine quelle laterali (sopra,sotto,destra,sinistra)
if (tabellone[4].stato == 0)
{
tabellone[4].stato = 2;
tabellone[4].text.text = "O";
}
else
{
if (tabellone[0].stato == 0)
{
tabellone[0].stato = 2;
tabellone[0].text.text = "O";
} else if (tabellone[2].stato == 0)
{
tabellone[2].stato = 2;
tabellone[2].text.text = "O";
} else if (tabellone[6].stato == 0)
{
tabellone[6].stato = 2;
tabellone[6].text.text = "O";
} else if (tabellone[8].stato == 0)
{
tabellone[8].stato = 2;
tabellone[8].text.text = "O";
}
else
{
if (tabellone[1].stato == 0)
{
tabellone[1].stato = 2;
tabellone[1].text.text = "O";
} else if (tabellone[3].stato == 0)
{
tabellone[3].stato = 2;
tabellone[3].text.text = "O";
} else if (tabellone[5].stato == 0)
{
tabellone[5].stato = 2;
tabellone[5].text.text = "O";
} else if (tabellone[7].stato == 0)
{
tabellone[7].stato = 2;
tabellone[7].text.text = "O";
}
}
}
Controllo();
turno_giocatore = true;
}
}
else
{
if (FlxG.keys.justPressed("ENTER"))
{
for (var c:int = 0; c < tabellone.length; c = c + 1)
{
tabellone[c].stato = 0;
tabellone[c].text.text = "";
turno_giocatore = true;
info_text.text = "Partita in Corso";
subinfo_text.text = "";
partita = 0;
}
}
}
}
private function Controllo():void
{
// controllo combinazioni vincenti
// orizzontali
if ((tabellone[0].stato == 1) && (tabellone[1].stato == 1) && (tabellone[2].stato == 1)) partita = 1;
if ((tabellone[3].stato == 1) && (tabellone[4].stato == 1) && (tabellone[5].stato == 1)) partita = 1;
if ((tabellone[6].stato == 1) && (tabellone[7].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
// verticali
if ((tabellone[0].stato == 1) && (tabellone[3].stato == 1) && (tabellone[6].stato == 1)) partita = 1;
if ((tabellone[1].stato == 1) && (tabellone[4].stato == 1) && (tabellone[7].stato == 1)) partita = 1;
if ((tabellone[2].stato == 1) && (tabellone[5].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
// diagonali
if ((tabellone[0].stato == 1) && (tabellone[4].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
if ((tabellone[2].stato == 1) && (tabellone[4].stato == 1) && (tabellone[6].stato == 1)) partita = 1;
// controllo combinazioni perdenti
// orizzontali
if ((tabellone[0].stato == 2) && (tabellone[1].stato == 2) && (tabellone[2].stato == 2)) partita = 2;
if ((tabellone[3].stato == 2) && (tabellone[4].stato == 2) && (tabellone[5].stato == 2)) partita = 2;
if ((tabellone[6].stato == 2) && (tabellone[7].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
// verticali
if ((tabellone[0].stato == 2) && (tabellone[3].stato == 2) && (tabellone[6].stato == 2)) partita = 2;
if ((tabellone[1].stato == 2) && (tabellone[4].stato == 2) && (tabellone[7].stato == 2)) partita = 2;
if ((tabellone[2].stato == 2) && (tabellone[5].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
// diagonali
if ((tabellone[0].stato == 2) && (tabellone[4].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
if ((tabellone[2].stato == 2) && (tabellone[4].stato == 2) && (tabellone[6].stato == 2)) partita = 2;
switch(partita)
{
case 1:
info_text.text = "HAI VINTO!";
subinfo_text.text = "Premi Invio per Ricominciare.";
break;
case 2:
info_text.text = "HAI PERSO!";
subinfo_text.text = "Premi Invio per Ricominciare.";
break;
}
}
}
}
In queste 220 righe di codice c’è tutto il nostro gioco.
Come al solito, partiamo con l’analisi delle import. Dovendo creare un nuovo stato, la prima classe da importare sarà FlxState. Le altre classi importate sono la solita FlxText e FlxG. Non ci importa di altro, è quello che ci serve e basta.
Proprio come avevamo fatto per IntroState, quindi, dichiariamo l’inizio della nuova classe GameState, estensione di FlxState, con la sintassi
public class GameState extends FlxState
Dopodiché possiamo iniziare a vedere i vari campi della classe. Innanzitutto troviamo “tabellone”, che per adesso viene identificato come un array. Il nostro scopo è quello di usare questo array per identificare un insieme di oggetti di tipo CasellaTabellone, in modo tale da poterli gestire uno ad uno e creare le nove caselle che ci servono.
Successivamente dichiariamo due altre variabili, di uso esclusivamente legato alla logica:
- partita, un intero che identifica la situazione attuale di gioco: se uguale a zero allora la partita è in corso, se uguale ad uno il giocatore ha vinto e altrimenti, se uguale a due, il giocatore ha perso;
- turno_giocatore: nel caso la partita sia in corso, se è uguale a true allora è il turno del giocatore. In tal caso l’utente potrà scegliere una casella vuota dove posizionare il proprio simbolo. Altrimenti sarà il turno del computer.
Gli altri tre oggetti che troviamo sono oggetti legati all’aspetto grafico del gioco: precisamente infatti abbiamo la grafica del tabellone (l’oggetto tab_bg di tipo TabelloneGraphic, di cui ho parlato nella lezione precedente) e due oggetti FlxText che servono per comunicare al giocatore la situazione di gioco attuale: precisamente, info_text si occuperà di comunicare se la partita è in corso oppure c’è un vincitore, mentre subinfo_text servirà a scrivere, nel caso la partita sia finita, “Premere Invio per Ricominciare”.
Tutto qua, spero di essere stato chiaro fino a questo punto.
Ora dobbiamo passare avanti, analizzando finalmente i metodi della classe.
Iniziamo dal metodo “create”:
override public function create():void
{
FlxG.mouse.show();
tabellone = new Array();
tab_bg = new TabelloneGraphic();
add(tab_bg);
info_text = new FlxText(5, 250, 280, "Partita In Corso");
info_text.size = 20;
add(info_text);
subinfo_text = new FlxText(5, 275, 280, "");
subinfo_text.size = 10;
add(subinfo_text);
// VALORI TABELLONE:
// 0. Vuoto
// 1. X
// 2. O
// prima riga
tabellone[0] = new CasellaTabellone(20, 20, 0);
tabellone[1] = new CasellaTabellone(100, 20, 0);
tabellone[2] = new CasellaTabellone(180, 20, 0);
// seconda riga
tabellone[3] = new CasellaTabellone(20, 100, 0);
tabellone[4] = new CasellaTabellone(100, 100, 0);
tabellone[5] = new CasellaTabellone(180, 100, 0);
// terza riga
tabellone[6] = new CasellaTabellone(20, 180, 0);
tabellone[7] = new CasellaTabellone(100, 180, 0);
tabellone[8] = new CasellaTabellone(180, 180, 0);
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
add(tabellone[i].text);
}
turno_giocatore = true;
}
Il metodo “create” ha pochi e semplici compiti. Per prima cosa, si occupa di inizializzare i vari oggetti e variabili. Dopodiché provvede a popolare l’array “tabellone” con oggetti di tipo “CasellaTabellone”, in modo tale da avere proprio quello che ci serve.
Ma andiamo con ordine. L’istruzione
FlxG.mouse.show();
serve a mostrare il cursore del mouse, dato che lo dovremo usare nel gioco. Successivamente abbiamo l’inizializzazione degli oggetti che compongono il nostro stato: eviterò di descrivere in dettaglio la cosa in quanto sono tutte cose già viste e semplici. Si tratta, infatti, di eseguire l’add per i vari oggetti e nulla di più.
Nota: il testo di subinfo_text è inizialmente impostato come una stringa vuota, in quanto il messaggio “Premere Invio per Ricominciare” deve essere mostrato solo in caso di vincita o perdita della partita.
Subito dopo, quindi, ci occupiamo di popolare l’array del tabellone. Vi riporto un esempio di “popolamento” giusto per farvi capire, in modo tale da lasciare a voi il resto per la comprensione. Non è niente di che, non vi preoccupate
tabellone[0] = new CasellaTabellone(20, 20, 0);
“tabellone[0]” indica che stiamo appunto assegnando qualcosa di nuovo al primo elemento dell’array. Ciò che assegnamo è dall’altra parte dell’uguale: un nuovo oggetto di tipo “CasellaTabellone”, nella posizione x = 20, y = 20 e impostata inizialmente come vuota (il terzo parametro è lo stato della casella, ricordatelo).
Se vi state chiedendo come mai ho usato quei valori per le x e per le y non c’è niente di eccezionale da sapere. Ho semplicemente fatto il calcolo mentre disegnavo le due linee del tabellone in photoshop
D’altronde per iniziare una cosa più spartana di così non si può
Torniamo a noi: le ultime istruzioni sono le seguenti:
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
add(tabellone[i].text);
}
turno_giocatore = true;
Il ciclo for serve a scandire uno ad uno tutti gli elementi del tabellone, permettendo così di aggiungerli alla scena tramite il metodo add (altrimenti non avremmo visto niente). Impostando la variabile “turno_giocatore” su true decidiamo quindi che sarà sempre il giocatore ad iniziare per primo.
Per il metodo “create” abbiamo concluso. Ora possiamo passare al metodo di “update”. Di seguito il codice completo.
override public function update():void
{
if (partita == 0)
{
// se è il turno del giocatore
if (turno_giocatore)
{
if (FlxG.mouse.justPressed())
{
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
if ((FlxG.mouse.x > tabellone[i].rect.x) && (FlxG.mouse.x < (tabellone[i].rect.x + tabellone[i].rect.width)))
{
if ((FlxG.mouse.y > tabellone[i].rect.y) && (FlxG.mouse.y < (tabellone[i].rect.y + tabellone[i].rect.height)))
{
if (tabellone[i].stato == 0)
{
tabellone[i].stato = 1;
tabellone[i].text.text = "X";
turno_giocatore = false;
Controllo();
}
}
}
}
}
}
else
{
// qui viene implementato un algoritmo molto semplice per gestire l'avversario. si punta a controllare
// la casella centrale, quindi quelle diagonali ed infine quelle laterali (sopra,sotto,destra,sinistra)
if (tabellone[4].stato == 0)
{
tabellone[4].stato = 2;
tabellone[4].text.text = "O";
}
else
{
if (tabellone[0].stato == 0)
{
tabellone[0].stato = 2;
tabellone[0].text.text = "O";
} else if (tabellone[2].stato == 0)
{
tabellone[2].stato = 2;
tabellone[2].text.text = "O";
} else if (tabellone[6].stato == 0)
{
tabellone[6].stato = 2;
tabellone[6].text.text = "O";
} else if (tabellone[8].stato == 0)
{
tabellone[8].stato = 2;
tabellone[8].text.text = "O";
}
else
{
if (tabellone[1].stato == 0)
{
tabellone[1].stato = 2;
tabellone[1].text.text = "O";
} else if (tabellone[3].stato == 0)
{
tabellone[3].stato = 2;
tabellone[3].text.text = "O";
} else if (tabellone[5].stato == 0)
{
tabellone[5].stato = 2;
tabellone[5].text.text = "O";
} else if (tabellone[7].stato == 0)
{
tabellone[7].stato = 2;
tabellone[7].text.text = "O";
}
}
}
Controllo();
turno_giocatore = true;
}
}
else
{
if (FlxG.keys.justPressed("ENTER"))
{
for (var c:int = 0; c < tabellone.length; c = c + 1)
{
tabellone[c].stato = 0;
tabellone[c].text.text = "";
turno_giocatore = true;
info_text.text = "Partita in Corso";
subinfo_text.text = "";
partita = 0;
}
}
}
}
Dunque, prima di commentare pezzo per pezzo il nostro codice, occorre spiegare come ho voluto strutturare la cosa. Come ben sapete la variabile “partita” si occupa di segnare lo stato attuale del gioco: se uguale a zero la partita è in corso, altrimenti viene mostrato il testo di vincita o di perdita e bisogna attendere la pressione del tasto Invio per ricominciare.
Nel caso la partita sia attiva ci sono delle istruzioni da compiere che vengono eseguite ad ogni frame:
- se è il turno del giocatore allora:
controllare se ha cliccato su una casella
se questa è vuota metterci il segno X e quindi passare il turno al computer
se questa è occupata non succede niente - se è il turno del computer allora:
calcolare la posizione della prossima O e quindi posizionare il simbolo sul tabellone
controllare un’eventuale vincita o perdita
Per quanto riguarda la gestione del computer ho deciso di evitare di perdermi in chiacchiere troppo complesse legate a reti neurali ed intelligenza artificiale. Certo, sarebbe senza dubbio interessante, ma ora come ora se dobbiamo creare il nostro primo gioco non è il caso di complicare troppo le cose.
Per questo motivo, il computer farà le seguenti valutazioni durante il suo turno:
controllo della casella centrale: se libera metterà lì la O;
se la casella centrale non è libera controllerà le caselle agli angoli, mettendo quindi la O alla prima libera;
se anche le diagonali non sono libere allora cercherà una casella libera tra quelle ai lati.
Come vedrete non è niente di troppo complesso, nemmeno da implementare a livello di codice. Con i primi test (ma anche prima) noterete subito che battere il computer è facilissimo: ho pensato che può essere una cosa accettabile considerati gli scopi di questo tutorial.
Passiamo quindi ad analizzare il codice nello specifico. Per prima cosa partiamo dal codice legato alla gestione dell’input dell’utente durante il suo turno:
if (partita == 0)
{
// se è il turno del giocatore
if (turno_giocatore)
{
if (FlxG.mouse.justPressed())
{
for (var i:int = 0; i < tabellone.length; i = i + 1)
{
if ((FlxG.mouse.x > tabellone[i].rect.x) && (FlxG.mouse.x < (tabellone[i].rect.x + tabellone[i].rect.width)))
{
if ((FlxG.mouse.y > tabellone[i].rect.y) && (FlxG.mouse.y < (tabellone[i].rect.y + tabellone[i].rect.height)))
{
if (tabellone[i].stato == 0)
{
tabellone[i].stato = 1;
tabellone[i].text.text = "X";
turno_giocatore = false;
Controllo();
}
}
}
}
}
}
Se la partita è in corso (partita = 0) è il turno del giocatore. Ad ogni frame verrà controllato il click del mouse, tramite l’istruzione
if (FlxG.mouse.justPressed())
nel caso la condizione si verifichi correttamente, si provvederà ad entrare in un ciclo for che scandirà, casella per casella, tutto il tabellone. Nel caso le coordinate del mouse saranno all’interno di una delle caselle, si passerà quindi a controllare se quella casella scelta è libera.
if (tabellone[i].stato == 0)
{
tabellone[i].stato = 1;
tabellone[i].text.text = "X";
turno_giocatore = false;
Controllo();
}
se libera (stato = 0) allora lo stato verrà portato ad 1, e la X verrà scritta nella casella, tramite l’assegnamento all’oggetto text della casella stessa.
“turno_giocatore” verrà impostato su false e verrà eseguito il metodo Controllo (che vedremo tra un po’) che si occupa di controllare la presenza di eventuali combinazioni vincenti (o perdenti) sul tabellone.
Chiarita la parte legata al turno del giocatore, passiamo al turno avversario.
else
{
// qui viene implementato un algoritmo molto semplice per gestire l'avversario. si punta a controllare
// la casella centrale, quindi quelle diagonali ed infine quelle laterali (sopra,sotto,destra,sinistra)
if (tabellone[4].stato == 0)
{
tabellone[4].stato = 2;
tabellone[4].text.text = "O";
}
else
{
if (tabellone[0].stato == 0)
{
tabellone[0].stato = 2;
tabellone[0].text.text = "O";
} else if (tabellone[2].stato == 0)
{
tabellone[2].stato = 2;
tabellone[2].text.text = "O";
} else if (tabellone[6].stato == 0)
{
tabellone[6].stato = 2;
tabellone[6].text.text = "O";
} else if (tabellone[8].stato == 0)
{
tabellone[8].stato = 2;
tabellone[8].text.text = "O";
}
else
{
if (tabellone[1].stato == 0)
{
tabellone[1].stato = 2;
tabellone[1].text.text = "O";
} else if (tabellone[3].stato == 0)
{
tabellone[3].stato = 2;
tabellone[3].text.text = "O";
} else if (tabellone[5].stato == 0)
{
tabellone[5].stato = 2;
tabellone[5].text.text = "O";
} else if (tabellone[7].stato == 0)
{
tabellone[7].stato = 2;
tabellone[7].text.text = "O";
}
}
}
Controllo();
turno_giocatore = true;
}
}
Per il turno avversario il grosso del lavoro si trova in questo blocco if – else if, che ripercorre il ragionamento logico fatto inizialmente: il controllo della casella centrale, quelle all’angolo ed infine quelle ai lati. L’eventuale casella libera viene controllata con una comparazione della variabile stato (se stato è uguale a zero allora la casella è libera) e nel caso sia libera l’assegnazione viene eseguita mediante due istruzioni (di seguito un esempio):
tabellone[7].stato = 2;
tabellone[7].text.text = “O”;
nulla di più, nulla di meno. Anche in questo caso viene eseguito il metodo “Controllo” per verificare le eventuali combinazioni vincenti e la variabile “turno_giocatore” viene reimpostata su true, per far continuare la partita.
Non dobbiamo scordarci inoltre che dobbiamo gestire anche il caso in cui la partita sia finita: ecco il codice che ci serve.
else
{
if (FlxG.keys.justPressed("ENTER"))
{
for (var c:int = 0; c < tabellone.length; c = c + 1)
{
tabellone[c].stato = 0;
tabellone[c].text.text = "";
turno_giocatore = true;
info_text.text = "Partita in Corso";
subinfo_text.text = "";
partita = 0;
}
}
}
}
Quello che facciamo dovreste averlo capito: aspettiamo che l’utente prema il tasto Invio per ricominciare la partita. Appena lo fa il tabellone viene resettato (impostando per ogni casella lo stato a zero e il testo come una stringa vuota) insieme agli oggetti FlxText e alla variabile partita, che viene riportata a zero (partita uguale a zero = partita in corso, ricordatelo
)
Forza, siamo quasi alla fine. Tutto quello che ci rimane ora è definire il metodo Controllo, che deve verificare le cosidette combinazioni vincenti: le tre in orizzontale, le tre in verticale e le due in diagonale.
Et voilà il codice per voi:
private function Controllo():void
{
// controllo combinazioni vincenti
// orizzontali
if ((tabellone[0].stato == 1) && (tabellone[1].stato == 1) && (tabellone[2].stato == 1)) partita = 1;
if ((tabellone[3].stato == 1) && (tabellone[4].stato == 1) && (tabellone[5].stato == 1)) partita = 1;
if ((tabellone[6].stato == 1) && (tabellone[7].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
// verticali
if ((tabellone[0].stato == 1) && (tabellone[3].stato == 1) && (tabellone[6].stato == 1)) partita = 1;
if ((tabellone[1].stato == 1) && (tabellone[4].stato == 1) && (tabellone[7].stato == 1)) partita = 1;
if ((tabellone[2].stato == 1) && (tabellone[5].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
// diagonali
if ((tabellone[0].stato == 1) && (tabellone[4].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
if ((tabellone[2].stato == 1) && (tabellone[4].stato == 1) && (tabellone[6].stato == 1)) partita = 1;
// controllo combinazioni perdenti
// orizzontali
if ((tabellone[0].stato == 2) && (tabellone[1].stato == 2) && (tabellone[2].stato == 2)) partita = 2;
if ((tabellone[3].stato == 2) && (tabellone[4].stato == 2) && (tabellone[5].stato == 2)) partita = 2;
if ((tabellone[6].stato == 2) && (tabellone[7].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
// verticali
if ((tabellone[0].stato == 2) && (tabellone[3].stato == 2) && (tabellone[6].stato == 2)) partita = 2;
if ((tabellone[1].stato == 2) && (tabellone[4].stato == 2) && (tabellone[7].stato == 2)) partita = 2;
if ((tabellone[2].stato == 2) && (tabellone[5].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
// diagonali
if ((tabellone[0].stato == 2) && (tabellone[4].stato == 2) && (tabellone[8].stato == 2)) partita = 2;
if ((tabellone[2].stato == 2) && (tabellone[4].stato == 2) && (tabellone[6].stato == 2)) partita = 2;
switch(partita)
{
case 1:
info_text.text = "HAI VINTO!";
subinfo_text.text = "Premi Invio per Ricominciare.";
break;
case 2:
info_text.text = "HAI PERSO!";
subinfo_text.text = "Premi Invio per Ricominciare.";
break;
}
}
Effettuare un controllo è semplice, dato che abbiamo ogni casella numerata da zero ad otto. Per controllare, per esempio, la prima riga, sarà sufficiente usare la seguente istruzione:
if ((tabellone[0].stato == 1) && (tabellone[1].stato == 1) && (tabellone[2].stato == 1)) partita = 1;
oppure ancora, per controllare la diagonale che va da sinistra in alto verso destra in basso dovremo usare:
if ((tabellone[0].stato == 1) && (tabellone[4].stato == 1) && (tabellone[8].stato == 1)) partita = 1;
Così come per le combinazioni vincenti, il gioco controlla anche le combinazioni perdenti nello stesso identico modo: basterà verificare che lo stato delle caselle interessate sia uguale a 2. Lo switch posto alla fine serve a cambiare il testo in basso in base al risultato della partita.
Salviamo tutto, compiliamo e… abbiamo completato il nostro tris
TicTacToe con Flixel – Parte 4 – Due Classi di Supporto
giu 6
Bene! Abbiamo realizzato la nostra prima schermata, anche per avere un’idea più precisa di come stanno le cose con Flixel. Ora dobbiamo iniziare a pensare di creare la schermata di gioco vera e propria. Volendo fare una cosa molto, molto semplice, la schermata di gioco avrà un aspetto del genere:
Il tabellone (ovvero quelle due linee bianche) sarà creato caricando un’immagine di background. Per quanto riguarda le O e le X invece avremo delle caselle di testo, implementate in classi apposite che spiegherò a breve.
Per il testo “Partita in Corso” (che muterà in “HAI VINTO!” o “HAI PERSO!” a seconda dei casi) si tratta di un semplice testo, che abbiamo già visto nella lezione precedente.
Ora che abbiamo le idee più chiare possiamo proseguire. In questo articolo, prima di scrivere il codice del gioco vero e proprio, ci occuperemo di creare due classi di supporto: una per il tabellone di gioco ed una per disegnare un’immagine su schermo.
Per prima cosa, pensiamo ad oggetti. Un tabellone da cosa è composto? Da caselle. Possiamo quindi ricondurre la creazione del tabellone ad un insieme di caselle. Qualcuno ha detto array di caselle? Bingo.
Analizziamo ancora meglio dal punto di vista concettuale un eventuale oggetto Casella. Cosa deve possedere
- Delle coordinate x ed y, dove disegnare il simbolo corrispondente;
- Una variabile (di stato) che ci dica se la casella è vuota oppure occupata da un simbolo;
- Ovviamente un oggetto testo, che sia vuoto se la casella è vuota oppure che mostri la X o la O a seconda della variabile di stato;
- Un oggetto che “delimiti” la casella, in modo tale da riconoscere il click dell’utente.
Adesso proviamo a tradurre tutto in codice. Creiamo una nuova classe e diamole come nome “CasellaTabellone.as”. Il codice è qui di seguito.
package
{
import flash.geom.Rectangle;
import org.flixel.FlxText;
public class CasellaTabellone
{
public var x:int;
public var y:int;
public var stato:int;
public var text:FlxText;
public var rect:Rectangle;
public function CasellaTabellone(x1:int, y1:int, stato1:int)
{
x = x1;
y = y1;
stato = stato1;
text = new FlxText(x1, y1, 50, "");
text.size = 40;
rect = new Rectangle(x1, y1, 50, 50);
}
}
}
Per prima cosa, le import: oltre ad importare la classe FlxText stavolta dobbiamo prendere anche la classe flash.geom.Rectangle, che ci servirà a delimitare l’area di ogni singola casella.
Passiamo adesso alla classe vera e propria: i campi sono gli stessi che avevamo definito concettualmente: x ed y sono le coordinate (rappresentate con variabili di tipo int), lo stato della casella viene identificato con una variabile di nome “stato” di tipo int.
Nota: per regolarmi ho deciso di usare la seguente leggenda: la variabile stato assumerà il valore 0 se vuota, 1 se occupata da una X e 2 se occupata da una O.
Abbiamo quindi i due oggetti text e rect, rispettivamente oggetti di tipo FlxText e Rectangle. Prima di analizzare il costruttore una piccola riflessione: avrei potuto ottimizzare le cose evitando di usare le variabili x ed y, considerando che l’oggetto Rectangle già dispone di due coordinate x ed y, oltre alla larghezza e altezza
Ora, il codice del costruttore:
public function CasellaTabellone(x1:int, y1:int, stato1:int)
{
x = x1;
y = y1;
stato = stato1;
text = new FlxText(x1, y1, 50, "");
text.size = 40;
rect = new Rectangle(x1, y1, 50, 50);
}
Al costruttore passiamo tre argomenti: la coordinata x, la coordinata y e lo stato iniziale della casella. Successivamente inizializziamo l’oggetto text e gli diamo una grandezza pari a 40, tramite il campo “size”. Infine inizializziamo anche l’oggetto rect, usando come coordinate quelle già specificate nel costruttore e usando una larghezza fissa di 50 per ogni casella.
Prima di passare al gioco vero e proprio aspettate ancora un po’, in quanto ora andremo ad implementare l’immagine del tabellone precedentemente preparata. La trovate qui di seguito:
Per usarla nel codice dovremo creare una nuova classe per questa immagine: creiamo un nuovo file actionscript e chiamiamolo “TabelloneGraphic.as”. Il contenuto del file sarà il seguente:
package
{
import org.flixel.FlxSprite;
public class TabelloneGraphic extends FlxSprite
{
[Embed(source='../files/bg.jpg')]
private var imggraph:Class;
public function TabelloneGraphic()
{
loadGraphic(imggraph, false, false, 640, 480, true);
}
}
}
C’è ben poco da dire: la classe creata estende la già presente FlxSprite, di Flixel. L’istruzione
[Embed(source='../files/bg.jpg')] private var imggraph:Class;
spiega che bisogna incorporare il file specificato dal percorso e il suo contenuto deve essere utilizzabile tramite l’oggetto imggraph, di tipo generico “Class”. Insomma, sono dei dati portati nel codice, pronti per essere usati.
Nel costruttore quindi carichiamo l’immagine in maniera definitiva, tramite l’istruzione
loadGraphic(imggraph, false, false, 300, 300, true);
- Il primo parametro è l’oggetto di tipo class da importare.
- Il secondo, di tipo boolean, indica se l’immagine sarà animata durante il gioco oppure no.
- Il terzo, sempre di tipo boolean, indica se l’immagine deve subire l’effetto di reverse (un effetto tipo specchio, per capirci).
- Il quarto e il quinto servono a stabilire larghezza e altezza dell’immagine.
- Il sesto invece serve al sistema per capire se c’è trasparenza nell’immagine oppure no.
Possiamo quindi salvare tutto e, finalmente, concentrarci sulla classe di gioco vera e propria.
TicTacToe con Flixel – Parte 3 – IntroState
giu 5
In questo breve articolo, parte del nostro tutorial dedicato alla realizzazione del gioco del tris con Flixel, inizieremo a scrivere qualcosa sullo schermo utilizzando gli strumenti messi a disposizione dal framework.
Il gioco avrà una struttura molto semplice: una volta caricato mostrerà una schermata introduttiva e poi ci porterà direttamente al gioco vero e proprio. Insomma, realizzeremo due “schermate”: la schermata introduttiva e la schermata di gioco.
Ora, il tutto si può tradurre nella creazione di due “stati” del gioco. (Chi ha studiato gli automi potrà capirmi, altrimenti pensate appunto al concetto di schermata.) Come possiamo creare quindi queste nuove entità? In modo decisamente semplice.
Nella schermata di esplorazione del nostro progetto, aggiungiamo una nuova classe actionscript nella stessa cartella delle altre due. Questa volta la chiameremo “IntroState.as”. Rappresenterà la nostra schermata iniziale, che apparirà al giocatore in questo modo:
Ora che avete un’idea più chiara iniziamo a lavorare sul codice, in modo tale da ottenere il risultato voluto. Per prima cosa vi riporto come al solito tutto il listato, per poi commentarlo linea per linea. Se dovesse esserci qualcosa di poco chiaro fatemelo presente
package
{
import org.flixel.FlxState;
import org.flixel.FlxText;
import org.flixel.FlxG;
public class IntroState extends FlxState
{
public var intro_text:FlxText;
public var info_text:FlxText;
public var cred_text:FlxText;
override public function create():void
{
intro_text = new FlxText(10, 10, 280, "tic\ntac\ntoe.");
intro_text.size = 40;
add(intro_text);
info_text = new FlxText(55, 270, 280, "Press Enter to Play");
info_text.size = 20;
add(info_text);
cred_text = new FlxText(15, 230, 280, "made by admin\n@francescomalatesta.net");
cred_text.size = 12;
cred_text.alignment = "right";
add(cred_text);
FlxG.flash.start(0xffffffff, 1);
}
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.state = new GameState;
}
}
}
}
La prima cosa che potete notare è che abbiamo importato varie componenti delle librerie Flixel. Nello specifico qui abbiamo tre import: FlxState, FlxText e FlxG. La prima è la classe che rappresenta uno “stato” del gioco: il nostro IntroState si basa proprio su questa classe. La seconda invece è dedicata alla gestione del testo e alla sua stampa su schermo. La terza invece presenta svariate features dedicate alle più disparate funzionalità: dall’input ad alcuni effetti speciali.
Un’altra cosa che vorrei fare presente prima di continuare è che è opportuno notare l’assenza di un metodo costruttore, in questa classe. Abbiamo però due override, sia per il metodo “create” che per il metodo “update”. La domanda quindi può sorgere spontanea: perché scrivere del codice in create e non in update e viceversa?
Semplicemente, il metodo “create” prepara tutto quello che c’è da preparare prima di entrare in azione. Una volta che tutto è al suo posto, quindi, viene eseguito il metodo “update” ad ogni frame, in modo tale da tenere sempre aggiornato il programma. Nel caso del nostro IntroState, infatti, ad ogni frame controlleremo l’eventuale pressione del tasto Invio per passare alla schermata successiva.
Bene, passiamo al codice. Per prima cosa abbiamo:
public class IntroState extends FlxState
{
public var intro_text:FlxText;
public var info_text:FlxText;
public var cred_text:FlxText;
che è decisamente semplice da capire. Tutto quello che facciamo qua è creare una nuova classe di nome IntroState, estensione di FlxState. Dentro questa classe inoltre creiamo tre oggetti testo, che verranno stampati sullo schermo. Rifacendovi alla schermata precedente potete infatti distinguere tre blocchi di testo:
- il primo, della scritta “tic tac toe”;
- il secondo, che dice di premere il tasto Invio per cominciare;
- il terzo, che rappresenta dei brevissimi credits
Una volta dichiarati questi oggetti possiamo passare al metodo “create”. Ecco qui di seguito il codice:
override public function create():void
{
intro_text = new FlxText(10, 10, 280, "tic\ntac\ntoe.");
intro_text.size = 40;
add(intro_text);
info_text = new FlxText(55, 270, 280, "Press Enter to Play");
info_text.size = 20;
add(info_text);
cred_text = new FlxText(15, 230, 280, "made by admin\n@francescomalatesta.net");
cred_text.size = 12;
cred_text.alignment = "right";
add(cred_text);
FlxG.flash.start(0xffffffff, 1);
}
Anche qui abbiamo fatto veramente poche cose. Prendiamo le prime tre righe in esame:
intro_text = new FlxText(10, 10, 280, “tic\ntac\ntoe.”); – tramite questa istruzione inizializziamo un nuovo oggetto di tipo FlxText, che dovrà essere posizionato alle coordinate 10, 10, avrà una larghezza di 280 pixel (ciò vuol dire che il ritorno a capo è totalmente automatizzato) e presenterà il testo “tic tac toe.”. Il simbolo “\n” che vedete tra una parola e l’altra è il carattere speciale di ritorno a capo. Nella schermata infatti dopo ogni parola di questo blocco si va a capo.
intro_text.size = 40; – qua non c’è quasi bisogno di nessuna spiegazione. Quello che abbiamo fatto qui è stato semplicemente assegnare al testo una grandezza pari a 40 punti. Flixel permette di gestire con estrema facilità molti aspetti del testo: basti pensare a parametri come, appunto, size oppure color.
add(intro_text); – con quest’ultima istruzione diciamo di disegnare a schermo il nostro testo. Un po’ come l’addChild, per intenderci.
Vi è sembrato difficile? Beh, vi assicuro che non lo è assolutamente. Smanettate con i valori, dateci dentro, sperimentate ed in poco tempo imparerete ad usare bene il testo con Flixel. Così come abbiamo fatto per intro_text, la procedura è stata ripetuta in maniera uguale per gli altri due oggetti, info_text e cred_text.
Come “esercizio” vi lascio immaginare a cosa mai possa servire il comando “cred_text.alignment = “right”;”
Infine, per concludere in bellezza il metodo create, abbiamo l’ultima istruzione:
FlxG.flash.start(0xffffffff, 1);
Che cosa sarà mai? A mio parere questa è una delle più belle comodità offerte dal framework. Nello specifico, FlxG.flash permette di dare allo schermo un effetto di flash del colore che si vuole, per la durata desiderata. Come parametri in questo caso ho dato:
0xffffffff – ovvero 0x seguito dal codice in esadecimale del colore bianco (ffffff) seguito da altre due lettere per indicare il valore del canale alpha (ff, quindi nessuna trasparenza).
1 – indica la durata del flash, in secondi
Spero non sia troppo difficile da capire. Ovviamente c’è un motivo ben preciso per posizionare il codice proprio lì: dato che le istruzioni contenute in “create” vengono eseguite all’inizio, il nostro flash verrà sparato come se dovesse introdurre la nostra schermata. Niente male per una sola riga di codice, vero?
Ora che abbiamo finito il codice per il metodo create possiamo passare al metodo update. Come detto precedentemente, le istruzioni di questo metodo vengono eseguite ad ogni frame del nostro gioco. Ricordate di ragionare in quest’ottica.
override public function update():void
{
if (FlxG.keys.justPressed("ENTER"))
{
FlxG.state = new GameState;
}
}
Tutto quello che viene fatto qui è presto detto: controlliamo la pressione del tasto Invio e, se verificata, passiamo alla schermata di gioco. Traducendo il tutto a livello di codice vuol dire che assegnamo al nostro gioco un nuovo stato. In questo caso GameState, che non ancora abbiamo definito a livello di codice, che rappresenta la schermata di gioco vera e propria.
Salvate tutte le modifiche e tornate un secondo alla classe principale, “TicTacToeGame.as”: cambiate il terzo argomento passato al metodo super, da “null” diventa “IntroState”. In questo modo diremo al programma che deve partire dalla schermata di introduzione.
Il risultato potete osservarlo nella schermata sottostante.
Alla prossima lezione e grazie per l’ascolto
TicTacToe con Flixel – Parte 2 – Ambiente di Sviluppo, Preparazione e Prima Finestra
giu 5
Bene, dopo aver speso due parole per descrivere brevemente il nostro gioco, dobbiamo preparare l’ambiente di sviluppo con il quale lavoreremo. In poche parole, vi spiegherò brevemente come scaricare, installare ed impostare i programmi e le librerie adatte.
Alcuni di voi sapranno già che Flixel non funziona con Adobe Flash. Nel caso non ne siate a conoscenza, ora ne siete al corrente. Per lavorare con questo prezioso framework bisogna usare un programma come FlashDevelop, un IDE gratuito, facile da installare e da scaricare.
Per prima cosa, ecco i passi da fare:
- Scaricare ed Installare FlashDevelop
- Scaricare il Flex SDK
- Scaricare il Framework Flixel 2.0
- Aggiungere i riferimenti al Flex SDK e a Flixel dall’ambiente di sviluppo.
In un articolo di qualche mese fa ho già descritto in maniera piuttosto dettagliata come scaricare ed installare il caro FlashDevelop. Per questo motivo vi rimando all’articolo interessato (http://francescomalatesta.net/2010/04/11/installare-flashdevelop-preparazione/) e possiamo proseguire con il passo successivo.
Scaricare il Flex SDK (con Flixel va bene la versione 3.4) è decisamente facile. Per prima cosa andiamo sulla pagina ad esso dedicata sul sito Adobe (http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+3), selezioniamo la versione che ci interessa (al momento in cui scrivo ho scaricato ed uso la 3.4.1.10084, tra le Stable Builds), scorriamo la pagina che ci viene proposta e, dopo aver acconsentito cliccando su “I Agree” ci verrà mostrato il link per il download.
Dopo circa un centinaio di megabyte avremo concluso questa fase. Prendete l’archivio zip scaricato ed estraete i suoi contenuti in una cartella facile da rintracciare. Per farvi un esempio, io ho creato una cartella “libs” nella cartella principale del mio hard disk e qui metto in cartelle separate tutte le varie librerie.
Per quanto riguarda l’SDK ci siamo quasi. Avviamo FlashDevelop ed apriamo il menù delle opzioni e preferenze dell’ambiente, cliccando su “Tools->Program Settings”. Selezionate la scheda “AS3Context” e cercate la voce “FlexSDK Location”. Sarà qui che dovrete specificare la cartella dove avete posizionato l’SDK.
Cliccate su close e chiudete anche l’IDE. Adesso è il turno di Flixel. Per scaricare Flixel andate a questo indirizzo (http://flixel.org/) e cliccate su “Standard” subito sotto la scritta “Download”. Dopo aver scaricato l’archivio la procedura è la stessa per il FlexSDK: create un’altra cartella nella cartella “libs” (io l’ho chiamata “flixel”, viva l’originalità) ed estraeteci il contenuto del file.
Adesso che tutto è pronto possiamo procedere con la creazione di un nuovo progetto con FlashDevelop. Innanzitutto, avviamo il programma e clicchiamo su “Project → New Project”. Diamo il nome al progetto (che sarà di tipologia Actionscript 3 senza preloader), confermiamo e ci ritroveremo davanti al nostro file principale, con una bozza dei metodi essenziali già pronti.
Mi pare chiaro che dovete cancellare tutto.
Per prima cosa rinominate il file principale del progetto (non è obbligatorio ma ve lo consiglio): da “Main.as” chiamatelo “TicTacToeGame.as”, come ho fatto io. Da qui parte tutto quanto, dato che abbiamo il metodo Main, ovvero il punto d’ingresso del programma.
Quello che faremo ora sarà aggiungere il secondo riferimento, quello di Flixel. È tutto molto semplice e veloce: nella finestra di esplorazione del progetto (che trovate a destra di default) cliccate con il pulsante destro del mouse sulla cartella principale del progetto e cliccate quindi su “Properties”.
Vi si aprirà una piccola finestra delle impostazioni. Selezionate la scheda “Classpaths” e nella lista che vi troverete di fronte aggiungete anche il percorso del framework Flixel, sul vostro hard disk. Se le cose funzioneranno allora nella finestra di esplorazione del progetto le cose cambieranno leggermente:
Adesso che abbiamo tutti i riferimenti non dobbiamo fare altro che scrivere le prime linee di codice. Ecco come appare, una volta modificato, il file TicTacToeGame.as:
package
{
import org.flixel.*;
[SWF(width = "300", height = "300", backgroundColor = "#ffffff")]
[Frame(factoryClass="Preloader")]
public class TicTacToeGame extends FlxGame
{
public function TicTacToeGame()
{
super(300, 300, null, 1);
}
}
}
Non vi spaventate, tranquilli. Spiego tutto riga per riga. La direttiva di import che troviamo all’inizio serve a dire a FlashDevelop che useremo flixel. L’asterisco alla fine indica che prenderemo in esame tutta la libreria: per ora lasciate le cose così, con la pratica imparerete cosa importare e cosa no a seconda delle necessità più varie.
La riga successiva descrive il file swf che andremo a creare. Avrà una grandezza di 300×300 pixel e avrà un colore di sfondo bianco. La riga successiva serve a dare delle informazioni sul preloader, ovvero il componente che si occuperà di gestire il caricamento del gioco prima di mostrarlo. Non vi preoccupate per ora, ne parleremo tra un po’.
Subito dopo abbiamo la definizione della classe:
public class TicTacToeGame extends FlxGame
la classe è TicTacToeGame (ha lo stesso nome del file che la ospita) ed estende la classe FlxGame, offerta da Flixel. Il contenuto del metodo è molto semplice e consiste in una sola istruzione:
super(300, 300, null, 1);
del metodo “super” ho già parlato in altri tutorial. Analizziamo invece gli argomenti passati: i primi due numeri, 300 e 300, sono la dimensione della finestra da utilizzare. Il terzo argomento è lo “stato” in cui il gioco si deve ritrovare all’avvio.
Per farvi capire meglio, prendete lo stato iniziale come, genericamente, la schermata da cui si parte. Un esempio potrebbe essere un menù principale, o un intro qualunque prima di passare al gioco vero e proprio. Per ora lo abbiamo impostato su null dato che non abbiamo ancora creato gli stati del gioco.
Quello che faremo ora, per questa prima lezione, sarà creare la finestra di gioco vuota dopo aver impostato l’ambiente di sviluppo.
L’ultimo argomento passato è il fattore di zoom: se impostato ad 1 vi farà vedere tutto normalmente, impostato a 2 raddoppierà la grandezza di tutto, portando il gioco ad avere un effetto ancora più “pixeloso”.
Dopo aver salvato il file creiamo un altro file Actionscript nella stessa cartella del file principale. Lo chiameremo “Preloader.as”. Il Preloader ha il compito di gestire il caricamento del gioco in attesa che sia totalmente scaricato sul pc, in locale. Può essere rappresentato in vari modi: generalmente è la schermata indicante la percentuale di caricamento di un qualsiasi gioco flash.
Come avete potuto vedere nella mia guida tradotta (qui l’articolo) realizzare un Preloader efficace e potente può essere molto, molto lungo e faticoso. Fortunatamente, Flixel ci mette a disposizione uno strumento già pronto, che sarà proprio quello che utilizzeremo.
Copiate questo codice nel file Preloader.as:
package
{
import org.flixel.FlxPreloader;
public class Preloader extends FlxPreloader
{
public function Preloader():void
{
className = "TicTacToeGame";
super();
}
}
}
La direttiva di import stavolta chiede di prendere tutti i dati riguardanti la classe FlxPreloader, quella che appunto ci offre Flixel. La classe quindi viene creata di conseguenza, come un’estensione di FlxPreloader ed il metodo costruttore non fa altro che prendere in input il nome della classe principale del gioco, quella da cui parte tutto, tramite la variabile “className”. In questo caso, “TicTacToeGame”.
Il metodo super chiude tutto. Salvate anche questo file. Ora potete cliccare su Build per compilare tutto e provare il prodotto finito. Ecco la nostra finestra bella pronta.
Anche questa parte è andata!!
Alla prossima!




Ultimi Commenti