Realizzare Asteroids [parte 2]

Oggi faremo girare l’astronave in senso orario o antiorario, non è una cosa molto difficile.

Prima di cominciare, però, dobbiamo creare un’altra cartella che chiameremo elementi, esattamente allo stesso percorso in cui si trova la cartella grafica, creata nella lezione precedente. Questo ci aiuterà a differenziare meglio le Classi che compongono il nostro gioco.

La prima Classe che ci serve è quella che definirà l’Astronave di gioco. Nella lezione precedente abbiamo già realizzato la Classe che disegna l’Astronave e questa potrebbe sembrare una ripetizione. In realtà è una questione di ordine: la Classe Astronave nel Package grafica si occupa soltanto del disegno, la Classe che scriveremo adesso utilizza il disegno e aggiunge tutti i Metodi e le Proprietà necessari ad utilizzarlo nel gioco.

Se in futuro avremo la necessità di cambiare l’aspetto dell’Astronave (magari caricando un’immagine) ma non il suo comportamento, il nostro lavoro sarà più semplice.

In un nuovo file .as scriviamo questo codice:

package elementi{
    import grafica.Astronave;
    import flash.display.Sprite;
    public class Player extends Sprite {
        private var _color:uint;
        private var _speed:int;
        public function Player(x:int = 100,y:int = 100,color:uint = 0x006699,speed:int = 0) {
            _color = color;
            this.x = x;
            this.y = y;
            _speed = speed;
            this.addChild(new Astronave(_color, 16));
        }
        public function ruota(senso:String = 'orario') {
            var r:int = senso == 'orario' ? 10 : -10;
            this.rotation += r;
        }
    }
}

Questa Classe è molto semplice: oltre ad avere un’istanza del disegno dell’Astronave ha un Metodo ruota, chiamando questo Metodo dall’esterno potremo dire alla nostra Astronave di ruotare in senso orario o in senso antiorario.

Questo file va salvato con il nome Player.as nella cartella elementi.

Per ruotare il nostro Sprite utilizziamo la sua Proprietà rotation. La proprietà rotation rappresenta l’angolo di rotazione di un oggetto grafico all’interno del nostro filmato. L’angolo di rotazione è espresso in gradi: i valori possibili vanno da zero a trecentosessanta e gli estremi si equivalgono (360 = 0), così possiamo definire l’angolo di rotazione usando sia un numero positivo che un numero negativo:

Esempi di rotazione

Rotazione di un Oggetto grafico con la Proprietà rotation

La Proprietà rotation del nostro Sprite cambierà ogni volta che richiameremo il Metodo ruota. Al cambiare del valore di questa Proprietà vedremo cambiare l’orientamento dell’Astronave.

Adesso scriveremo la Classe principale del nostro  gioco. Anche se non abbiamo ancora tutti gli elementi che ci servono per vedere in azione il gioco finito possiamo già gestire la rotazione dell’Astronave tramite tastiera. La Classe si chiamerà AsteroidsGame e la salveremo dentro la cartella principale del Progetto, fuori dai nostri Package.

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    import elementi.Player;

    public class AsteroidsGame extends Sprite {
        private var _p:Player;
        private var _keyDown:Object;
        private var _gameTime:Timer = new Timer(40);
        public function AsteroidsGame() {
            _keyDown = new Object();
            this.addEventListener(Event.ADDED_TO_STAGE, init);
        }
        private function init(e:Event) {
            _p = new Player(stage.stageWidth/2, stage.stageHeight/2);
            this.addChild(_p);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, dKey);
            stage.addEventListener(KeyboardEvent.KEY_UP, uKey);
            _gameTime.addEventListener(TimerEvent.TIMER, goOn);
            _gameTime.start();
        }
        private function dKey(e:KeyboardEvent) {
            _keyDown[e.keyCode] = 1;
        }
        private function uKey(e:KeyboardEvent) {
            _keyDown[e.keyCode] = null;
        }
        private function goOn(e:TimerEvent) {
            if (_keyDown[39]) {
                _p.ruota('orario');
            } else if (_keyDown[37]) {
                _p.ruota('antiorario');
            }
        }
    }
}

