1. Introduzione
  2. Iniziamo
  3. Nemici Multipli
  4. Game Over
  5. Punteggi e Orologio
  6. Tanti Piccoli Miglioramenti
  7. Usiamo la Tastiera
  8. Aggiungere un Preloader (Parte 1 - Parte 2)
  9. Aggiungere Musica ed Effetti Sonori
  10. Livelli Multipli
  11. Salvare e Caricare Informazioni
  12. Garbage Collection

In questa parte della guida dedicata alla realizzazione di un gioco completo di abilità in Flash / Actionscript3 vi mostrerò come aggiungere al vostro lavoro una schermata di Game Over, ovvero una schermata da mostrare in caso di fallimento da parte del giocatore. L’immagine qui di seguito mostra un esempio di ciò che faremo.

AvoiderGame_Part3_16

Mettiamoci all’opera.

Come già detto in precedenza, nella prima parte della nostra guida, utilizzeremo un solo frame per tutto il gioco. Questo vuol dire che non potremo inserire una semplice schermata di Game Over al secondo frame e utilizzare la funzione “nextFrame()” nel caso il personaggio muoia.

La soluzione a questo problema è semplice: posizioneremo un grande “Game Over” (si, una scritta) al di sopra della finestra di gioco, in modo tale che durante la partita non si veda. Nel caso il giocatore perda, allora l’immagine del Game Over scenderà, come un sipario.


Creiamo l’Overlay.

Bene, iniziamo. Se avete seguito le altre parti del tutorial, fate una copia di backup del lavoro svolto finora e aprite il file .FLA. Nel caso, invece, vogliate iniziare da qui, scaricate il file alla fine della Lezione 2.

Creiamo un nuovo filmato “Inserisci > Nuovo Simbolo” e chiamiamolo GameOverText, disegnandoci dentro qualcosa che ricordi al giocatore, o che comunque gli faccia capire, che il gioco è terminato. Ecco quello che abbiamo usato noi:

AvoiderGame_Part3_01

Il font utilizzato è Arial Black, è stato colorato in Dark Red per farlo emergere dal background Grigio. Fate attenzione al fatto che ho centrato il punto di registrazione al centro della scritta stessa. Se non sapete cosa voglia dire tutto questo, tornate alla prima lezione e seguite la parte relativa ai punti di registrazione :)

Se stiamo usando del testo, Flash potrebbe decidere che quest’ultimo debba essere “dinamico”. Questo vuol dire che, facendo delle modifiche lato codice, il testo può cambiare sullo schermo. Se è statico, invece, tutto questo non può avvenire.

Fin quando non inseriremo dei punteggi (e quindi avremo necessità dei testi dinamici) non abbiamo bisogno di questa caratteristica. Cliccando sul testo, cerchiamo tra le sue proprietà una lista e scegliamo “Testo Statico”. Altrimenti avremo un errore di questo tipo:

1046: Type was not found or was not a compile-time constant: TextField.

Adesso dobbiamo rendere il nostro nuovo simbolo disponibile al codice. Dalla libreria, clicchiamo con il pulsante destro del mouse sull’ultimo simbolo creato e apriamo la scheda delle proprietà. Tra le proprietà del concatenamento, mettiamo il segno di spunta su “Esporta per Actionscript” e segnamoci il nome della classe che ci suggerisce Flash (in questo caso dovrebbe essere “GameOverText”, lo stesso nome del simbolo.

Provvediamo quindi ad aprire la classe documento del nostro file nel modo che più vi piace. Cercate la famosa funzione “onTick” e quindi la porzione di codice in cui controlliamo la collisione con i nemici (che poi corrisponde al game over nel caso appunto ci sia il tocco). Attualmente il codice è il seguente:

if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
}

Lasciando le cose così capiamo bene quel che succede: tutto viene stoppato appena l’avatar entra in collisione con il nemico. Ora facciamo in modo che il nostro testo di Game Over appaia:
if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
  var gameOverText:GameOverText = new GameOverText();
  gameOverText.x = 200;
  gameOverText.y = 150;
  addChild( gameOverText );
}

