Pikselnivå Kollisjonsdeteksjon

Hittil har våre kollisjonsdeteksjonsmetoder vært basert på matematikk. Selv om dette er nyttig, er det tilfeller hvor den matematiske tilnærmingen bare ikke er verdt det, for eksempel med en uregelmessig, organisk form - de nødvendige beregningene er for komplekse og dyre å begrunne. I stedet kan vi sjekke hver enkelt piksel av figurene. Dette er også en kostbar tilnærming, men det kan i det minste optimeres.


Kollisjonsdeteksjon

Dette er det siste stykket vi skal prøve å lage. Dra kroken over kokosetreet, og merk hva teksten nederst sier.


Trinn 1: En etter en

Anta at vi har to bitmaps, og vi vil gjerne sjekke om de kolliderer, piksel etter piksel: hva betyr det? Vel, la oss anta at begge bitmapene dine er 3x3px, og alle pikslene er fylt.

Vi skal bokstavelig talt gjøre dette:

  1. Sjekk om a1 og b1 deler samme sted.
  2. Gjenta trinn (1), men nå for a1 og b2.
  3. Gjenta det samme mellom a1 og b3, b4 ... b9.
  4. Gjenta trinn (1) til (3) for a2, a3 ... a9.

Det er noen observasjoner som jeg vil påpeke.

observasjon Beskrivelse
Øverste venstre piksler De øverste venstre pikslene for begge bitmaps brukes som startpunkt for kontroller. For eksempel er a1 startpunktet kontrollert mot alle piksler i b, som starter med b1. Begge øverste venstre piksler.
Scan-line-progresjon Som nevnt i forrige punkt, er sjekken gjort i rekkefølge av a1, a2, a3 ... a9. Legg merke til hvordan disse pikslene er ordnet.
Felles koordinatrom Anta at både grafikk legges til scenens visningsliste. Plasseringen av hver piksel i begge bitmaps, i scenens koordinatrom, vil bli sammenlignet for å se om det forekommer overlappinger.
Dyrt beregning For to 3x3 bitmaps er det nødvendig med maks 9x9 repetisjoner. Hvis bitmapstørrelsen går til 100x100, kan du se hvor raskt totalberegningen vokser. Men hvis en tjekke returnerer et positivt resultat, kan resten av kontrollene avbrytes, siden når en pixel overlapper i begge bitmaps, kan vi si at det skjer en kollisjon mellom bitmaps

Trinn 2: Ekstra hensyn

Nå kan trinn 1 tas bokstavelig talt hvis alle piksler er fylt. Med bitmap-grafikk definerer vi et område med rektangulær dimensjon. Men ikke alle piksler er fylt for å danne grafikken.

Eksemplet under viser riktig bitmap som bare tar b2, b4, b5, b6 og b8. I dette tilfellet bør vi sjekke hver piksel i venstre bitmap (a1, a2, a3 ... a9) mot bare pikslene b2, b4, b5, b6, b8 i den riktige bitmappen.

Nå gir ActionScript oss en annen parameter, alfa, som definerer gjennomsiktigheten av pikselet, idet 0 er helt gjennomsiktig og 1 er helt ugjennomsiktig. For b2, b4, b5, b6, b8 kan vi definere en grenseverdi for alfa, si 0,5.

Så antar at b2 og b8 er begge piksler med alfa 0,1; fordi de er mindre enn terskelverdien på 0,5, vil vi ikke vurdere at de fylles piksler og derfor ikke sjekker dem. Så til slutt blir hver piksel i venstre bitmap (a1, a2, a3 ... a9) merket mot b4, b5, b6 i bare bitmap.


Trinn 3: Implementering av ActionScript

I ActionScript kan vi overordne vektorgrafikk inn i BitmapData forekomster. Du kan forestille deg at ActionScript tar en røntgen av en vektorgrafikk, og overfører den til en BitmapData, som fungerer som fotografisk film.

(Tips: Hvis du tegner i Flash IDE og deretter eksporterer til FlashDevelop som jeg gjør, må du kontrollere at dimensjonene til BitmapData er store nok til å inneholde tegningen.)

Her, Ctree og Hook er to MovieClip-symboler, tegnet i Flash; vi "X-ray" dem for å få en BitmapData-forekomst for hver:

 privat var kokosnøtt: CTree, hk: Hook; private var bdat1: BitmapData, bdat2: BitmapData; privat var t1: TextField; offentlig funksjon Matrix_Bitmap () kokosnøtt = ny CTree (); addChild (kokos); coconut.x = stage.stageWidth * 0,3; coconut.y = scene.stageHeight * 0.2; bdat1 = ny BitmapData (150, 150, true, 0x00000000); bdat1.draw (kokos); 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, end); t1 = nytt TextField (); addChild (t1); t1.x = stage.stageWidth * 0.2; t1.y = scene.stageHeight * 0,8; t1.width = 300; t1. høyde = 100; stage.addEventListener (Event.ENTER_FRAME, sjekk); 

