Pixel-nivå kollisionsdetektion för transformerad grafik

I den tidigare handledningen gick vi igenom grunderna för kollisionsdetektering på pixelnivå. I denna handledning ska vi utforska användningen av matriser för att bättre definiera området av intresse - mycket användbart för grafik som har roterats, översatts eller snedställts.


Kollisionsdetektering

Detta är det sista stycket vi ska försöka progam. Klicka på bollen och kroken för att starta den interaktiva demo.

Lägg märke till hur, trots att den grafiska roterande kokosnötstrukturen och omvandlas på annat sätt, har vi fortfarande pixel-perfekt kollisionsdetektering.


Steg 1: Den dra Fungera

Utförande av kollisionsdetektering på pixelnivå kräver att grafiken kastas till en BitmapData objekt. Vi har tittat på detta i föregående handledning genom att använda analogi av en röntgen. I denna handledning ska jag förklara denna "röntgen" -process.

Antag att vi har en bit av en grafik (diagram 1) och ville kasta den till en BitmapData objekt; vi måste definiera dimensionerna av BitmapData objekt först (diagram 2). I det här fallet är det ganska enkelt eftersom grafiken är bredd och höjd egenskaper ger detta. Då kallar vi dra() metod; Pixlar som är minst hälften upptagna av grafiken fylls upp, teoretiskt (diagram 3). Denna uppsättning pixlar kommer att jämföras med ännu en uppsättning pixlar från en annan grafik för att kontrollera om de båda är kollisionerade (diagram 4).


Steg 2: Olika samordningsområden

Det finns olika koordinatutrymmen som används i Flash IDE (diagram 1 och 2 ovan). Jag är säker på att varje läsare skulle ha upplevt detta - se min handledning om affine-utrymmen för ett mer detaljerat utseende.

I Flash IDE ritar vi en bild och gör den till en symbol för typen Filmklipp. När vi dubbelklickar på Filmklipp, vi anländer till ett annat koordinatutrymme (diagram 3). Härifrån kan vi klicka på sceneniketten för att lämna grafikens lokala koordinatutrymme och komma fram till scenens koordinatutrymme. Då kan vi börja omforma förekomsten av Filmklipp på scenen (diagram 4). Faktum är att vi kan skapa flera instanser av Filmklipp, var och en av dem har olika omvandlingar.

I biblioteket förblir det ursprungliga grafiket oförändrat trots att alla tweaks görs på dess kopior på scenen. Detta är rationellt eftersom när vi gör en ny kopia av en Filmklipp På scenen är de alltid desamma som den ursprungliga kopian i biblioteket. Nu är frågan "Hur tar Flash upp hela omvandlingen vi gjort till kopiorna på scenen?" Tja, de använder vardera MovieClip.transform.matrix egendom för att fånga alla dina omvandlingar (översättning, rotation, skew, etc).

Låt oss nu gå tillbaka till där vi slutade. Det är viktigt att vi förstår detta eftersom dra() metod av BitmapData hänvisar inte till den grafiska instansen på scenen när man utför en "röntgen" utan snarare den oförändrade grafiska källan i biblioteket.

De BitmapData Objektets första pixel justeras med registreringspunkten (den röda punkten) på Filmklipp på det lokala koordinatutrymmet (se diagram 3 i steg 1), tar du bilden i bildform med de mått vi anger.

När det kommer till hitTest kontrollerar, ActionScript justerar den här första pixeln (den övre vänstra pixeln) av BitmapData objekt med registreringspunkten för den grafiska instansen på scenen. Med detta, alla pixlar i BitmapData objektet kartläggs på koordinatutrymmet på scenen och erhåller sina individuella koordinater. Kontroller kan senare utföras genom att jämföra dessa koordinater mellan två bitmappar för att se om några pixlar överlappar varandra.

Notera: Denna förklaring antar Filmklipp eller Sprite Exempel läggs till i scenens visningslista. I ActionScript kan vi faktiskt lägga till visningsobjekt i själva dokumentkatalogen eftersom det sträcker sig Filmklipp eller Sprite.


Steg 3: Problemet

Så om grafiken av intresse roteras på scenen, hur utför vi det dra()?

Från ovanstående diagram kan vi tydligt se dessa problem.

Diagram Problem Beskrivning
1 Dimension av BitmapData objekt Eftersom objektorienteringen har ändrats, krävs den nödvändiga dimensionen av BitmapData Gjutet kan inte längre vara bekvämt taget från bredd och höjd egenskaper hos den grafiska förekomsten.
2 Orientering av grafisk källa Förekomsten av grafiken på scenen roteras, men den i biblioteket är inte. Vi behöver ta en ögonblicksbild av den transformerade grafiska källan från biblioteket.
3 Koordinera högst vänster pixel (startpunktpunkt) för BitmapData objekt Vi kan inte justera BitmapData objektets första pixel med registreringspunkten för grafisk instans. Detta kommer att vara felaktigt.

Steg 4: Lösningen

För att lösa dessa problem ska vi först definiera en rektangel som strikt begränsar den roterade grafiska instansen på scenen. ActionScript-bestämmelser för detta via getBounds () fungera. Detta kommer att lösa vårt första problem. Observera bilden nedan. Observera att det finns en skillnad mellan den grafiska instansens registreringspunkt och rektangeln.

