Pixelniveau-botsdetectie voor getransformeerde grafische afbeeldingen

In de vorige zelfstudie hebben we de basisprincipes van botsingdetectie op pixelniveau doorgenomen. In deze zelfstudie zullen we het gebruik van matrices onderzoeken om het interessegebied beter te definiëren - erg handig voor afbeeldingen die zijn geroteerd, vertaald of scheefgetrokken.


Collision Detection

Dit is het laatste stuk dat we zullen proberen te programmeren. Klik op de bal en haak om de interactieve demo te starten.

Merk op dat, ondanks dat de kokosnoot grafisch roteert en anderszins wordt getransformeerd, we nog altijd pixel-perfecte botsingsdetectie hebben.


Stap 1: De trek Functie

Voor het uitvoeren van botsingsdetectie op pixelniveau moet de afbeelding naar a worden gegoten BitmapData voorwerp. We hebben dit in de vorige tutorial bekeken door de analogie van een röntgenopname te gebruiken. In deze tutorial zal ik dit röntgenonderzoek uitleggen.

Stel dat we een stuk van een afbeelding hebben (diagram 1) en dit naar a wilden casten BitmapData voorwerp; we moeten de dimensies van de definiëren BitmapData object eerst (diagram 2). In dit geval is het vrij eenvoudig, omdat de afbeeldingen breedte en hoogte eigenschappen bieden dit. Dan noemen we de trek() methode; pixels die voor de helft door de grafiek worden ingenomen, worden in theorie opgevuld (diagram 3). Deze reeks pixels wordt vergeleken met een andere reeks pixels van een andere afbeelding om te controleren op een botsing tussen de twee (diagram 4).


Stap 2: Verschillende coördinaatruimten

Er zijn verschillende coördinatenruimten gebruikt in Flash IDE (diagrammen 1 en 2 hierboven). Ik ben er zeker van dat elke lezer dit zou hebben meegemaakt - zie mijn tutorial over affiene ruimtes voor een gedetailleerdere blik.

In de Flash IDE tekenen we een afbeelding en veranderen deze in een symbool van het type Filmclip. Wanneer we dubbelklikken op de Filmclip, we komen op een andere coördinatenruimte (diagram 3). Vanaf hier kunnen we op het label op het podium klikken om de lokale coördinatenruimte van deze afbeelding te verlaten en in de coördinaatruimte van de stage aan te komen. Dan kunnen we beginnen met het transformeren van de instantie van Filmclip op het podium (schema 4). In feite kunnen we meerdere instanties van de maken Filmclip, elk van hen heeft verschillende transformaties.

In de bibliotheek blijft de oorspronkelijke afbeelding onveranderd ondanks alle aanpassingen aan de kopieën op het werkgebied. Dit is rationeel omdat telkens wanneer we een nieuwe kopie maken van een Filmclip op het podium zijn ze altijd hetzelfde als het originele exemplaar in de bibliotheek. Nu is de vraag: "Hoe legt Flash alle transformatie vast die we hebben gedaan met de kopieën op het podium?" Nou, ze gebruiken elk de MovieClip.transform.matrix eigenschap om al uw transformaties vast te leggen (vertaling, rotatie, skew, enz.).

Laten we nu teruggaan naar waar we gebleven waren. Het is cruciaal dat we dit begrijpen omdat het trek() methode van BitmapData verwijst niet naar de grafische instantie op het podium bij het uitvoeren van een "röntgenfoto", maar naar de ongewijzigde grafische bron in de bibliotheek.

De BitmapData de eerste pixel van het object komt overeen met het registratiepunt (de rode stip) van de Filmclip op de lokale coördinatenruimte (verwijs naar diagram 3 in stap 1) en leg vervolgens de afbeelding vast in pixelvorm met de afmetingen die we specificeren.

Als het aankomt op hitTest controleert, uitlijnt ActionScript deze eerste pixel (de pixel linksboven) van de BitmapData object met het registratiepunt van de grafische instantie op het werkgebied. Hiermee zijn alle pixels in de BitmapData object wordt toegewezen aan de coördinaatruimte op het werkgebied en verkrijgt zijn individuele coördinaten. Controles kunnen later worden uitgevoerd door deze coördinaten tussen twee bitmaps te vergelijken om te zien of er pixels overlappen.

Notitie: Deze uitleg gaat uit van de Filmclip of sprite exemplaar wordt toegevoegd aan de displaylijst van de stage. In ActionScript kunnen we weergaveobjecten toevoegen aan de documentklasse zelf, omdat deze zich uitbreidt Filmclip of sprite.


Stap 3: Het probleem

Dus als de afbeelding van interesse op het toneel wordt geroteerd, hoe moeten we presteren trek()?

Uit het bovenstaande diagram kunnen we duidelijk deze problemen zien.

Diagram Probleem Omschrijving
1 Dimensie van BitmapData voorwerp Aangezien de objectoriëntatie is gewijzigd, is de vereiste dimensie van BitmapData cast kan niet langer gemakkelijk uit de breedte en hoogte eigenschappen van de grafische instantie.
2 Oriëntatie van de grafische bron Het exemplaar van de afbeelding op het werkgebied wordt geroteerd, maar dat in de bibliotheek is dat niet. We moeten een momentopname maken van de getransformeerde grafische bron uit de bibliotheek.
3 Coördinaat van pixel linksboven (beginpixel) van BitmapData voorwerp We kunnen het niet uitlijnen BitmapData de eerste pixel van het object met het registratiepunt van de grafische instantie. Dit is onjuist.

Stap 4: De oplossing

