Realizzare Asteroids [parte 8]

In questo Tutorial ci scontreremo con uno dei problemi più grossi che si incontrano nella realizzazione di un gioco in Flash: la Collisione.

Abbiamo quasi finito il gioco Asteroids, con qualche licenza, ma non ci siamo ancora occupati di questo aspetto perché ho voluto tenerlo per ultimo.

Prima di avventurarci in questa impresa vediamo quali sono gli strumenti che abbiamo a disposizione in AS3:

  • Metodo hitTestObject della Classe DisplayObject.
  • Metodo hitTestPoint della Classe DisplayObject.

Vediamo come funzionano e se fanno al caso nostro…

hitTestObject

La documentazione di ActionScript dice questo:

hitTestObject(obj:DisplayObject):Boolean
// Valuta l'oggetto di visualizzazione per stabilire se si sovrappone o si interseca con l'oggetto di visualizzazione obj.

Sembrerebbe sufficiente scrivere una cosa simile a questa:

Asteroide.hitTestObject(Astronave)

Purtroppo però non è così: il Metodo hitTestObject non confronta la forma degli oggetti, confronta i loro Boundary Box, che sono rettangolari.

Il Boundary Box di un’Oggetto di Visualizzazione è il più piccolo rettangolo in grado di contenere l’Oggetto stesso. Il Boundary Box corrisponde esattamente all’Oggetto di Visualizzazione solo nel caso in cui questo sia rettangolare e non sia ruotato.

Quello che succede quando proviamo a verificare la collisione tra un Asteroide e l’Astronave è più comprensibile osservando l’immagine qui sotto:

Esempio di Collisione con hitTestObject

Esempio di Collisione con hitTestObject

Come si vede nell’immagine l’Astronave e l’Asteroide non si toccano affatto, ma i loro Boundary (le linee tratteggiate) sono intersecati. Se usassimo questo sistema per verificare la Collisione i risultati non sarebbero soddisfacenti.

hitTestPoint

Questo metodo è molto più preciso del precedente, ci dice se un Oggetto di Visualizzazione è sovrapposto a un punto, date le coordinate. Lo stiamo già usando per intercettare la collisione tra gli Asteroidi e i Proiettili sparati dall’Astronave.

Purtroppo, se il trucco funziona con i Proiettili – che sono tondi e piccoli, quindi semplificabili con un Punto – non funzionerà con l’Astronave. Ecco cosa succederebbe se usassimo questo sistema per intercettare la Collisione tra le Coordinate dell’Astronave e un Asteroide:

Esempio di Collisione con hitTestPoint

Esempio di Collisione con hitTestPoint

Come si vede nell’immagine hitTestPoint non è sufficiente per intercettare la Collisione tra l’Astronave e l’Asteroide, infatti se usassimo come riferimento il centro dell’Astronave (evidenziato in rosa) tutto il resto della grafica non sarebbe sotto controllo. Anche intersecandosi visibilmente, le due forme non risulterebbero collidere.

Improvvisiamo?

Visto che nessuno dei due Metodi disponibili ci fornisce la soluzione immediata al problema della Collisione ci troviamo a dover inventare un sistema che funzioni. Esistono centinaia di approcci possibili al problema della Collisione, tra tutti penso che il più geniale sia quello di Grant Skinner (Shape Based Collision Detection), scritto in AS2 (ma si trovano in rete numerose conversioni di questa tecnica in AS3).

Noi non useremo il sistema di Skinner, un po’ perché ho la presunzione di voler sempre essere originale, un po’ perché non mi va di inserire una logica basata sui pixel in un progetto che fino ad ora ha fatto a meno delle Bitmap.

Il disegno dell’Astronave di questo gioco non è poi così complesso, ho pensato che basterà verificare la collisione di tre soli punti per ottenere un risultato soddisfacente.

Tre punti per descrivere l’Astronave

All’interno della Classe Player inserisco tre Proprietà, ognuna di queste Proprietà ci fornirà un Punto che fa parte del perimetro dell’Astronave. Il disegno qui sotto mostra quali punti intendo utilizzare:

Tre Punti per descrivere la Forma dell'Astronave.

Tre Punti per descrivere la Forma dell’Astronave.

All’interno della Classe Player inserisco i Metodi che mi restituiranno questi Punti:

public function get p1():Point {
    return new Point(0,-this.height/2);
}
public function get p2():Point {
    return new Point(this.width/2,this.height/2);
}
public function get p3():Point {
    return new Point(-this.width/2,this.height/2);
}

Il controllo della Collisione tra gli Asteroidi e questi tre punti lo inseriremo nella Classe AsteroidsGame, all’interno del Metodo goOn. Visto che esiste già un ciclo for per il controllo della Collisione tra un Asteroide e il Proiettile, lo modificheremo per fargli eseguire tutti i controlli di Collisione. Diventa così:

var p1 = _p.localToGlobal(_p.p1);
var p2 = _p.localToGlobal(_p.p2);
var p3 = _p.localToGlobal(_p.p3);
for (var i = 0; i < _asteroidsArray.length; i++) {
    _asteroidsArray[i].muovi();
    var hit1:Boolean = _asteroidsArray[i].hitTestPoint(p1.x, p1.y, true);
    var hit2:Boolean = _asteroidsArray[i].hitTestPoint(p2.x, p2.y, true);
    var hit3:Boolean = _asteroidsArray[i].hitTestPoint(p3.x, p3.y, true)
    if (hit1 || hit2 || hit3) {
        gameOver();
    } else if (_asteroidsArray[i].hitTestPoint(_b.x,_b.y,true) && _b.parent) {
        this.removeChild(_b);
        splitAsteroid(i);
    }
}

La prima parte recupera i tre punti di cui abbiamo già parlato dall’Istanza dell’Astronave e li converte dalle coordinate interne dell’Astronave alle coordinate Globali dello Stage (così quando spostiamo o ruotiamo l’Astronave riusciamo sempre a sapere le coordinate di questi tre punti sullo Stage).

Nel caso in cui un Asteroide abbia colpito uno dei tre punti dell’Astronave viene invocata una funzione che non esiste ancora, ma la cui funzione è chiara: si chiama gameOver.

Game Over

La Funzione gameOver ha il compito di riportare tutti gli elementi di gioco allo stato originale, quindi:

  1. Reimposta posizione, rotazione e accelerazione dell’Astronave ai valori iniziali.
  2. Elimina tutti gli Asteroidi presenti e ricrea i 4 iniziali.
  3. Reimposta il delay del Timer a 40.
  4. Azzera il punteggio.

Ecco qui la Funzione:

private function gameOver() {
    _p.x = stage.stageWidth/2;
    _p.y = stage.stageHeight/2;
    _p.rotation = 0;
    _p._boostX = 0;
    _p._boostY = 0;
    while (_asteroidsArray.length > 0) {
        var removeIndex = _asteroidsArray.length - 1;
        this.removeChild(_asteroidsArray[removeIndex]);
        _asteroidsArray.splice(removeIndex,1);
    }
    addAsteroids();
    _gameTime.delay = 40;
    _score = 0;
}

Per modificare i valori di _boostX e _boostY dell’Astronave è necessario modificare le righe di inizializzazione all’interno della Classe Player, sostituendo la direttiva private con public (altrimenti non sarebbero accessibili dall’esterno).

Ecco il risultato finale:

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...

3 Risposte

  1. Marco ha detto:

    Interessante!
    Si potrebbe avere un pdf che riunisce tutte le 8 parti del tutorial?

  2. luca rivara ha detto:

    bello! ma veramente complicato, bisogna essere molto portati per programmare (e avere risorse)

    • Daniele Alessandra ha detto:

      Grazie per il tuo apprezzamento… più che altro credo che serva tanta pazienza e tempo libero per realizzare questi giochini, poi in rete si trovano tantissime risorse.

      Leggo sul tuo blog che hai intenzione di realizzare un gioco in AS3 e di rendere pubblico il sorgente, divertente… se hai bisogno di una mano chiedi pure ;)

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.