Non abbiamo fatto niente di complicato, anzi questa procedura ormai dovrebbe risultarti familiare! Abbiamo creato un nuovo oggetto del tipo GameOverText, gli abbiamo dato una posizione centrale (ed in questo ci aiuta il punto di registrazione che ci evita calcoli inutili) e abbiamo addChild-ato (termine molto molto gergale :D ) il nostro simbolo al filmato principale.

Penso sia sottointeso che per cambiare la posizione del testo di Game Over basta modificare i valori x ed y illustrati poco sopra ;)

Salviamo, testiamo tutto come al solito e le cose dovrebbero andare a finire così:

AvoiderGame_Part3_209

Eccellente.


Qualcosa in più a questa Schermata!

Bene, il nostro Game Over funziona bene. Il tipo di schermata Overlay ha sempre avuto il suo perchè, come per esempio in giochi tipo Sonic. Ma cosa dovremmo fare se, invece di una semplice scritta, volessimo realizzare un intera schermata di Game Over?

Niente paura, non faremo cose molto diverse da quelle che abbiamo già fatto. Semplicemente, quando andremo a creare il simbolo per questa schermata, lo faremo grande tanto quanto la schermata di gioco. Creiamo un nuovo filmato, stavolta chiamandolo “GameOverScreen”. Facciamo un bel rettangolone nero, dandogli le stesse dimensioni della nostra finestra (400 x 300 per chi non dovesse ricordarlo) e piazziamoci una scritta bianca bella grande. “Game Over”, appunto.

Nota: per modificare le grandezze del rettangolo nero creato, come per tutti gli oggetti, basta cercare il pannello “Informazioni” e nello specifico la Larghezza e l’Altezza. Il pannello “Informazioni” è spesso una scheda affiancata al pannello “Allinea”. Fate attenzione ;)

Tornando a noi, stavolta cambieremo il punto di registrazione del nostro simbolo. Invece del centro assoluto, useremo l’angolo in alto a sinistra. Perché? Semplicemente non vogliamo curarci di quale sia il centro della schermata di gioco ed in questo modo ci basterà semplicemente inserire (0, 0) come coordinate della schermata di Game Over qualora volessimo mostrarla. Potete cambiare l’allineamento del punto di registrazione tramite il pannello “Allinea”.

AvoiderGame_Part3_03

Nota: nel caso non vi fosse chiaro il mio ragionamento sullo (0, 0) ricordatevi il discorso che feci qualche lezione addietro per il sistema di coordinate cartesiane.

AvoiderGame_Part3_05 Ed eccola qua, la nostra schermata.

Abbellite la schermata, se volete, e quando sarete pronti ripetete la procedura usata (su GameOverText) per esportare il simbolo nel codice. Non credo ci sia bisogno di rispiegarla :)

Apriamo la nostra classe documento ed ecco il codice che troveremo:

if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
  var gameOverText:GameOverText = new GameOverText();
  gameOverText.x = 200;
  gameOverText.y = 150;
  addChild( gameOverText );
}

Basteranno pochi semplici cambiamenti per rendere funzionante al 100% la nostra schermata nera e bianca. Quali? Eccoli:
if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
  var gameOverScreen:GameOverScreen = new GameOverScreen();
  gameOverScreen.x = 0;
  gameOverScreen.y = 0;
  addChild( gameOverScreen );
}

Salviamo tutto ed avviamo il nostro gioco. Appena entreremo in collisione con il nostro nemico, ecco cosa ci troveremo davanti:

AvoiderGame_Part3_06

Ma vogliamo fare ancora di più.

Nella prossima parte di questa sezione della guida, infatti, creeremo, proprio come abbiamo fatto per il Game Over, una schermata di gioco. Una sorta di contenitore che contenga il giocatore, i nemici e tutto ciò che fa parte della schermata di gioco, per toglierla ed usarla a nostro piacimento, semplificando enormemente le cose.


