Pixel-nivå kollisionsdetektion

Hittills har våra kollisionsdetekteringsmetoder lagts matematiskt. Även om det här är till hjälp, finns det fall där det matematiska tillvägagångssättet inte bara är värt det, till exempel med oregelbunden organisk form - beräkningarna är för komplexa och dyra att motivera. Istället kan vi kontrollera varje enskild pixel av formerna. Detta är också ett dyrt tillvägagångssätt, men det kan åtminstone optimeras.


Kollisionsdetektering

Detta är det sista stycket vi ska försöka skapa. Dra kroken över kokosnötet och notera vad texten längst ner säger.


Steg 1: En efter en

Anta att vi har två bitmaps och vi vill kolla om de kolliderar, pixel med pixel: vad betyder det? Tja, låt oss anta att båda dina bitmaps är 3x3px, och alla pixlar är fyllda.

Vi gör bokstavligen detta:

  1. Kontrollera om a1 och b1 delar samma plats.
  2. Upprepa steg (1) men nu för a1 och b2.
  3. Upprepa samma mellan a1 och b3, b4 ... b9.
  4. Upprepa steg (1) till (3) för a2, a3 ... a9.

Det finns några observationer som jag skulle vilja påpeka.

Observation Beskrivning
Överst vänstra bildpunkter De övre vänstra bildpunkterna för båda bitmapparna används som startpunkt för kontroller. Exempelvis är a1 startpunkten kontrollerad mot alla pixlar i b, som börjar med b1. Båda vänstra bildpunkterna.
Scan-line progression Som nämnts i föregående punkt görs kontrollen i ordning av a1, a2, a3 ... a9. Observera hur dessa pixlar är ordnade.
Gemensamt koordinatutrymme Antag att båda bilderna läggs till i scenens visningslista. Platsen för varje pixel i båda bitmapparna, i scenens koordinatutrymme, kommer att jämföras för att se om några överlappningar uppstår.
Dyrt beräkning För två 3x3 bitmappar krävs högst 9x9 repetitioner. Om min bitmappsstorlek går till 100x100 kan du se hur snabbt den totala beräkningen växer. Om någon kontroll returnerar ett positivt resultat kan resten av kontrollerna avbrytas, eftersom när en pixel överlappar varandra i båda bitmapparna, kan vi säga att en kollision händer mellan bitmapparna

Steg 2: Extra överväganden

Nu kan steg 1 tas bokstavligen om alla pixlar är fyllda. Med bitmapgrafik definierar vi ett område med rektangulär dimension. Men inte alla pixlar fylls för att bilda grafiken.

Exemplet nedan visar rätt bitmapp som bara upptar b2, b4, b5, b6 och b8. I det här fallet bör vi kontrollera varje pixel i den vänstra bitmappen (a1, a2, a3 ... a9) mot endast pixlarna b2, b4, b5, b6, b8 i rätt bitmapp.

Nu ger ActionScript oss en annan parameter, alfa, som definierar pixelns transparens, varvid 0 är helt transparent och 1 är helt ogenomskinlig. För b2, b4, b5, b6, b8 kan vi definiera ett tröskelvärde för alfa, säg 0,5.

Så antar att b2 och b8 är båda pixlarna med alfa 0,1; Eftersom de är mindre än tröskelvärdet 0,5, anser vi inte att de ska fyllas pixlar och därför inte kontrollera dem. Så i slutändan kontrolleras varje pixel i den vänstra bitmappen (a1, a2, a3 ... a9) mot b4, b5, b6 endast i rätt bitmapp.


Steg 3: Implementering av ActionScript

I ActionScript kan vi överföra vektorgrafik till BitmapData instanser. Du kan tänka dig att ActionScript tar en röntgen av en vektorgrafik och överför den till en BitmapData, som fungerar som den fotografiska filmen.

(Tips: Om du skriver in Flash IDE och sedan exporterar till FlashDevelop så gör jag att du måste se till att dimensionerna för BitmapData är tillräckligt stora för att innehålla ritningen.)

