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 nostra guida aggiungeremo qualcosa in più al nostro gioco. Parlo di un pulsante per permettere al nostro giocatore di riavviare la partita nel caso muoia, oltre ad una schermata di menù posta all’inizio del gioco.

AvoiderGame_Part4_25

Davvero pensavate di fare un gioco senza menù? ;)

Faccio, come al solito, le dovute precisazioni prima di iniziare questa parte della guida: nel caso sia la prima volta che seguite questo tutorial e non volete iniziare da zero (bensì da questo punto) allora andate alla lezione precedente e prendete i files dal file Zip a fine articolo.

Altrimenti solita storia… una bella copia di backup dei files e via!


Let Me Try That, Again

Al momento, la nostra situazione riguardo al Game Over è la seguente: il giocatore entra in contatto con il nemico, viene mandato nella schermata di Game Over, appunto, e nel caso voglia riniziare deve necessariamente eseguire il refresh della pagina.

C’è davvero bisogno di dire che questa pratica è assolutamente da evitare? ;)

Per questo motivo ora creeremo un semplice pulsante nella schermata di Game Over che ci farà iniziare da capo. Creiamo un nuovo simbolo dal menù “Inserisci > Nuovo Simbolo” e stavolta, invece di creare un simbolo di tipo “Clip filmato” creiamone uno di tipo “Pulsante”. Chiamatelo RestartButton.

AvoiderGame_Part4_02

In questo modo entreremo come al solito nella fase di editing del pulsante. Disegnate quello che volete (sicuramente qualcosa che faccia capire al giocatore che cliccando lì possa riniziare il gioco), questo è quello che abbiamo preparato noi:

AvoiderGame_Part4_03

Come potete vedere niente di troppo complesso. Prima di continuare con altre mosse, diamo un occhiata per bene alla linea temporale del nostro pulsante, ben diversa da quella degli altri clip precedenti:

AvoiderGame_Part4_04

Un pulsante, al contrario dei vari frames in successione, contiene solo quei frame effettivamente necessari alla gestione di un pulsante semplice.

  • Su: il “look” del nostro pulsante se non viene toccato.. insomma, da “fermo”;
  • Sopra: l’aspetto del nostro clip nel caso il mouse ci passi sopra;
  • Giù: l’aspetto del pulsante una volta che viene cliccato;
  • Premuto: L’area del pulsante “sensibile” al mouse (a breve maggiori approfondimenti)

Il box ombreggiato in grigio con un punto al centro sotto la colonna “Su” (o Up in inglese) indica che abbiamo disegnato l’immagine per quello stato del pulsante. A diversi stati, infatti, può dipendere tranquillamente un diverso aspetto. Provvediamo quindi a disegnare il pulsante per gli altri stati. Clicchiamo col pulsante destro sui frame vuoti ed inseriamoci un Keyframe, tramite l’istruzione “Inserisci Fotogramma Chiave” (la stessa cosa ovviamente andrà fatta per tutti e quattro gli stati):

AvoiderGame_Part4_06

AvoiderGame_Part4_07

Come previsto, ecco come appare il box dopo il disegno ;)

AvoiderGame_Part4_08

Se vediamo le cose così abbiamo realizzato anche l’ultimo.

Perfetto. Il consiglio che vi do per ora, per fare le cose più velocemente, è di non realizzare un pulsante troppo complicato: copiate ed incollate il contenuto del primo frame in tutti gli altri e salvate così. Al massimo, per dargli un minimo di “vita” invertite i colori del pulsante nel secondo stato (Sopra).

AvoiderGame_Part4_09

Sarà così che vedremo il nostro pulsante quando ci andremo sopra con il mouse.

Ed ora veniamo alla colonna “Premuto”. Dunque, dovete sapere ciò che viene disegnato in questa colonna definisce la zona “cliccabile” del nostro pulsante. Per esempio, supponiamo di aver disegnato un pulsante con un ombreggiatura.

La zona cliccabile, ovviamente, sarà solamente quella interna e non l’ombra. In questo frame, quindi, andremo a disegnare la zona che sarà “sensibile” al click. Nel nostro caso, per esempio, abbiamo deciso di rendere tutto il pulsante cliccabile. Per questo motivo disegneremo una forma grande quanto il pulsante:

AvoiderGame_Part4_11

Si, una semplice forma, niente di complesso.