Aggiungiamo una Schermata di Gioco

Prima di iniziare, dobbiamo essere assolutamente chiari su quelli che sono i nostri intenti. Attualmente il nostro gioco è impostato in questo modo:

AvoiderGame_Part3_13

La Classe Documento controlla praticamente ogni singolo aspetto del gioco. Se continuiamo con questa linea di pensiero, tuttavia, appena inizieremo ad aggiungere una schermata dei titoli, un menù e qualche altro livello, tutto il codice inizierà ad apparire pesante ed ingarbugliato da gestire.

E noi non vogliamo che questo avvenga, vero?

Separiamo un pò le cose! Ecco cosa proponiamo in questo tutorial:

AvoiderGame_Part3_14

Sicuramente è tutto molto più ordinato, stavolta.

In questo modo la classe principale si occuperà solo di accertarsi che noi, in un certo istante, stiamo osservando la schermata giusta. Ogni schermata, invece, si occuperà di gestire tutti gli aspetti che la riguardano. La schermata di gioco si occuperà di gestire nemici, avatar e così via, così come un eventuale schermata di menù si occuperà di gestire le varie voci che presenterà.

Come implementare tutto questo? Non sarete sorpresi, probabilmente, se vi dico che dovrete fare semplicemente un MovieClip che chiameremo “PlayScreen”.

Questa nuova schermata farà più o meno la stessa cosa di quello che faceva, fino ad adesso, la nostra cara classe principale. Si occuperà di gestire il movimento dell’avatar, del movimento dei nemici, della loro generazione e ovviamente delle collisioni.

La differenza principale che c’è tra l’uso della classe documento e un Movie Clip a parte è che, di default, nel documento c’è già un background. Per il Movie Clip invece questa regola non vale, per cui dovremo aggiungerne uno noi! Modifichiamo il nostro “PlayScreen”, disegnandoci un rettangolo. Proprio come GameOverScreen, il nostro rettangolo sarà grande tanto quanto l’area di gioco, ovvero 400 x 300 pixels. Noi abbiamo usato un blu molto chiaro:

AvoiderGame_Part3_07

C’è un’altra cosa molto importante da dire, della quale credo vi siate accorti pure voi. Dato che abbiamo disegnato questo rettangolo, perchè non disegnare qualcosa di più complesso rispetto ad una semplice schermata blu chiaro?

Possiamo infatti usare anche del testo o qualsiasi altra cosa l’editor ci permetta. Ecco quello che noi abbiamo usato per il tutorial:

AvoiderGame_Part3_08

Ora andiamo al codice. Come abbiamo già detto, questa schermata si occuperà di gestire ciò che faceva la document class precedentemente (si, uso a ruota document class, classe documento e classe principale per farvi capire che sono la stessa cosa per me in questi tutorials): per cui immaginate che avrà bisogno perlopiù dello stesso codice!

Potremmo riscrivere il codice da capo, partendo da zero. Potremmo, in alternativa, copiarlo in blocco e metterlo in ordine dove serve. Oppure potremmo utilizzare quella meravigliosa cosa chiamata Object Oriented Design (design orientato agli oggetti).

Qui entra in gioco una riflessione importante. L’intero documento, essenzialmente, è un oggetto derivato dal Movie Clip. Quando l’assegnamo ad una classe è la stessa identica cosa che facciamo quando, dalla libreria, prendiamo un oggetto e lo colleghiamo analogamente ad una classe.

Per cui, possiamo tranquillamente collegare la classe principale da noi precedentemente scritta al nostro “PlayScreen”. Tra le sue proprietà (gruppo “concatenamento”) selezioniamo “Esporta per Actionscript”. Inserite il nome che abbiamo scelto per la nostra classe documento (nel nostro caso era AvoiderGame). Clicchiamo su Ok per confermare.

