Rilevamento di collisione a livello di pixel per grafica trasformata

Nel precedente tutorial, abbiamo esaminato le basi del rilevamento delle collisioni a livello di pixel. In questo tutorial, esploreremo l'uso delle matrici per definire meglio l'area di interesse, molto utile per grafici che sono stati ruotati, tradotti o inclinati.


Rilevamento collisione

Questo è l'ultimo pezzo che tenteremo di programmare. Fare clic sulla palla e agganciare per avviare la demo interattiva.

Notate come, nonostante la rotazione dell'albero del cocco e la sua trasformazione, abbiamo ancora il rilevamento delle collisioni pixel-perfect.


Step 1: The disegnare Funzione

L'esecuzione del rilevamento delle collisioni a livello di pixel richiede che l'immagine sia trasmessa su a BitmapData oggetto. Abbiamo esaminato questo aspetto nel precedente tutorial usando l'analogia di una radiografia. In questo tutorial, spiegherò questo processo di "radiografia".

Supponiamo di avere un pezzo di un grafico (diagramma 1) e di volerlo trasmettere a BitmapData oggetto; dobbiamo definire le dimensioni del BitmapData prima l'oggetto (diagramma 2). In questo caso, è abbastanza semplice perché il grafico larghezza e altezza proprietà forniscono questo. Quindi chiamiamo il disegnare() metodo; i pixel che sono almeno parzialmente occupati dal grafico saranno riempiti, in teoria (diagramma 3). Questa matrice di pixel verrà confrontata con un'altra serie di pixel di un altro grafico per verificare una collisione tra i due (diagramma 4).


Passaggio 2: diversi spazi di coordinate

Ci sono diversi spazi di coordinate usati in Flash IDE (diagrammi 1 e 2 sopra). Sono sicuro che ogni lettore avrebbe sperimentato questo - vedi il mio tutorial sugli spazi affini per un aspetto più dettagliato.

Nell'ID Flash, disegniamo un'immagine e la trasformiamo in un simbolo di tipo Un filmato. Quando facciamo doppio clic sul Un filmato, arriviamo a uno spazio di coordinate diverso (diagramma 3). Da qui, possiamo fare clic sull'etichetta dello stage per uscire dallo spazio delle coordinate locali di questo grafico e arrivare allo spazio delle coordinate dello stage. Quindi possiamo iniziare a trasformare l'istanza di Un filmato sul palco (diagramma 4). In effetti possiamo creare più istanze di Un filmato, ognuno di loro ha diverse trasformazioni.

Nella Libreria, la grafica originale rimane invariata nonostante tutte le modifiche apportate alle sue copie sul palco. Questo è razionale perché ogni volta che creiamo una nuova copia di a Un filmato sul palco, sono sempre gli stessi della copia originale nella libreria. Ora la domanda è: "In che modo Flash cattura tutta la trasformazione che abbiamo apportato alle copie sul palco?" Bene, ognuno usa il MovieClip.transform.matrix proprietà per catturare tutte le tue trasformazioni (traduzione, rotazione, inclinazione, ecc.).

Ora torniamo al punto in cui avevamo interrotto. È fondamentale che lo comprendiamo perché il disegnare() metodo di BitmapData non si riferisce all'istanza grafica sullo stage quando si esegue una "radiografia", ma piuttosto alla sorgente grafica non modificata nella libreria.

Il BitmapData il primo pixel dell'oggetto si allinea con il punto di registrazione (il punto rosso) del Un filmato nello spazio delle coordinate locali (fare riferimento al diagramma 3 nel passaggio 1), quindi acquisisce l'immagine in forma di pixel con le dimensioni che specifichiamo.

Quando si tratta di hitTest controlla, ActionScript allinea questo primo pixel (il pixel in alto a sinistra) di BitmapData oggetto con il punto di registrazione dell'istanza grafica sullo stage. Con questo, tutti i pixel nel BitmapData l'oggetto sarà mappato sullo spazio delle coordinate sul palco e ottenere le loro coordinate individuali. I controlli possono essere eseguiti successivamente confrontando queste coordinate tra due bitmap per vedere se i pixel si sovrappongono.

Nota: Questa spiegazione presuppone il Un filmato o folletto l'istanza viene aggiunta all'elenco di visualizzazione dello stage. In ActionScript, possiamo effettivamente aggiungere oggetti di visualizzazione alla classe del documento stessa perché si estende Un filmato o folletto.


Passaggio 3: il problema

Quindi, se la grafica di interesse viene ruotata sul palco, come eseguiamo disegnare()?

Dal diagramma sopra, possiamo vedere chiaramente questi problemi.

Diagramma Problema Descrizione
1 Dimensione di BitmapData oggetto Poiché l'orientamento dell'oggetto è cambiato, la dimensione richiesta di BitmapData il cast non può più essere preso convenientemente dal larghezza e altezza proprietà dell'istanza grafica.
2 Orientamento della fonte grafica L'istanza del grafico sullo stage viene ruotata, ma quella nella libreria non lo è. Abbiamo bisogno di scattare un'istantanea della sorgente grafica trasformata dalla libreria.
3 Coordinata del pixel in alto a sinistra (pixel iniziale) di BitmapData oggetto Non possiamo allineare il BitmapData primo pixel dell'oggetto con il punto di registrazione dell'istanza grafica. Questo sarà errato.

Passaggio 4: la soluzione

