I den här handledningen följer jag tillvägagångssättet från Richard Davey (Tack, Richard!), Och används av honom och andra för att upptäcka kollisioner mellan bitmapar med en subtil modifikation. Jag jämför också prestanda mellan olika metoder för bitmap-kollisionsdetektering med hjälp av Grant Skinner PerformanceTest-sele.
Obs! Förutom att vara en del av Shoot-'Em-Up-sessionen är denna artikel också en del av kollisionsdetektering och -reaktion.
Jag beskriver här alternativt kortfattat här.
Först kontrollerar vi om de två bitmapparnas gränsvärdar överlappar varandra med rektangel. Skripten är som nedan. Först, variablerna.
privat var fiende1: Bitmapp, myShip: Bitmap; privat var myShipSp: Sprite; privat var rec_e: rektangel, rec_m: rektangel; privat var intersec: rektangel;
enemy1 = new E1 som Bitmap; addChild (enemy1); myShip = new My as Bitmap; myShipSp = ny Sprite; addChild (myShipSp); myShipSp.addChild (MinFrakt); enemy1.x = stage.stageWidth >> 1; myShipSp.x = stage.stageWidth >> 1; enemy1.y = stage.stageHeight * 0.2; myShipSp.y = stage.stageHeight * 0,8; // tecknar lådor runt sprite tecknet (fiend1.getBounds (scen), detta, 0); rita (myShipSp.getBounds (scen), detta, 0);
Här kontrollerar vi för eventuella överlappande områden mellan lådorna. Checka ut DetectVisible.as
i källans nedladdning för hela skriptet
Uppdatering av privata funktioner (e: Event): void // bestämning av gränslinje för skärningspunkten rec_e = enemy1.getBounds (scen); rec_m = myShipSp.getBounds (scen); intersec = rec_e.intersection (rec_m); // redraw gränsen box av båda sprites this.graphics.clear (); rita (fiende1.getBounds (scen), detta, 0); rita (myShipSp.getBounds (scen), detta, 0); // bara rita gränslinje för skärningspunkt om det finns en om (! intersec.isEmpty ()) lines.graphics.clear (); rita (intersec, linjer); t.text = "Intersection område med röd rektangel." else t.text = "Inget korsningsområde."
Här är en demo. Dra det mindre rymdskeppet runt.
(Oroa dig inte för den röda rutan som blir "efterlämnad" när skeppet släcks ut från den andra gränsen.)
Så om det finns ett skärande fältområde fortsätter vi för att kontrollera om det finns överlappande pixlar i området. Låt oss först försöka dra bitmap i det här skärningsområdet. Det fullständiga skriptet är in DetectVisible2.as
Uppdatering av privata funktioner (e: Event): void // bestämning av gränslinje för skärningspunkten rec_e = enemy1.getBounds (scen); rec_m = myShipSp.getBounds (scen); intersec = rec_e.intersection (rec_m); // redraw gränsen box av båda sprites this.graphics.clear (); rita (fiende1.getBounds (scen), detta, 0); rita (myShipSp.getBounds (scen), detta, 0); // bara rita gränslinje för skärningspunkt om det finns en om (! intersec.isEmpty ()) lines.graphics.clear (); rita (intersec, linjer); // för att rita skärningsområdet och kontrollera överlappning av färgat område var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = ny BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y bdt_intersec.draw (fiende1, eM); bdt_intersec.draw (myShip, myM); bm_intersec.bitmapData = bdt_intersec; bm_intersec.x = 10 bm_intersec.y = stage.stageHeight * 0,8 - bm_intersec.height; t.text = "Intersection område med röd rektangel. \ n" else t.text = "Inget korsningsområde."
Observera att eftersom vi ritar området med hjälp av en matris beaktas eventuell skalning, skevning och andra omvandlingar på båda bitmapparna. Här är en demo; kolla in rutan i nedre vänstra hörnet.
Så hur kontrollerar vi rätt pixel? Jo först, vi ger färgen på denna skärningslåda en nyans av svart (Röd = 0, Grön = 0, Blå = 0). Sedan kommer skuggan av det mindre rymdskeppet att målas i den här mörka rutan som grön, med blandningsläge för ADD. På samma sätt blir skuggan av det större stationära främmande rymdskeppet målat rött.
Så nu kommer det att finnas områden av rött och grönt för rymdskepp, och svart om det inte finns något överlappande område. Men om det finns pixlar från dessa två bitmappar som överlappar varandra, dras de i gult (Rött = 255, Grönt = 255, Blått = 0). Vi använder metoden Bitmapdata.getColorBoundsRect
att kontrollera om det här området finns.
Här är snippet i Main.as
// för att rita skärningsområdet och kontrollera överlappning av färgat område var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = ny BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y // tweak färg bdt_intersec.draw (enemy1, eM, new ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, new ColorTransform (1,1,1,1, -255,255, -255), BlendMode.ADD); bm_intersec.bitmapData = bdt_intersec; bm_intersec.x = 10 bm_intersec.y = stage.stageHeight * 0,8 - bm_intersec.height; t.text = "Intersection område med röd rektangel. \ n" // kolla efter förekomsten av rätt färg intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); om (! intersec_color.isEmpty ()) t.appendText ("Och det finns interesecting pixels i området.");
Observera att vi undertrycker de röda och blåa komponenterna i linje 113 för att maximera grön för det lilla rymdskeppet. På linje 112 gör vi detsamma med det främmande rymdskeppet med Blue och Green-komponenterna.
Så efter att ha fått kommentarer om prestandafrågor angående kollisionsdetektering bestämde jag mig för att göra några snabba och smutsiga tester på dessa sätt. Jag skapade 20 fientliga rymdskepp och ett spelarens rymdskepp och kontrollerad kollisionsdetektering mellan den ena spelaren fartyget mot de andra 20. Dessa sprites är packade i samma närhet för att tvinga kollisionsdetektering för alla tillvägagångssätt för att ha en komplett körning.
Det första tillvägagångssättet är det enklaste. BitmapData
fångas vid initiering och för varje ram kontrolleras kollisionsdetektering med användning av BitmapData.hitTest ()
. För det andra tillvägagångssättet, BitmapData
uppdateras varje ram och kollisionsdetektering görs baserat på dessa BitmapData
fångad. Den tredje hänvisar till det tillvägagångssätt som föreslås av denna handledning.
Så resultatet för en av de tester jag har gjort är som nedan.
- bitmapdata fixed (1000 iterations) Player version: WIN 11,1,102,55 (debug) - metod ... ttl ms ... avg ms bitmapdata fixed 168 0.17 - - bitmapdata uppdateringar (1000 iterationer) Spelarens version: WIN 11,1,102,55 (debug) - metod ... ttl ms ... avg ms bitmapdata uppdateringar 5003 5,00 - - anpassad metod (1000 iterationer) Spelarens version: WIN 11,1,102,55 (debug) - metod ... ttl ms ... avg ms anpassad metod 4408 4.41 -
De Utvärderingsprov
ger olika resultat när jag kör test. Så jag sprang det några gånger och härledde en genomsnittlig tid. Slutsats: Den snabbaste metoden är den första, följt av den tredje och sedan den andra metoden.
Så lagring bort BitmapData
för bitmaps när de först introduceras i scen och kontrollerar hitTest
varje ram efter är faktiskt effektiv, förutsatt att dessa sprites inte utför några andra omvandlingar än översättning (som rotation, skevning och skalning) över tiden. Annars kommer du att vara tvungen att anta antingen den andra eller den tredje tillvägagångssättet, och den tredje är effektivare enligt bilden ovan.
Du kan kolla in Collisions.as
och Results.as
för hela skriptet.
Jag började därefter att söka efter de specifika raderna av kod som tog upp mer beräknatid. Det andra och tredje tillvägagångssättet tog mer tid, så jag avled flera funktioner från dem, som bryter på olika punkter. Kolla in ett av resultaten nedan.
- default hitTest (1000 iterationer) Spelarens version: WIN 11,1,102,55 (debug) include bounds - method ... ttl ms ... avg ms default hitTest 189 0.19 - - default hitTest (1000 iterationer) Spelarens version: WIN 11,1,102,55 ( debug) include transform-method ... ttl ms ... avg ms default hitTest 357 0.36 - - default hitTest (1000 iterationer) Spelarens version: WIN 11,1,102,55 (debug) inkluderar hittest - metod ... ttl ms ... avg ms default hitTest 4427 4,43 - - anpassad metod (1000 iterationer) Spelarversion: WIN 11,1,102,55 (debug) inlcude bounds och transform-metod ... ttl ms ... avg ms anpassad metod 411 0.41 - - anpassad metod (1000 iterationer) Spelarens version: WIN 11, 1,102,55 (debug) inkluderar draw and bounds - metod ... ttl ms ... avg ms anpassad metod 3320 3.32 -
Den första, andra och tredje gången hänvisar till den andra metoden vid olika brytpunkter, och den fjärde och femte gången hänvisar till den tredje metoden. Titta på tredje och femte gången, BitmapData.draw
verkar ta mycket beräkningstid. Och tiden som tas för att dra med andra tillvägagångssättet verkar vara dyrare vid beräkningstiden, vilket får mig att tänka att storlekarna för BitmapData.draw
att driva på spelar roll. Du kan kolla in Collisions2.as
och Results2.as
för hela skript.
En sak som jag finner lite störande är inkonsekvensen av dessa test - jag får inte alltid samma resultat, även om de nästan alltid följer samma rankning hela tiden. Så det är tillräckligt bra för att göra en enkel jämförelse mellan funktionerna.
Tja, tack för din tid och läser det här lilla tipset. Hoppas det har varit användbart. Lämna kommentarer om du inte håller med något i denna handledning. Jag skulle gärna svara på feedback!