AvoiderGame_Part3_10

Ops, c’è qualcosa che non va.

Eh si, non possiamo associare mica due Movie Clip alla stessa classe! Prima di poter fare quello che vogliamo, quindi, dobbiamo creare una nuova classe documento. Usciamo dalle proprietà.


Una Nuova Classe Documento

Creiamo un nuovo file ActionScript3. Stavolta il codice sarà:

package
{
  import flash.display.MovieClip;
  public class DocumentClass extends MovieClip
  {
    public function DocumentClass()
    {

    }
  }
}

e salviamo il nostro file come DocumentClass.as, ovviamente nella cartella “Classi” o comunque quella destinata a contenere le classi.

AvoiderGame_Part3_11

Di sicuro il nome scelto non ci confonderà!!! ;)

Come vi ho suggerito nella prima lezione, potete controllare l’effettivo collegamento della classe cliccando sull’icona della matita. Ora possiamo collegare il nostro PlayScreen alla classe AvoiderGame, nello stesso modo spiegato prima.

Fatelo, stavolta non avrete errori di sorta.

Salvate tutto e provate il gioco: non verrà fuori niente. Questo perchè la classe principale non ha attivato la schermata di gioco, la PlayScreen. Modifichiamo quindi la nostra classe documento in questo modo:

package
{
  import flash.display.MovieClip;
  public class DocumentClass extends MovieClip
  {
    public var playScreen:AvoiderGame;

    public function DocumentClass()
    {
      playScreen = new AvoiderGame();
      addChild( playScreen );
    }
  }
}

Non abbiamo fatto niente di complicato, vero? ;)

Dovrebbe essere inutile ormai, ma ricapitolerò queste due righe di codice: abbiamo creato l’oggetto playScreen di tipo AvoiderGame (la classe che si occupa, appunto, della schermata di gioco, avatar e nemici) e l’abbiamo attaccato al nostro filmato principale.

Salviamo tutto e proviamo. Cool.

AvoiderGame_Part3_210


Continuiamo ad Ottimizzare.

Beh, abbiamo fatto molto, ma la struttura che c’eravamo preposti non è ancora stata completata del tutto. Le cose le volevamo in questo modo:

AvoiderGame_Part3_14

e invece ce le ritroviamo così:

AvoiderGame_Part3_15

La differenza sta nel fatto che la nostra schermata di gioco fa qualcosa in più rispetto a ciò che dovrebbe fare! Sto parlando del Game Over ;) Certo, il gioco funziona ugualmente, ma se una cosa si deve fare, la si deve fare per bene!

Il nostro obiettivo è quindi il seguente: quando il nostro avatar muore, non vogliamo che la classe principale continui a mostrarci la PlayScreen, ma deve invece darci il Game Over (GameOverScreen)!!! Ora, usando Actionscript2 ce la saremmo cavata facilmente, con un po’ d’uso di “_root” e “_stage”.

Ma no, non siamo in Actionscript2 e no, non prenderemo questa strada.

Torniamo un secondo indietro quindi, quando avevo parlato (nella prima parte) dell’oggetto timer. In particolare, dell’ EventListener.

Possiamo pensare infatti ad un EventListener come ad un robot. Un robot che controlla se una particolare condizione è vera. Se è vera, allora richiama un’altra funzione di conseguenza. Altrimenti tutto resta come prima.

Ecco cosa avevamo usato l’ultima volta, spero lo ricordiate!

gameTimer.addEventListener( TimerEvent.TIMER, onTick );

TimerEvent.TIMER infatti indicava lo scadere del periodo di aggiornamento del gioco, mentre “onTick” era la funzione da mandare ad ogni aggiornamento. Bene, realizzeremo qualcosa del genere per quanto riguarda la morte del nostro avatar, in modo tale da poter lasciare tutto quanto molto “indipendente”.

Ed è quello che serve per separare finalmente la GameOverScreen e la PlayScreen. Aggiungiamo questo codice per prima cosa alla classe principale:

public function DocumentClass()
{
  playScreen = new AvoiderGame();
  playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath );
  addChild( playScreen );
}

A differenza del caro TimerEvent.TIMER, stavolta AvatarEvent.DEAD non è un evento già costruito in flash. Per cui rimbocchiamoci le maniche miei prodi, dovremo crearlo noi. Per prima cosa creiamo la funzione “onAvatarDeath” che richiameremo in caso di morte.
public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{

}

A questo punto mi chiederete: “a Francè, tutto questo è molto bello, ma… che ci devo mettere?”

La risposta ve la potete dare voi: cosa succede quando il nostro avatar muore?

  • Creiamo il nostro GameOverScreen;
  • Lo posizioniamo sullo schermo;
  • Lo aggiungiamo con addChild.

Ed ecco quindi il codice palesemente preso, copiato ed incollato.

public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
  var gameOverScreen:GameOverScreen = new GameOverScreen();
  gameOverScreen.x = 0;
  gameOverScreen.y = 0;
  addChild( gameOverScreen );

  playScreen = null;
}

Il tutto con una piccola aggiunta: “playScreen = null”. Questo servirà a resettare il nostro gioco quando andremo in GameOver. “Resettare”, in questo caso, vuol dire cancellare tutto ciò che concerne il nostro gioco attuale. Ri-eseguendo l’istruzione “playscreen = new PlayScreen();” potremo riavviare il gioco da capo.

Ultima cosa da ricordare: assegnando null ad un oggetto visibile su schermo, questo non sarà più visibile. Quindi “nullificando” il nostro playScreen questo verrà automaticamente nascosto alla vista del giocatore.

Spero d’essere stato chiaro!

Ora si crea l’evento.


Il Grande Evento

Permettetemi un minimo di umorismo pessimo come questo del titolo, in quanto al momento in cui scrivo sono reduce da una specie di mal di pancia più febbre che mi sta rendendo così.

Veniamo a noi.

Avete presente il nostro Avatar? Estende la classe Movie Clip e può fare tutte le cose che questo fa, con l’aggiunta di altre azioni specificate da noi nella classe.

Allo stesso, identico modo funziona il TimerEvent che avevamo utilizzato precedentemente: estende la classe Event. Ora faremo qualcosa di nuovo: estenderemo la classe Event creando una nuova classe chiamata AvatarEvent, che servirà al nostro EventListener per riconoscere la morte del nostro povero giocatore.

Bene, provvediamo quindi ad estendere la classe Event come descritto (creando un altro file AS chiamato AvatarEvent.as):

package
{
  import flash.events.Event;
  public class AvatarEvent extends Event
  {
    public function AvatarEvent()
    {

    }
  }
}

Potrebbe sembrare in una forma corretta, tuttavia non è così. Quando creiamo un nuovo evento, questo si aspetta da noi il passaggio del “tipo” di evento, un po’ come la classe Enemy che si aspettava il passaggio delle coordinate x ed y per partire.

Per farvi un esempio spicciolo, volendo creare un evento di scroll dovremmo usare una sintassi come la seguente:

new Event( Event.SCROLL )

Sulla scia di quest’esempio quindi iniziamo col fornire il tipo di evento. Utilizzeremo il tipo stringa ed il codice verrà mutato nel modo riportato:
package
{
  import flash.events.Event;
  public class AvatarEvent extends Event
  {
    public function AvatarEvent( type:String )
    {

    }
  }
}

Dopo aver fatto questo, dobbiamo specificare l’evento da usare (la morte). Utilizzeremo una costante, ovvero una “variabile” non variabile. In termini semplici, un contenitore che al suo interno riporta un valore che non può più essere cambiato.
package
{
  import flash.events.Event;
  public class AvatarEvent extends Event
  {
    public static const DEAD:String = "dead";

    public function AvatarEvent( type:String )
    {

    }
  }
}