Så etter det begynner vi kontrollene ved å bruke hitTest () metode av BitmapData klasse.

På hver ramme som passer, oppdaterer vi plasseringen av den øverste venstre piksel for hver bitmap før du setter forekomster av BitmapData gjennom disse rigorøse hitTest () sjekker. Legg også merke til at rekkevidden for alfa input her er 0 ~ 255 - det vil si at det ikke er noen grense. Mer om åpenhet i neste trinn.

 privat funksjonskontroll (e: Event): void var punkt1: Punkt = nytt punkt (coconut.x, coconut.y); // øverste venstre piksel av tre var punkt2: Punkt = nytt punkt (hk.x, hk.y); // øverste venstre piksel av krok hvis (bdat1.hitTest (punkt1, 255, bdat2, punkt2, 255)) // kontroller om noen fulle piksler overlapper t1.text = "Minst ett piksel har kollidert" annet t1 .text = "Ingen kollisjon"

Her er et eksempel på produksjonen fra ActionScript ovenfor. Klikk på kroken og ta den nær kokosnøtten og kontroller svaret på tekstboksen. Spill rundt med dette ved å bringe enden av kroken nær kanten av kokosnøttens blader, for å se om denne kollisjonen er presis med pikselnivå.


Trinn 4: Gjennomsiktighetsnivå

Hvis du har et bilde som er, for eksempel, forsvinner (blir gjennomsiktig), kan du fortelle ActionScript på hvilket nivå av gjennomsiktighet du anser som en piksel som passer for å utføre kollisjonskontroller.

Ta eksemplet nedenfor: Det er flere nivåer av gjennomsiktighet på sprite og, som du ser, blir den gradvis senket til høyre. Hvis vi setter gjennomsiktighetsnivået til 0,5, blir en piksel med en alfa på 0,5 ~ 1 betraktet som ugjennomsiktig og egnet for kollisjonsdeteksjon. De lavere enn 0,5 vil bli betraktet som gjennomsiktige. Selv når disse pikslene kolliderer med en annen gjenstand, registrerer de ikke en ekte kollisjon.

En annen detalj som jeg nevnte akkurat nå, er at ActionScript BitmapDatas hitTest funksjon alfa parameterverdien varierer faktisk fra 0 ~ 255. Så det jeg gjør er bare å multiplisere min terskelverdi med 255 for å konvertere rekkevidden.

 privat funksjonskontroll (e: Event): void var punkt1: Punkt = nytt punkt (bar1.x, bar1.y); var punkt2: Punkt = nytt punkt (bar2.x, bar2.y); var terskel: Nummer = 255 * 0,5 hvis (bdat1.hitTest (punkt1, terskel, bdat2, punkt2, terskel)) t1.text = "Minst ett piksel har kollidert" annet t1.text = "Ingen kollisjon" 

Trinn 5: Optimalisering

Jeg har nevnt at pikselnivå kollisjon gjenkjenning er computationally dyrt. Dette betyr at vi bare velger det når det er strengt nødvendig. Hvis to objekter er svært langt fra hverandre, er det ingen grunn til å bruke denne tilnærmingen, og en normal kollisjonsdetektering for avgrensningsboks (hitTestObject ()) Vil gjøre.

Her er ideen:

  1. Bruk hitTestObject () for å se om to objekters grensekasser har kollidert.
  2. Hvis svaret er ja, er disse to gjenstandene ganske nært. Fortsett med kontroll av pikselnivå.
  3. Hvis svaret er nei, er disse to objektene langt fra hverandre. Avslutt kollisjonskontroll uten kontroll av pikselnivå.
 privat funksjonskontroll (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) hvis (closeEnough) var punkt1: Punkt = nytt punkt (coconut.x, coconut.y); var punkt2: Punkt = nytt punkt (hk.x, hk.y); hvis (bdat1.hitTest (punkt1, 255, bdat2, punkt2, 255)) t1.text = "Minst ett piksel har kollidert" annet t1.text = "Ingen kollisjon"

For en full ActionScript-referanse, sjekk ut Matrix_Bitmap3.as fra kilden nedlasting.


Konklusjon

Takk for at du leser. I neste Quick Tip bruker vi matriser for å transformere BitmapData.