Per risolvere questi problemi, prima definiremo un rettangolo che delimita strettamente l'istanza grafica ruotata sullo stage. Disposizioni di ActionScript per questo tramite il getBounds () funzione. Questo risolverà il nostro primo problema. Osserva l'immagine qui sotto. Si noti che c'è una differenza tra il punto di registrazione dell'istanza grafica e quello del rettangolo.

Ho incluso la presentazione Flash di seguito per mostrare il riquadro di delimitazione rettangolare (riquadro rosso) e il riquadro di delimitazione dello spazio locale (scatola nera)


Passaggio 5: trasformazione

Successivamente, porteremo un'istantanea della sorgente grafica transforrmed ​​in questo rettangolo sul palco. Osserva l'immagine qui sotto.

Iniziamo con il punto di registrazione della sorgente grafica allineato con quello dell'area rettangolare (diagramma 1). Quindi, ruotiamo (diagramma 2) e lo compensiamo (diagramma 3) prima di prendere la cattura "a raggi X" dell'immagine sul BitmapData oggetto (diagramma 4).

Possiamo farlo manualmente o scegliere di fare una copia delle istanze grafiche transform.matrix proprietà. Quando si utilizza il secondo approccio, dovremmo fare attenzione a non utilizzare il transform.matrix traduzione proprietà - in caso contrario, i punti di registrazione non saranno allineati come si vede nel diagramma 1. In entrambi i casi, dovremo calcolare la distanza x e y da sfalsare.


Passaggio 6: implementazione di ActionScript

Dopo questa lunga spiegazione, spero sia più facile capire il codice. Ho evidenziato le linee importanti e ho aggiunto commenti:

 private var coconut: CTree, hk: Hook; private var bdat1: BitmapData, bdat2: BitmapData; private var t1: TextField; angolo var privato: Number = 45 private var coconutBox: Rectangle; funzione pubblica Matrix_Bitmap4 () coconut = new CTree (); addChild (cocco); coconut.rotation = angle; coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; coconutBox = coconut.getBounds (questo); // ottiene una casella rettangolare nel passaggio 2 var coconut_newX: Number = coconut.x - coconutBox.x // ottieni offset x nel passaggio 3 var coconut_newY: Number = coconut.y - coconutBox.y // ottieni offset y nel passaggio 3 var m : Matrix = new Matrix (); m.rotate (angolo / 180 * Math.PI); // ruota l'immagine nel passaggio 3 // var m: Matrix = coconut.transform.matrix // consigliato se è avvenuta una trasformazione di molte dimensioni. //m.tx = 0; m.tà = 0; // In questo caso, sta facendo lo stesso lavoro della riga precedente. m.translate (coconut_newX, coconut_newY); // implementa l'offset bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); bdat1.draw (cocco, m);

Inoltre, non dimenticare di cambiare la posizione del primo pixel (in alto a sinistra) in BitmapData oggetto a quello della scatola rettangolare

 controllo funzione privata (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) // var point1: Point = new Point (coconut.x, coconut.y); // ora che abbiamo una casella diversa con posizione diversa per iniziare pixel, // dovremmo fare riferimento a coconutBox come punto di partenza var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Almeno un pixel si è scontrato" else t1.text = "Nessuna collisione"

Ed ecco un esempio del lavoro.


Passaggio 7: Aggiornamento costante

Se la forma di interesse, in questo caso l'albero di cocco, è in costante trasformazione (rotazione, ridimensionamento, restringimento, inclinazione, ecc.), Allora la BitmapData l'oggetto deve essere aggiornato su ogni frame e questo richiederà un po 'di elaborazione. Inoltre, si noti che ho optato per l'approccio alternativo menzionato nel passaggio 4. Ecco lo script per aggiornare la copia dei raggi X del grafico per ciascun fotogramma:

 funzione privata updateBmp (): void coconutBox = coconut.getBounds (this); // ottiene una casella rettangolare nel passaggio 2 var coconut_newX: Number = coconut.x - coconutBox.x // ottieni offset x nel passaggio 3 var coconut_newY: Number = coconut.y - coconutBox.y // ottieni offset y nel passaggio 3 // var m: Matrix = new Matrix (); //m.rotate(angle / 180 * Math.PI); // ruota il grafico nel passaggio 3 var m: Matrix = coconut.transform.matrix // consigliato se sono avvenute molte trasformazioni, m.tx = 0; m.tà = 0; // per questo caso, sta facendo lo stesso lavoro della riga precedente. m.translate (coconut_newX, coconut_newY); // implementa l'offset bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); b = new Bitmap (bdat1); addChild (b); b.x = stage.stageWidth * 0.3; b.y = stage.stageHeight * 0.2; bdat1.draw (cocco, m); 

La seguente funzione viene eseguita ogni fotogramma:

 controllo di funzione privata (e: Event): void coconut.rotation + = angle; // modifiche dinamiche in fase di esecuzione coconut.scaleX + = 0.01 var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) updateBmp (); var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Almeno un pixel si è scontrato" else t1.text = "Nessuna collisione" bdat1.dispose (); 

E questo è l'output. Inizia a trascinare il gancio per vedere l'animazione.


Conclusione

Capisco che questo tutorial potrebbe non essere troppo veloce da leggere, ma è fondamentale acquisire una chiara comprensione di ciò che sta accadendo. Spero che questo sia stato utile e se sei interessato a manipolare di più questa matrice 2x2, visita il mio articolo sull'argomento. Grazie.