Per la trasposizione in codice, la parola chiave è public const.

Dopo queste istruzioni, quindi, saremo capaci di scrivere:

new AvatarEvent( AvatarEvent.DEAD )

Inoltre, nel codice in alto, avrete notato anche la parola chiave Static. L’abbiamo usata perchè un oggetto o una variabile statica non ha bisogno di un oggetto istanziato.

Cercherò di spiegarmi meglio.

Credo conosciate tutti, ormai, la struttura della classe Enemy. Essendo un estensione della Movie Clip, ha le proprietà x ed y che consentono di gestire il movimento. Quindi, dopo aver creato un nemico tramite la dichiarazione (e l’inizializzazione) di un nuovo oggetto, possiamo richiamare la variabile ed usarla.

Esempio: “eric.x = 20;”

Se invece provate a scrivere qualcosa come “Enemy.x = 20”, capirete presto che non ha molto senso. Questo perchè il nemico non è uno solo e soprattutto Enemy identifica la classe, una “traccia” da seguire per l’oggetto e non l’oggetto stesso!

Le cose cambiano invece per AvatarEvent, che ha un valore costante. In questo caso specifico abbiamo un valore costante che utilizzeremo nell’ambito della classe (la costante DEAD). Che senso ha creare una variabile, allocando magari quel poco di spazio in più che sappiamo non serve? Basterà rendere statica la stringa DEAD e utilizzarla quando serve.

Siamo quasi pronti all’uso del nostro gioco, ma dobbiamo ancora fare qualche cosa. In primis dobbiamo capire cosa fare con il “tipo” passato nell’istanza specifica dell’AvatarEvent. Per ora utilizzeremo una funzione che non spiegherò precisamente, in quanto verrà ripresa più avanti, nella lezione 5. Il codice è il seguente:

package
{
  import flash.events.Event;
  public class AvatarEvent extends Event
  {
    public static const DEAD:String = "dead";

    public function AvatarEvent( type:String )
    {
      super( type );
    }
  }
}

Volendo giusto accennare qualcosa per non rimanere a digiuno, il metodo “super” esegue il codice dall’interno del metodo costruttore della classe superiore “Event”, passando il tipo da noi utilizzato. In poche parole tutto questo procedimento rende il nostro codice appena scritto capace di essere utilizzato dal resto del sistema.

Se non capite non vi preoccupate, la prima volta è stato abbastanza confuso anche per me e solo con la pratica si possono acquisire certe conoscenze!

Ora dobbiamo aggiungere le ultime istruzioni per fare in modo che l’evento venga attivato appena il nostro avatar muore. Qui le cose tornano ad essere un po’ più comprensibili!

Torniamo nella nostra classe AvoiderGame.as e cambiamo le cose da:

if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
  var gameOverScreen:GameOverScreen = new GameOverScreen();
  gameOverScreen.x = 0;
  gameOverScreen.y = 0;
  addChild( gameOverScreen );
}

a:
if ( avatar.hitTestObject( enemy ) )
{
  gameTimer.stop();
  dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}

La linea di codice inserita permette di avviare l’evento AvatarEvent, di tipo DEAD (morto). Salvate tutto e, come al solito provate il vostro gioco. Dovrebbe tutto funzionare correttamente e senza problemi.

Posso ben capire che questa lezione può aver messo in testa un po’ di confusione. Molto spesso, tuttavia, a fronte di una maggiore complessità di ciò che creiamo può ritornarci come vantaggio una maggiore scalabilità del programma: aggiungere nuove features in futuro, quindi, sarà ancora più facile del previsto.

Avrete la prova di tutto questo nel prossimo capitolo della nostra guida, nella quale aggiungerò al nostro gioco un pulsante di Reset e uno schermo iniziale dei titoli con il minimo sforzo.

Ovviamente i files della lezioni di oggi sono disponibili Qui. Scaricateli e fatene buon uso!

Per oggi è tutto!

  • Share/Bookmark