Abbiamo colorato la forma in rosso per far capire che ricordarci che questa parte NON sarà visibile al giocatore. Sarà semplicemente un reminder per noi e per il programma, che cercherà la zona “cliccabile” del pulsante.

Concetti chiari? Bene, ora provvediamo ad aggiungere il nostro pulsante alla schermata di Game Over. Apriamo proprio questa schermata dalla libreria (nel mio caso ho fatto doppio click su GameOverScreen, voi ragionate di conseguenza).

AvoiderGame_Part4_12

E rieccola qua, ancora una volta… quasi non ce la facciamo più!

Cerchiamo il nostro pulsante di reset nella libreria e trasciniamolo sulla schermata aperta, in modo tale da posizionarlo come vogliamo.

AvoiderGame_Part4_13

Diamine, ci siamo scordati che il bordo nero del pulsante non sarà visibile sul nero! Beh dai, per questa volta perdonatemelo… :)

Tornando indietro nel tempo, vi ricordate quando avevo parlato di classi ed istanze? Bene, il pulsante che avete sullo schermo è un’istanza della classe RestartButton. Dal momento che non abbiamo definito il pulsante tramite ActionScript, non ha un nome di istanza. Nel caso vogliamo rendere il pulsante istanza accessibile tramite codice (e noi lo vogliamo) dobbiamo dargli un nome.

Dopo aver selezionato il pulsante appena aggiunto, diamo un’occhiata al pannello delle proprietà:

AvoiderGame_Part4_14

Qualcosa mi dice che avete capito al volo quale campo ci serve.

Nella casella “<Instance Name>” digitiamo un nome per il nostro pulsante. Chiamatelo, per semplicità, “restartButton” (mi raccomando maiuscole e minuscole).

AvoiderGame_Part4_15

Bene, salviamo tutto e adesso passiamo al codice. Qualcuno dovrà pur dire a questo benedetto pulsante cosa fare, no?


EventListener è la Via, mio giovane padawan. (si vede poco che mi piace Star Wars.)

Tempo addietro, precisamente nella precedente lezione, avevamo fatto un buon uso degli EventListener per richiamare una funzione precisa nel caso di morte del giocatore. Oltre a questo uso, inoltre, non dobbiamo scordarci il Tick del caro gameTimer.

Ora scriveremo altro codice di questo genere: stavolta però per “ascoltare” un eventuale click sul pulsante di Restart appena creato. Dato che il nostro pulsante esiste nella GameOverScreen, il codice che concerne il pulsante di Reset andrà posizionato nella classe GameOverScreen.

Ma un momento… non abbiamo mai creato questa classe a livello di codice!

Creiamo un nuovo file .as (ormai per i nomi e per la posizione in cui salvare vi lascio liberi, in tre lezioni l’avrete imparato a sufficienza) e riportiamoci il seguente codice:

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

    }
  }
}

(io l’ho salvato come GameOverScreen.as nella directory “Classi”, ovviamente)

Salviamo tutto il nostro codice ed avviamo il nostro consueto test. Le cose non andranno come desideriamo ed ecco l’errore che riceveremo:

GameOverScreen.as, Line 2 1046:   Type was not found or was not a compile-time constant: SimpleButton.

Cosa è successo? Flash ha constatato che c’è un pulsante nella GameOverScreen. Tuttavia, si chiede cosa sia in quanto non ha mai “sentito parlare” di questo pulsante.

Cosa vuol dire tutto ciò? Che adesso andremo ad importare il nostro pulsante nel codice in maniera appropriata! Niente di astruso, basterà una linea di codice in più e null’altro:

package
{
  import flash.display.MovieClip;
  import flash.display.SimpleButton;

  public class GameOverScreen extends MovieClip
  {
    public function GameOverScreen()
    {

    }
  }
}

Adesso si che potete salvare tutto e testare il vostro bel prodotto. Noterete che, quando ci andrete sopra col mouse o lo premerete, cambierà grafica proprio come definito in fase di editing. Nulla da dire, vero? A parte il fatto che una volta che ci si clicca, al contrario di quanto ci aspettiamo…

… non succede niente.

E mi pare ovvio se non ci mettiamo un po’ di codice dietro!!! Davvero pensavate di cavarvela così? ;) Continuiamo l’aggiunta di codice nella nostra classe GameOverScreen:

package
{
  import flash.display.MovieClip;
  import flash.display.SimpleButton;
  import flash.events.MouseEvent;

  public class GameOverScreen extends MovieClip
  {
    public function GameOverScreen()
    {
      restartButton.addEventListener( MouseEvent.CLICK, onClickRestart );
    }

    public function onClickRestart( mouseEvent:MouseEvent ):void
    {

    }
  }
}

Come potete immaginare, public function onClickRestart( mouseEvent:MouseEvent ) è la funzione che noi chiameremo una volta accertato il click sul nostro pulsante. Proprio come avvenuto precedentemente, stiamo passando come parametro le informazioni dell’evento sotto forma di MouseEvent.

Ovviamente dobbiamo aggiungere anche il nostro EventListener! restartButton.addEventListener ( MouseEvent.CLICK, onClickRestart ) si occuperà proprio di questo, aggiungendo il Listener al nostro pulsante. Appena rilevato il click sarà richiamata la funzione illustrata poco sopra.

Nota: Mi raccomando, MouseEvent.CLICK. (il click va scritto MAIUSCOLO)

Infine abbiamo la direttiva import flash.events.MouseEvent;. Senza questa linea di codice flash non potrebbe capire che cos’è un MouseEvent e quindi non potrebbe neanche capire la parola chiave CLICK.

Dopo queste precisazioni su questo EventListener, torniamo al gioco. Sappiamo che per riavviare il gioco dobbiamo eseguire le seguenti operazioni:

  • Rimuovere il GameOverScreen;
  • “Ricaricare” il PlayScreen.

Nella parte precedente della nostra guida abbiamo riorganizzato tutto in maniera più funzionale e ad occuparsi di queste cose ora è la classe documento. Sarà questa quindi la classe che modificheremo. Apriamo il file DocumentClass.as (o quello dal nome scelto da noi) e aggiungiamo:

public function restartGame():void
{
  playScreen = new AvoiderGame();
  playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath );
  addChild( playScreen );

  gameOverScreen = null;
}

Con le prime linee di codice “ricarichiamo” il nostro gioco, re-inizializzando la nostra playScreen, aggiungendo l’EventListener per la morte del nostro Avatar e aggiungendo la schermata alla classe documento tramite addChild.

La linea successiva, proprio come avevamo fatto per il GameOver nella lezione 3, rende null la schermata di GameOver, rimuovendola così dallo schermo e dalla memoria.

Nota Importante: se ci fate caso, la nostra gameOverScreen è disponibile solo nel contesto della funzione “onAvatarDeath()”. Questo perché viene inizializzata proprio là dentro. Per questa ragione dovremo rendere disponibile la nostra schermata a livello globale, con queste istruzioni nella classe documento.

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

Ricordatevi, inoltre, di rimuovere assolutamente il “var” dalla linea di codice “var gameOverScreen” dalla funzione onAvatarDeath, dato che da ora sarà già definita a priori. Ecco come apparirà il codice in questo caso.
public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
  gameOverScreen = new GameOverScreen();
  gameOverScreen.x = 0;
  gameOverScreen.y = 0;
  addChild( gameOverScreen );

  playScreen = null;
}

Dopo tutti questi cambiamenti, per sicurezza, vi riporto la classe documento per intero, in modo tale da facilitarvi eventuali modifiche e controlli ;)
package
{
  import flash.display.MovieClip;
  public class DocumentClass extends MovieClip
  {
    public var playScreen:AvoiderGame;
    public var gameOverScreen:GameOverScreen;

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

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

      playScreen = null;
    }

    public function restartGame():void
    {
      playScreen = new AvoiderGame();
      playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath );
      playScreen.x = 0;
      playScreen.y = 0;
      addChild( playScreen );

      gameOverScreen = null;
    }
  }
}

Abbiamo aggiunto anche altre linee:

  • playScreen.x = 0;
  • playScreen.y = 0;

proprio com’era avvenuto con il gameOverScreen. Il codice riportato poco sopra è stato inoltre copiato ed incollato anche nella funzione restartGame(), in quanto abbiamo la necessità di riposizionare la finestra di gioco una volta riavviato!

Successivamente, vi mostrerò un modo più elegante per evitare ripetizioni di codice inutili.


Riavviamo!!!

