Detectarea coliziunii la nivel de pixel

Până acum, metodele noastre de detectare a coliziunilor au fost bazate matematic. Deși acest lucru este util, există cazuri în care abordarea matematică nu merită, cum ar fi o formă neregulată, organică - calculele necesare sunt prea complexe și costisitoare de justificat. În schimb, putem verifica fiecare pixel individual al formelor. Aceasta este și o abordare costisitoare, dar poate fi cel puțin optimizată.


Detectarea coliziunii

Aceasta este piesa finală pe care vom încerca să o creăm. Trageți cârligul peste arborele de nucă de cocos și notați ce spune textul din partea inferioară.


Pasul 1: unul câte unul

Să presupunem că avem două bitmap-uri și am dori să verificăm dacă se ciocnesc, pixel după pixel: ce înseamnă asta? Să presupunem că ambele fișiere bitmap sunt de 3x3 pixeli și toți pixelii sunt umpluți.

Vom face literalmente acest lucru:

  1. Verificați dacă a1 și b1 au aceeași locație.
  2. Repetați pasul (1), dar acum pentru a1 și b2.
  3. Repetați același lucru între a1 și b3, b4 ... b9.
  4. Repetați pașii (1) la (3) pentru a2, a3 ... a9.

Sunt câteva observații pe care aș vrea să le subliniez.

Observare Descriere
Top pixeli stânga Pixelii din stânga sus pentru ambele fișiere bitmap sunt utilizați ca pixel de pornire pentru verificări. De exemplu, a1 este pixelul de pornire verificat împotriva tuturor pixelilor din b, care începe cu b1. Ambii pixeli din stânga sus.
Progresia liniei de scanare După cum sa menționat în punctul anterior, verificarea se face în ordinea a1, a2, a3 ... a9. Rețineți modul în care sunt aranjați aceste pixeli.
Spațiu comun de coordonate Se presupune că ambele grafice sunt adăugate la lista de afișare a etapei. Locația fiecărui pixel în ambele fișiere bitmaps, în spațiul de coordonate al etapei, vor fi comparate pentru a vedea dacă apar suprapuneri.
Calculul scump Pentru două bitmapuri 3x3, este necesară o repetare de maxim 9x9. Dacă mărimea bitmap-ului meu merge la 100x100, puteți vedea cât de repede crește calculul total. Cu toate acestea, dacă un singur test întoarce un rezultat pozitiv, restul verificărilor poate fi anulat, deoarece atunci când un pixel se suprapune în ambele rapoarte de imagine, putem spune că se întâmplă o coliziune între imaginile bitmaps

Pasul 2: Considerații suplimentare

Acum, pasul 1 poate fi luat literal dacă toți pixelii sunt umpluți. Cu grafică bitmap, definim o arie de dimensiune dreptunghiulară. Dar nu toți pixelii sunt completați pentru a forma graficul.

Exemplul de mai jos arată bitmap-ul drept ocupând doar b2, b4, b5, b6 și b8. În acest caz, ar trebui să verificăm fiecare pixel din bitmap-ul stâng (a1, a2, a3 ... a9) numai față de pixelii b2, b4, b5, b6, b8 din bitmap-ul drept.

Acum, ActionScript ne oferă un alt parametru, alfa, care definește transparența pixelului, 0 fiind complet transparentă și 1 total opacă. Pentru b2, b4, b5, b6, b8, putem defini o valoare de prag pentru alfa, spune 0.5.

Deci, presupuneți că b2 și b8 sunt ambii pixeli cu alfa 0,1; deoarece acestea sunt mai mici decât valoarea de prag de 0,5, nu le vom considera a fi umplut pixeli, și, prin urmare, nu le verifica. Așadar, în final, fiecare pixel din bitmap-ul stâng (a1, a2, a3 ... a9) este bifat pe b4, b5, b6 numai în graficul din dreapta.


Pasul 3: Implementarea ActionScript

În ActionScript, putem suprapune grafica vectorială în BitmapData instanțe. Vă puteți imagina dacă ActionScript realizează o rază X a unei imagini vectoriale și o transferă în a BitmapData, care acționează ca filmul fotografic.

(Sfat: Dacă desenați Flash IDE și apoi exportați în FlashDevelop așa cum fac, asigurați-vă că dimensiunile BitmapData sunt suficient de mari pentru a conține desenul.)

