Rilevamento collisione a livello di pixel

Fino ad ora, i nostri metodi di rilevamento delle collisioni sono stati basati matematicamente. Sebbene ciò sia utile, ci sono casi in cui l'approccio matematico non vale la pena, come con una forma irregolare e organica - i calcoli richiesti sono troppo complessi e costosi da giustificare. Invece, possiamo controllare ogni singolo pixel delle forme. Anche questo è un approccio costoso, ma può essere almeno ottimizzato.


Rilevamento collisione

Questo è l'ultimo pezzo che tenteremo di creare. Trascina il gancio sull'albero di cocco e osserva cosa dice il testo in fondo.


Step 1: uno per uno

Supponiamo di avere due bitmap e vorremmo verificare se si scontrano, pixel per pixel: cosa significa? Bene, supponiamo che entrambi i tuoi bitmap siano 3x3px e che tutti i pixel siano riempiti.

Faremo letteralmente questo:

  1. Controlla se a1 e b1 condividono la stessa posizione.
  2. Ripeti il ​​passaggio (1) ma ora per a1 e b2.
  3. Ripeti lo stesso tra a1 e b3, b4 ... b9.
  4. Ripeti il ​​passaggio da (1) a (3) per a2, a3 ... a9.

Ci sono alcune osservazioni che vorrei sottolineare.

Osservazione Descrizione
Pixel in alto a sinistra I pixel in alto a sinistra per entrambi i bitmap vengono utilizzati come pixel di partenza per i controlli. Ad esempio, a1 è il pixel iniziale controllato rispetto a tutti i pixel in b, che inizia con b1. Entrambi i pixel in alto a sinistra.
Progressione della linea di scansione Come accennato nel punto precedente, il controllo viene eseguito in ordine di a1, a2, a3 ... a9. Nota il modo in cui questi pixel sono disposti.
Spazio comune di coordinate Supponi che entrambi i grafici vengano aggiunti all'elenco di visualizzazione dello stage. La posizione di ciascun pixel in entrambe le bitmap, nello spazio delle coordinate dello stage, sarà confrontato per vedere se si verificano sovrapposizioni.
Calcolo costoso Per due bitmap 3x3, è necessario un massimo di 9x9 ripetizioni. Se la dimensione della mia bitmap va a 100x100, puoi vedere quanto velocemente cresce il calcolo totale. Tuttavia, se qualsiasi controllo restituisce un risultato positivo, il resto dei controlli può essere interrotto, poiché quando un pixel si sovrappone in entrambi i bitmap, possiamo dire che avviene una collisione tra le bitmap

Passaggio 2: considerazioni aggiuntive

Ora, il passaggio 1 può essere preso alla lettera se tutti i pixel sono riempiti. Con la grafica bitmap, definiamo un'area di dimensione rettangolare. Ma non tutti i pixel sono riempiti per formare il grafico.

L'esempio seguente mostra la bitmap giusta che occupa solo b2, b4, b5, b6 e b8. In questo caso, dovremmo controllare ogni pixel nella bitmap di sinistra (a1, a2, a3 ... a9) solo contro i pixel b2, b4, b5, b6, b8 nella bitmap giusta.

Ora ActionScript ci fornisce un altro parametro, alfa, che definisce la trasparenza del pixel, con 0 completamente trasparente e 1 completamente opaco. Per b2, b4, b5, b6, b8, possiamo definire un valore di soglia per alfa, dì 0.5.

Quindi, supponi che b2 e b8 siano entrambi pixel con alfa 0,1; poiché sono inferiori al valore di soglia di 0,5, non li considereremo pixel pieni e quindi non li controlleremo. Quindi, alla fine, ogni pixel nella bitmap di sinistra (a1, a2, a3 ... a9) viene controllato rispetto a b4, b5, b6 solo nella bitmap destra.


Passaggio 3: implementazione di ActionScript

In ActionScript, possiamo sovrapporre la grafica vettoriale a BitmapData le istanze. È possibile immaginare ActionScript che acquisisce una radiografia di un'immagine vettoriale e trasferirla in a BitmapData, che si comporta come il film fotografico.

(Suggerimento: se stai disegnando in Flash IDE e poi esporti in FlashDevelop come sto facendo, assicurati che le dimensioni del BitmapData sono abbastanza grandi da contenere il disegno).