Ora abbiamo una funzione in GameOverScreen che viene chiamata ogni qualvolta il pulsante di riavvio viene premuto. Ovvero, tutte le volte che il giocatore desidera riniziare.  Abbiamo inoltre una funzione nella classe documento che si occupa di scambiare le schermate in modo corretto e quindi di riavviare il gioco.

Sarebbe bello e semplice cercare di “collegare” queste due insieme, in modo tale da risolvere qualsiasi problema. Tuttavia, come già detto nell’articolo precedente, una soluzione del genere può portare ad alcuni problemi nel tempo.

Per questo motivo creeremo ancora una volta un evento personalizzato per le nostre esigenze. Nello specifico, riporto qui di seguito quello che faremo (seguite con attenzione!):

  • Il giocatore clicca sul pulsante di riavvio;
  • Quest’azione porta a mandare un evento MouseEvent di tipo CLICK;
  • L’evento viene “ascoltato” e quindi recepito dal nostro EventListener, che provvede ad eseguire il metodo “onClickRestart()”;
  • “onClickRestart()”, quindi, provvederà a far svolgere questo nuovo evento, che chiameremo “NavigationEvent”, di tipo RESTART (quindi qualcosa come NavigationEvent.RESTART);
  • Ovviamente il nostro evento corrisponderà ad un metodo richiamato di conseguenza che andremo a creare: il nome da noi scelto sarà “onRequestRestart()”;
  • “onRequestRestart()” infine provvederà a sistemare le schermate nel modo giusto.

Credo che più chiaro di così non potevo essere, rileggete bene tutto perché è essenziale capire COSA andremo a fare, prima di fare.

Wow! E’ stato lungo e soffocante anche da scrivere! Ma vi assicuro che è il metodo più elegante e soprattutto funzionale, che ci eviterà (nel caso di successive modifiche) un lavoro inutile e ancor più faticoso (oltre che arrangiato barbaramente).

Comunque sia, torniamo a noi. Ora dobbiamo creare il nostro NavigationEvent, per cui mettiamoci all’opera! Come prima cosa creiamo il nostro file: l’aspetto sarà molto simile all’altro evento creato prima, AvatarEvent. Infatti ecco il codice:

package
{
  import flash.events.Event;
  public class NavigationEvent extends Event
  {
    public static const RESTART:String = "restart";

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

Stessa, identica, struttura. (Penso vi sia chiaro e da ora in poi lo ometterò, credo. I file delle classi da me realizzati li salvo tutti nella cartella Classi)

Ora provvediamo a “sparare” il nostro evento, nella classe GameOverScreen (beh dove sennò, è da lì che il giocatore preme il pulsante!)

public function onClickRestart( mouseEvent:MouseEvent )
{
  dispatchEvent( new NavigationEvent( NavigationEvent.RESTART ) );
}

Così come è stato semplice realizzare l’evento, non dovrebbe essere neanche più un problema, ormai, aggiungere l’EventListener al posto giusto (la classe documento):
public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
  gameOverScreen = new GameOverScreen();
  gameOverScreen.addEventListener( NavigationEvent.RESTART, onRequestRestart );
  gameOverScreen.x = 0;
  gameOverScreen.y = 0;
  addChild( gameOverScreen );

  playScreen = null;
}

Naturalmente, avremo bisogno di creare anche la funzione “onRequestStart()”, per riavviare il gioco. Anche questa sarà posizionata nella classe documento.
public function onRequestRestart( navigationEvent:NavigationEvent ):void
{
  restartGame();
}

Ora che tutto è a posto (e dopo aver creato questa luuuunga catena di eventi) possiamo dire che anche questa piccola fase è andata! Salviamo tutto e proviamo la nostra creatura con la sua nuova feature ;)


Il Potere della Scelta

Non vi spaventate dal titolo, aggiungere un piccolo menù di scelta prima del gioco vero e proprio, arrivati a questo punto delle cose, è veramente semplice e non da problemi di alcun genere. Per cui vi avverto, in questa fase andremo un po’ più veloce rispetto al normale.

Per prima cosa, creiamo una schermata di menù (o dei titoli a dir si voglia) che ci piace. Possiamo crearla da zero oppure prendere spunto copiando la schermata di Game Over.

Ecco la nostra schermata principale:

AvoiderGame_Part4_23

Un capolavoro. Sono commosso.