Här, Ctree och Krok är två MovieClip-symboler, ritade i Flash; vi "X-ray" dem för att få en BitmapData-förekomst för varje:

 privat var kokosnöt: CTree, hk: Hook; privat var bdat1: BitmapData, bdat2: BitmapData; privat var t1: TextField; allmän funktion Matrix_Bitmap () kokosnöt = ny CTree (); addChild (kokosnöt); coconut.x = stage.stageWidth * 0,3; coconut.y = stage.stageHeight * 0.2; bdat1 = ny BitmapData (150, 150, true, 0x00000000); bdat1.draw (kokosnöt); hk = ny krok (); addChild (hk); bdat2 = ny BitmapData (100, 50, true, 0x00000000); bdat2.draw (hk); hk.addEventListener (MouseEvent.MOUSE_DOWN, start); hk.addEventListener (MouseEvent.MOUSE_UP, slutet); t1 = nytt TextField (); addChild (t1); t1.x = stadium.stageWidth * 0.2; t1.y = stadium.stageHeight * 0,8; t1.width = 300; t1. höjd = 100; stage.addEventListener (Event.ENTER_FRAME, check); 

Så efter det börjar vi kontrollerna genom att använda hitTest () metod för BitmapData klass.

På varje bildande ram uppdaterar vi platsen för den övre vänstra pixeln för varje bitmapp innan vi lägger instanser av BitmapData genom dessa rigorösa hitTest () kontroller. Notera också att intervallet för alfa input här är 0 ~ 255 - dvs det finns inget tröskelvärde. Mer om öppenhet i nästa steg.

 privatfunktionskontroll (e: Event): void var punkt1: Punkt = ny punkt (coconut.x, coconut.y); // topp-vänster pixel av träd var punkt2: Punkt = Ny punkt (hk.x, hk.y); // högsta vänster pixel av krok om (bdat1.hitTest (punkt1, 255, bdat2, punkt2, 255)) // kontrollera om några fyllda pixlar överlappar t1.text = "Minst en pixel har kolliderat" annat t1 .text = "Ingen kollision"

Här är ett exempel på produktionen från ActionScript ovan. Klicka på kroken och ta den nära kokosnötträdet och kolla svaret på textrutan. Leka med detta genom att föra änden av kroken nära kanten av kokosnötets löv, för att se om denna kollision är pixelnivåns precision.


Steg 4: OH-nivå

Om du har en bild som säger att du gradvis försvinner (blir transparent) kan du berätta ActionScript på vilken nivå av öppenhet du anser vara en pixel som passar för att utföra kollisionskontroller.

Ta exemplet nedan: Det finns flera nivåer av öppenhet på sprite och, som du kan se, sänkas den gradvis till höger. Om vi ​​ställer in transparensnivån till 0,5, anses en pixel med en alfa på 0,5 ~ 1 vara ogenomskinlig och lämpad för kollisionsdetektering. De som är lägre än 0,5 anses vara transparenta. Även när dessa pixlar kolliderar med ett annat objekt, registrerar de inte en riktig kollision.

En annan detalj som jag nämnde just nu är att ActionScript BitmapDatas hitTest funktion alfa parametervärdet varierar faktiskt från 0 ~ 255. Så vad jag gör är att multiplicera mitt tröskelvärde med 255 för att omvandla intervallet.

 privat funktionskontroll (e: Event): void var punkt1: Punkt = ny punkt (bar1.x, bar1.y); var punkt2: Punkt = ny punkt (bar2.x, bar2.y); var tröskelvärde: Number = 255 * 0.5 if (bdat1.hitTest (punkt1, tröskelvärde, bdat2, punkt2, tröskelvärde)) t1.text = "Minst en pixel har kolliderat" else t1.text = "No collision" 

Steg 5: Optimering

Jag har nämnt att kollisionsdetektering på pixelnivå är beräkningsmässigt dyrt. Det betyder att vi bara väljer det när det är absolut nödvändigt. Om två föremål är väldigt långt ifrån varandra är det ingen anledning att använda detta tillvägagångssätt, och en normal kollisionsdetektering av avgränsningsboxen (hitTestObject ()) ska göra.

Här är idén:

  1. Använda sig av hitTestObject () för att se om två objektets gränsvålar har kolliderat.
  2. Om svaret är ja, är dessa två objekt ganska nära. Fortsätt med kontroll av pixelnivå.
  3. Om svaret är nej, är dessa två objekt långt ifrån varandra. Avsluta kollisionskontroll utan kontroll av pixelnivå.
 privat funktionskontroll (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) om (closeEnough) var punkt1: Punkt = ny punkt (coconut.x, coconut.y); var punkt2: Punkt = ny punkt (hk.x, hk.y); om (bdat1.hitTest (punkt1, 255, bdat2, punkt2, 255)) t1.text = "Minst en pixel har kolliderat" annars t1.text = "Ingen kollision"

För en fullständig ActionScript-referens, kolla in Matrix_Bitmap3.as från källans nedladdning.


Slutsats

Tack för läsningen. I nästa Quick Tip använder vi matriser för att omvandla BitmapData.