Questa Classe comanderà tutto il gioco. Come per il precedente tutorial Snake, anche qui il tempo di gioco è regolato da un Oggetto Timer, solo che qui andiamo un po’ più veloci: con un delay di 40 millisecondi la Funzione goOn verrà eseguita 25 volte al secondo.

Timer e Framerate

Ogni tanto qualcuno mi chiede in che maniera il Timer dipende dal Framerate (Frequenza Fotogrammi), questo perché capita di vedere che cambiando il Framerate cambia anche il comportamento delle Applicazioni.

In realtà il Timer e il Framerate sono due cose distinte:

  • Il Timer funziona ad intervalli di tempo, espressi in millisecondi.
  • Il Framerate aggiorna la visualizzazione dell’Applicazione e può rallentare nel caso in cui i calcoli da fare siano complessi.

Se utilizziamo un Framerate molto basso, ad esempio 1fps (un fotogramma al secondo) e un Timer molto veloce, non avremo un risultato soddisfacente. Nel disegno che segue provo a mostrare l’effetto che si ha quando un Timer controlla la rotazione di un Oggetto incrementando il valore di rotation di 10 gradi 5 volte al secondo (delay = 400) con un’impostazione di 1fps:

Timer vs FPS

Timer vs FPS

Lo Sprite ruota di 10 gradi 5 volte al secondo, però viene disegnato soltanto una volta al secondo. Il risultato finale è che vediamo lo Sprite ruotare di 50 gradi una volta al secondo.

Gestire la pressione di più tasti

I Metodi assegnati ai Listener da tastiera hanno un comportamento un po’ più complesso rispetto a quelli che abbiamo scritto fino ad ora. Per questo gioco abbiamo l’esigenza di gestire la pressione di più tasti contemporaneamente: un tasto per ruotare, uno per muoversi, uno per sparare. Anche se per la sola rotazione questa necessità non è evidente abbiamo già impostato il meccanismo che useremo:

  1. Abbiamo inizializzato una Proprietà chiamata _keyDown. Questo è un Oggetto generico, questo significa che possiamo utilizzarlo per memorizzare tutti i valori che ci fanno comodo.
  2. Alla pressione di un tasto, il Metodo dKey crea una Proprietà nell’Oggetto _keyDown utilizzando il numero del tasto premuto (nel nostro codice assegna un valore di 1, ma avremmo potuto utilizzare qualsiasi cosa, l’importante è che questa Proprietà esista).
  3. Al rilascio di un tasto, il Metodo uKey distrugge la Proprietà corrispondente al tasto rilasciato dall’Oggetto _keyDown.
  4. Ogni volta che vogliamo sapere se un Tasto è attualmente premuto sulla tastiera non ci rimane che controllare che nell’Oggetto _keyDown esista una Proprietà con il numero corrispondente. Se questa Proprietà esiste vuol dire che è stata creata dal Metodo dKey e non ancora distrutta dal metodo uKey, più semplicemente il tasto è stato premuto e non ancora rilasciato.

Questo è il sistema più veloce che mi è venuto in mente per gestire questa cosa, veloce in termini di prestazioni. Avremmo potuto memorizzare gli stessi valori in un Array o definire una variabile singola per ognuno dei tasti che intendiamo gestire, però verificare il valore di una Variabile o cercare qualcosa in un Array richiedono a Flash Player uno sforzo maggiore che verificare soltanto se una Proprietà esiste oppure no. Inoltre non è necessario prevedere tutti i tasti che verranno premuti gia da adesso: questo sistema funzionerà con qualsiasi comando da tastiera.

Adesso non ci resta che provare il filmato:

  1. Apro Flash e creo un nuovo Filmato AS3.
  2. Imposto AsteroidsGame come Classe del Documento.
  3. Salvo il file .fla nella stessa cartella in cui si trova la Classe AsteroidsGame ed ecco qui:

No flash, please!

Purtroppo Adobe Flash Player non è più benvoluto come una volta, per questo motivo ho disabilitato tutte le applicazioni Flash presenti sul sito. Se vuoi puoi comunque scaricare il file SWF che si trovava qui, clicca qui per far partire il download

Potrebbero interessarti anche...

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.