Jag har inkluderat Flash-presentationen nedan för att visa den rektangulära avgränsningsrutan (röda rutan) och den lokala rutan för avgränsning (svart box)


Steg 5: Transformation

Därefter ska vi ta en ögonblicksbild av den transforrerade grafiska källan i denna rektangel på scenen. Observera bilden nedan.

Vi börjar med att ha registreringspunkten för grafikkällan i linje med det rektangulära området (diagram 1). Sedan roterar vi (diagram 2) och kompenserar det (diagram 3) innan vi tar "röntgenbilden" av bilden på bilden BitmapData objekt (diagram 4).

Vi kan göra det manuellt, eller välja att kopiera den grafiska instansen transform.matrix fast egendom. När vi använder det andra tillvägagångssättet, bör vi vara försiktiga att inte använda transform.matrix översättning egendom - annars kommer inte registreringspunkterna att anpassas som du ser i diagram 1. I båda fallen måste vi beräkna x- och y-avståndet till offset.


Steg 6: Implementering av ActionScript

Efter denna långa förklaring hoppas jag att det är lättare att förstå koden. Jag har markerat de viktiga raderna och tillagda kommentarer:

 privat var kokosnöt: CTree, hk: Hook; privat var bdat1: BitmapData, bdat2: BitmapData; privat var t1: TextField; privat varvinkel: Number = 45 privat var coconutBox: Rektangel; allmän funktion Matrix_Bitmap4 () kokosnöt = ny CTree (); addChild (kokosnöt); kokosnöt.rotation = vinkel; coconut.x = stage.stageWidth * 0,3; coconut.y = stage.stageHeight * 0.2; coconutBox = coconut.getBounds (this); // få rektangulär lådan i steg 2 var coconut_newX: Number = coconut.x - coconutBox.x // få offset x i steg 3 var coconut_newY: Number = coconut.y - coconutBox.y // förskjutna y i steg 3 var m : Matrix = ny matris (); m.rotat (vinkel / 180 * Math.PI); // rotera grafik i steg 3 // var m: Matrix = coconut.transform.matrix // rekommenderas om många förändringar skedde. //m.tx = 0; m.ty = 0; // För det här fallet gör det samma jobb som tidigare linje. m.translate (coconut_newX, coconut_newY); // implementera offset bdat1 = ny BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); bdat1.draw (kokosnöt, m);

Också för att inte glömma att ändra platsen för den första pixeln (högst upp till vänster) i BitmapData motsätta sig den rektangulära rutan

 privat funktionskontroll (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) om (closeEnough) // var punkt1: Punkt = ny punkt (coconut.x, coconut.y); // nu när vi har en annan ruta med olika plats för att starta pixel, // vi borde hänvisa till coconutBox som utgångspunkt var punkt1: Punkt = Ny punkt (coconutBox.x, coconutBox.y); var punkt2: Punkt = ny punkt (hk.x, hk.y); om (bdat1.hitTest (punkt1, 1, bdat2, punkt2, 1)) t1.text = "Minst en pixel har kolliderat" annars t1.text = "Ingen kollision"

Och här är ett exempel på arbetet.


Steg 7: Konstant uppdatering

Om formen av intresse, i detta fall kokosnötet, omvandlas ständigt (roterande, skalning, krympning, skevning, etc.) då BitmapData objektet måste uppdateras på varje bildruta och detta kommer att ta lite bearbetning. Observera också att jag har valt alternativet som nämns i steg 4. Här är skriptet för att uppdatera röntgenkopian av grafiken för varje ram:

 privatfunktionsuppdateringBmp (): void coconutBox = coconut.getBounds (this); // få rektangulär låda i steg 2 var coconut_newX: Number = coconut.x - coconutBox.x // få offset x i steg 3 var coconut_newY: Number = coconut.y - coconutBox.y // förskjutna y i steg 3 // var m: Matrix = ny matris (); //m.rotate(angle / 180 * Math.PI); // rotera grafik i steg 3 var m: Matrix = coconut.transform.matrix // rekommenderas om många transformationer hände, m.tx = 0; m.ty = 0; // för det här fallet, det gör samma jobb som tidigare linje. m.translate (coconut_newX, coconut_newY); // implementera offset bdat1 = ny BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); b = ny bitmapp (bdat1); addChild (b); b.x = stadium.stageWidth * 0,3; b.y = stadium.stageHight * 0,2; bdat1.draw (kokosnöt, m); 

Följande funktion utförs varje ram:

 privatfunktionskontroll (e: händelse): void coconut.rotation + = angle; // dynamiska förändringar vid runtime coconut.scaleX + = 0.01 var closeEnough: Boolean = coconut.hitTestObject (hk) om (closeEnough) updateBmp (); var punkt1: Punkt = ny punkt (coconutBox.x, coconutBox.y); var punkt2: Punkt = ny punkt (hk.x, hk.y); om (bdat1.hitTest (punkt1, 1, bdat2, punkt2, 1)) t1.text = "Minst en pixel har kolliderat" annars t1.text = "Ingen kollision" bdat1.dispose (); 

Och det här är utgången. Börja dra kroken för att se animationen.


Slutsats

Jag förstår att denna handledning kanske inte är för snabb att läsa, men det är viktigt att få en klar förståelse av vad som händer. Hoppas detta har varit till hjälp och om du är intresserad av att manipulera denna 2x2-matris mer, besök min artikel om ämnet. Tack.