Ora, il pulsante. Così come per la schermata iniziale, possiamo fare la stessa cosa con il pulsante di restart del gioco, ovvero facendone una copia e lavorandocelo per adattarlo alle nostre esigenze. Oppure crearlo da zero. Insomma quel che volete. Ed ecco quello che abbiamo realizzato noi:

AvoiderGame_Part4_20

E ricordate. Non sono pigro. Sto solamente “mantenendo costante la mia linea di design”.

Bando alle ciance, aggiungiamo il pulsante al nostro MenuScreen e usiamo come nome istanza “startButton”.

AvoiderGame_Part4_24

Che bello mamma! Quante immagini!

Ora passiamo al codice per gestire il tutto. Click destro su MenuScreen e selezioniamo “Proprietà”. Renderemo disponibile per il codice tutto quanto, per cui selezioniamo “Esporta per Actionscript” e come nome di riferimento della classe usiamo “MenuScreen”. Appunto. Clicchiamo su ok e come è ovvio ci si presenterà questa finestra:

AvoiderGame_Part4_22

Se non abbiamo la classe, dove vogliamo andare? Creiamo un nuovo file Actionscript3 che chiameremo MenuScreen.as. Il codice? Eccolo qui di seguito.

package
{
  import flash.display.MovieClip;
  import flash.display.SimpleButton;
  import flash.events.MouseEvent;

  public class MenuScreen extends MovieClip
  {
    public function MenuScreen()
    {
      startButton.addEventListener( MouseEvent.CLICK, onClickStart );
    }

    public function onClickStart( event:MouseEvent ):void
    {
      dispatchEvent( new NavigationEvent( NavigationEvent.START ) );
    }
  }
}

Salviamo la nostra classe. Al contrario di quanto ho fatto precedentemente, per questa volta non mi soffermerò a spiegare riga per riga tutto. Vi assicuro, è la stessa identica cosa fatta precedentemente per la schermata di Game Over, per cui mi sembra troppo sprecare dello spazio utile ;)

L’unica cosa da notare è che ho “sparato” un evento di tipo NavigationEvent.START. Attualmente, come ben sapete, NavigationEvent contempla solo il RESTART. Aggiungiamo quindi la definizione necessaria nel file del NavigationEvent:

package
{
  import flash.events.Event;
  public class NavigationEvent extends Event
  {
    public static const RESTART:String = "restart";
    public static const START:String = "start";

E il gioco sarà fatto.

Una cosa però dobbiamo dirla e ricordarla: al momento la nostra cara classe documento mostra, all’avvio, la schermata di gioco. Non è esattamente quel che vogliamo: abbiamo creato una schermata dei titoli, usiamola! Ovviamente anche in questo caso non sarà niente di complicato, basterà cambiare qualche linea di codice.

Da:

public var playScreen:AvoiderGame;
public var gameOverScreen:GameOverScreen;

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

A:
public var menuScreen:MenuScreen;
public var playScreen:AvoiderGame;
public var gameOverScreen:GameOverScreen;

public function DocumentClass()
{
  menuScreen = new MenuScreen();
  menuScreen.addEventListener( NavigationEvent.START, onRequestStart );
  menuScreen.x = 0;
  menuScreen.y = 0;
  addChild( menuScreen );
}

Abbiamo creato un oggetto chiamato menuScreen, senza più quindi impostare la schermata di gioco direttamente. Per ultima cosa, quindi, scriviamo la nostra funzione “onRequestStart”()”, necessaria a far partire il gioco:
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
  playScreen = new AvoiderGame();
  playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath );
  playScreen.x = 0;
  playScreen.y = 0;
  addChild( playScreen );

  menuScreen = null;
}

Salvate tutto e… buona giocata ;)

Ripeto, posso capire perfettamente che le cose possono essere un pochino “confuse” se vi avvicinate per la prima volta a questo linguaggio. Tuttavia, ho evitato di spiegare tutto in maniera approfondita per non ripetermi inutilmente per cose che possono essere capite senza problemi.

Come sempre il file della lezione di oggi è disponibile Qui. Confrontatelo pure con il vostro lavoro nel caso abbiate qualche perplessità!

Prima di lasciarvi, vi annuncio che nel prossimo articolo aggiungeremo al nostro gioco un ulteriore elemento di sfida: un bel sistema di punteggio. Inoltre aggiungeremo anche un orologio.

Alla prossima!

  • Share/Bookmark