Qui, Ctree e gancio sono due simboli MovieClip, disegnati in Flash; li "eseguiamo a raggi X" per ottenere un'istanza BitmapData per ciascuno:

 private var coconut: CTree, hk: Hook; private var bdat1: BitmapData, bdat2: BitmapData; private var t1: TextField; funzione pubblica Matrix_Bitmap () coconut = new CTree (); addChild (cocco); coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; bdat1 = new BitmapData (150, 150, true, 0x00000000); bdat1.draw (cocco); hk = new Hook (); addChild (HK); bdat2 = new BitmapData (100, 50, true, 0x00000000); bdat2.draw (HK); hk.addEventListener (MouseEvent.MOUSE_DOWN, start); hk.addEventListener (MouseEvent.MOUSE_UP, end); t1 = new TextField (); addChild (t1); t1.x = stage.stageWidth * 0.2; t1.y = stage.stageHeight * 0.8; t1.width = 300; t1. altezza = 100; stage.addEventListener (Event.ENTER_FRAME, check); 

Quindi, dopo, inizieremo i controlli usando il hitTest () metodo del BitmapData classe.

Su ogni frame che passa, aggiorneremo la posizione del pixel in alto a sinistra per ogni bitmap prima di inserire istanze di BitmapData attraverso questi rigidi hitTest () controlli. Si noti inoltre che l'intervallo per alfa l'input qui è 0 ~ 255 - cioè non c'è una soglia. Maggiori informazioni sulla trasparenza nel prossimo passaggio.

 controllo funzione privata (e: Event): void var point1: Point = new Point (coconut.x, coconut.y); // pixel in alto a sinistra dell'albero var point2: Point = new Point (hk.x, hk.y); // pixel in alto a sinistra di hook if (bdat1.hitTest (point1, 255, bdat2, point2, 255)) // controlla se i pixel pieni si sovrappongono t1.text = "Almeno un pixel si è scontrato" else t1 .text = "Nessuna collisione"

Ecco un esempio dell'output da ActionScript sopra. Clicca sul gancio e portalo vicino all'albero di cocco e controlla la risposta sulla casella di testo. Giocare con questo portando la fine del gancio vicino al bordo delle foglie dell'albero di cocco, per vedere se questa collisione è di precisione a livello di pixel.


Passaggio 4: Livello di trasparenza

Se si dispone di un'immagine che, per esempio, scompare gradualmente (diventando trasparente), si può dire a ActionScript a quale livello di trasparenza si considera un pixel adatto per eseguire i controlli di collisione.

Prendiamo l'esempio di seguito: ci sono diversi livelli di trasparenza sullo sprite e, come puoi vedere, viene gradualmente abbassato a destra. Se impostiamo il livello di trasparenza su 0,5, qualsiasi pixel con un alfa di 0,5 ~ 1 sarà considerato opaco e adatto al rilevamento delle collisioni. Quelle inferiori a 0,5 saranno considerate trasparenti. Anche quando questi pixel si scontrano con quello di un altro oggetto, non registreranno una collisione vera.

Un altro dettaglio che ho menzionato ora è ActionScript BitmapDataLa funzione hitTest alfa il valore del parametro varia effettivamente da 0 a 255. Quindi quello che faccio è semplicemente moltiplicare il mio valore di soglia per 255 per convertire l'intervallo.

 controllo di funzione privata (e: Event): void var point1: Point = new Point (bar1.x, bar1.y); var point2: Point = new Point (bar2.x, bar2.y); var threshold: Number = 255 * 0.5 if (bdat1.hitTest (point1, threshold, bdat2, point2, threshold)) t1.text = "Almeno un pixel si è scontrato" else t1.text = "Nessuna collisione" 

Passaggio 5: ottimizzazione

Ho menzionato che il rilevamento delle collisioni a livello di pixel è costoso dal punto di vista computazionale. Ciò significa che dovremmo solo optare per esso quando è strettamente necessario. Se due oggetti sono molto distanti, allora non c'è motivo di usare questo approccio e un normale rilevamento delle collisioni di delimitazione (hitTestObject ()) andrà bene.

Ecco l'idea:

  1. Uso hitTestObject () per vedere se i box di delimitazione di due oggetti si sono scontrati.
  2. Se la risposta è sì, allora questi due oggetti sono piuttosto vicini. Procedere con il controllo a livello di pixel.
  3. Se la risposta è no, allora questi due oggetti sono distanti. Termina i controlli di collisione senza controllo a livello di pixel.
 controllo funzione privata (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) var point1: Point = new Point (coconut.x, coconut.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 255, bdat2, point2, 255)) t1.text = "Almeno un pixel si è scontrato" else t1.text = "Nessuna collisione"

Per un riferimento ActionScript completo, controlla Matrix_Bitmap3.as dal download di origine.


Conclusione

Grazie per la lettura. Nel prossimo suggerimento rapido, utilizzeremo le matrici per trasformare BitmapData.