Aici, Ctree și Cârlig sunt două simboluri MovieClip, desenate în Flash; noi le "X-ray" pentru a obține o instanță BitmapData pentru fiecare:

 privat Var de nucă de cocos: CTree, hk: Hook; privat var bdat1: BitmapData, bdat2: BitmapData; privat var t1: TextField; funcția publică Matrix_Bitmap () coconut = CTree nou (); addChild (cocos); coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; bdat1 = BitmapData nou (150, 150, true, 0x00000000); bdat1.draw (cocos); hk = nou Cârlig (); addChild (hk); bdat2 = BitmapData nou (100, 50, true, 0x00000000); bdat2.draw (hk); hk.addEventListener (MouseEvent.MOUSE_DOWN, start); hk.addEventListener (MouseEvent.MOUSE_UP, sfârșit); t1 = TextField nou (); addChild (t1); t1.x = stage.stageWidth * 0.2; t1.y = stage.stageHeight * 0.8; t1.width = 300; T1. înălțime = 100; stage.addEventListener (Event.ENTER_FRAME, bifați); 

După aceea, vom începe verificările utilizând hitTest () metodă a BitmapData clasă.

Pe fiecare cadru de trecere, vom actualiza locația pixelului din stânga sus pentru fiecare bitmap înainte de a pune instanțe din BitmapData prin aceste riguroase hitTest () cecuri. Rețineți, de asemenea, că intervalul pentru alfa introducerea aici este 0 ~ 255 - adică nu există niciun prag. Mai multe despre transparență în pasul următor.

 verificarea funcției private (e: Event): void var point1: Point = punct nou (coconut.x, coconut.y); // pixelul de sus-stânga al arborelui var punctul2: Point = new Point (hk.x, hk.y); // pixelul de sus-stânga al cârligului dacă (bdat1.hitTest (punctul1, 255, bdat2, point2, 255)) // verificați dacă se suprapun pixelii pliniți t1.text = "Cel puțin un pixel sa ciocnit" altceva t1 .text = "Nicio coliziune"

Iată un exemplu de ieșire din ActionScript de mai sus. Faceți clic pe cârlig și apropiați-l de copacul de nucă de cocos și verificați răspunsul în caseta de text. Joacă-te cu asta prin a aduce capătul cârligului aproape de marginea frunzelor de cocos, pentru a vedea dacă această coliziune este de precizie la nivel de pixeli.


Pasul 4: Nivelul de transparență

Dacă aveți o imagine care, să zicem, dispare treptat (devenind transparentă), puteți spune ActionScript la ce nivel de transparență considerați că un pixel se potrivește pentru a efectua verificări de coliziune.

Luați exemplul de mai jos: există mai multe niveluri de transparență pe sprite și, după cum puteți vedea, se coboară treptat spre dreapta. Dacă setăm nivelul de transparență la 0.5, atunci orice pixel cu alfa de 0.5 ~ 1 va fi considerat opac și potrivit pentru detectarea coliziunii. Cele mai mici de 0,5 vor fi considerate transparente. Chiar și atunci când acești pixeli se ciocnesc cu cel al unui alt obiect, nu vor înregistra o adevărată coliziune.

Un alt detaliu pe care l-am menționat doar acum este că ActionScript BitmapData's hitTest alfa valoarea parametrului variază de la 0 ~ 255. Deci, ceea ce fac este pur și simplu să îmi înmulțez valoarea de prag cu 255 pentru a converti intervalul.

 verificarea funcției private (e: Eveniment): void var point1: Point = new Point (bar1.x, bar1.y); var punctul2: Punct = punct nou (bar2.x, bar2.y); Varful var: Numărul = 255 * 0,5 dacă (bdat1.hitTest (punctul1, pragul, bdat2, punctul2, pragul)) t1.text = "Cel puțin un pixel sa ciocnit" altceva t1.text = "No collision" 

Pasul 5: Optimizare

Am menționat că detecția coliziunii la nivel de pixel este computațional scumpă. Asta înseamnă că trebuie să optăm doar atunci când este strict necesar. Dacă două obiecte sunt foarte îndepărtate, atunci nu există niciun motiv să se folosească această abordare și o detectare normală a coliziunii casetă delimitare (hitTestObject ()) se va face.

Iată ideea:

  1. Utilizare hitTestObject () pentru a vedea dacă cutiile de delimitare a două obiecte s-au ciocnit.
  2. Dacă răspunsul este da, atunci aceste două obiecte sunt destul de aproape. Continuați cu verificarea nivelului pixelilor.
  3. Dacă răspunsul este negativ, atunci aceste două obiecte sunt foarte îndepărtate. Sfârșitul verificărilor de coliziune fără verificarea nivelului pixelilor.
 verificarea funcției private (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) dacă (closeEnough) var point1: Point = new Point (coconut.x, coconut.y); var punctul2: Punct = punct nou (hk.x, hk.y); dacă bdat1.hitTest (punctul1, 255, bdat2, point2, 255)) t1.text = "Cel puțin un pixel sa ciocnit" altceva t1.text = "Nicio coliziune"

Pentru o referință completă ActionScript, verificați Matrix_Bitmap3.as de la descărcarea sursei.


Concluzie

Mulțumesc pentru citire. În următorul Sfat rapid, vom folosi matrici pentru transformare BitmapData.

Cod