Om deze problemen op te lossen, zullen we eerst een rechthoek definiëren die de gedraaide grafische instantie op het werkvlak stevig omsluit. ActionScript-bepalingen hiervoor via de getBounds () functie. Dit zal ons eerste probleem oplossen. Bekijk de afbeelding hieronder. Merk op dat er een verschil is tussen het registratiepunt van de grafische instantie en dat van de rechthoek.

Ik heb de onderstaande Flash-presentatie toegevoegd om het rechthoekige selectiekader (rode kader) en het lokale kader voor de omgevingsbegrenzing (zwarte kader) weer te geven


Stap 5: Transformatie

Vervolgens brengen we een momentopname van de getransformeerde grafische bron in deze rechthoek op het podium. Bekijk de afbeelding hieronder.

We beginnen met het hebben van het registratiepunt van de grafische bron in lijn met dat van het rechthoekige gebied (diagram 1). Vervolgens roteren we (diagram 2) en verplaatsen we het (diagram 3) voordat we de "x-ray" -opname van de afbeelding op de BitmapData object (diagram 4).

We kunnen dit handmatig doen, of ervoor kiezen om een ​​kopie van de grafische instanties te maken transform.matrix eigendom. Bij gebruik van de tweede benadering, moeten we oppassen dat we de transform.matrix vertaling eigenschap - anders zullen de registratiepunten niet uitgelijnd zijn zoals u ziet in diagram 1. In beide gevallen moeten we de x- en y-afstand berekenen om te compenseren.


Stap 6: ActionScript-implementatie

Na deze lange uitleg hoop ik dat het gemakkelijker is om de code te begrijpen. Ik heb de belangrijke lijnen gemarkeerd en opmerkingen toegevoegd:

 private var coconut: CTree, hk: Hook; private var bdat1: BitmapData, bdat2: BitmapData; private var t1: TextField; private var angle: Number = 45 private var coconutBox: Rectangle; openbare functie Matrix_Bitmap4 () coconut = nieuwe CTree (); addChild (kokos); coconut.rotation = hoek; coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; coconutBox = coconut.getBounds (dit); // verkrijg een rechthoekige doos in stap 2 var coconut_newX: Number = coconut.x - coconutBox.x // verkrijg offset x in stap 3 var coconut_newY: Number = coconut.y - coconutBox.y // haal offset y in stap 3 var m : Matrix = nieuwe matrix (); m.rotate (hoek / 180 * Math.PI); // roteer afbeelding in stap 3 // var m: Matrix = coconut.transform.matrix // aanbevolen als er veel transformatie heeft plaatsgevonden. //m.tx = 0; m.ty = 0; // In dit geval doet het hetzelfde werk als de vorige regel. m.translate (coconut_newX, coconut_newY); // implementeer de offset bdat1 = nieuwe BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); bdat1.draw (kokosnoot, m);

Vergeet ook niet om de locatie van de eerste pixel (linksboven) in te wijzigen BitmapData voorwerp van dat van de rechthoekige doos

 functiecontrole (e: Event): void var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) // var point1: Point = new Point (coconut.x, coconut.y); // nu we een andere box hebben met een andere locatie voor het starten van pixel, // moeten we verwijzen naar coconutBox als uitgangspunt var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Ten minste één pixel is gebotst" else t1.text = "Geen botsing"

En hier is een voorbeeld van het werk.


Stap 7: Constante update

Als de vorm van interesse, in dit geval de kokosnootboom, constant transformeert (roterend, schalerend, krimpend, scheef, enz.) Dan is de BitmapData object moet op elk frame worden bijgewerkt en dit zal enige verwerking vergen. Merk ook op dat ik heb gekozen voor de alternatieve benadering die wordt genoemd in stap 4. Hier is het script om de röntgenkopie van de afbeelding voor elk frame bij te werken:

 persoonlijke functie updateBmp (): void coconutBox = coconut.getBounds (this); // verkrijg rechthoekige box in stap 2 var coconut_newX: Number = coconut.x - coconutBox.x // verkrijg offset x in stap 3 var coconut_newY: Number = coconut.y - coconutBox.y // zet offset y in stap 3 // var m: Matrix = nieuwe matrix (); //m.rotate(angle / 180 * Math.PI); // roteer afbeelding in stap 3 var m: Matrix = coconut.transform.matrix // aanbevolen als veel transformatie heeft plaatsgevonden, m.tx = 0; m.ty = 0; // in dit geval doet het hetzelfde werk als de vorige regel. m.translate (coconut_newX, coconut_newY); // implementeer de offset bdat1 = nieuwe BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); b = nieuwe bitmap (bdat1); addChild (b); b.x = stage.stageWidth * 0.3; b.y = stage.stageHeight * 0.2; bdat1.draw (kokosnoot, m); 

De volgende functie wordt elk frame uitgevoerd:

 persoonlijke functiecontrole (e: Event): void coconut.rotation + = angle; // dynamische wijzigingen tijdens runtime coconut.scaleX + = 0.01 var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) updateBmp (); var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Ten minste één pixel is gebotst" else t1.text = "Geen botsing" bdat1.dispose (); 

En dit is de uitvoer. Begin met het slepen van de haak om de animatie te zien.


Conclusie

Ik begrijp dat deze tutorial misschien niet te snel is om te lezen, maar het is van cruciaal belang om een ​​duidelijk beeld te krijgen van wat er gebeurt. Ik hoop dat dit nuttig was en als je geïnteresseerd bent in het manipuleren van deze 2x2 matrix meer, bezoek dan mijn artikel over het onderwerp. Bedankt.