În acest tutorial, voi urma abordarea sugerată de Richard Davey (Mulțumesc, Richard!) Și folosită de el și de alții pentru a detecta coliziuni între bitmap-uri cu o modificare subtilă. De asemenea, voi compara performanța dintre diferitele abordări ale detecției coliziunii bitmap folosind hamul PerformanceTest al Grant Skinner.
Notă: Pe lângă faptul că face parte din sesiunea Shoot-Em-Up, acest articol face parte și din Detectarea și reacția de coliziune.
Descriu această abordare alternativă pe scurt aici.
Mai întâi, verificăm dacă casetele de delimitare a celor două bitmap-uri se suprapun cu ajutorul dreptunghiului. Scripturile sunt cele de mai jos. În primul rând, variabilele.
private var enemy1: Bitmap, myShip: Bitmap; privat var myShipSp: Sprite; privat var rec_e: dreptunghi, rec_m: dreptunghi; privat var intersec: dreptunghi;
enemy1 = noul E1 ca bitmap; addChild (enemy1); myShip = nou My as Bitmap; myShipSp = Sprite nou; addChild (myShipSp); myShipSp.addChild (myShip); enemy1.x = stage.stageWidth >> 1; myShipSp.x = stage.stageWidth >> 1; enemy1.y = stage.stageHeight * 0.2; myShipSp.y = stage.stageHeight * 0.8; // desene cu desene în jurul remizei sprite (enemy1.getBounds (stage), aceasta, 0); trageți (myShipSp.getBounds (stadiu), aceasta, 0);
Aici verificăm orice zonă care se suprapune între cutii. Verifică DetectVisible.as
în descărcarea sursei pentru scenariul complet
actualizarea funcției private (e: Event): void // determinarea casetei de intersecție a intersecției rec_e = enemy1.getBounds (stadiu); rec_m = myShipSp.getBounds (stadiu); intersec = rec_e.intersecție (rec_m); // redraw caseta de legare a ambelor sprites this.graphics.clear (); trage (enemy1.getBounds (stadiu), aceasta, 0); trageți (myShipSp.getBounds (stadiu), aceasta, 0); / / desenați numai o margine de intersecție a zonei de intersecție dacă există o if (! intersec.isEmpty ()) lines.graphics.clear (); desen (intersec, linii); t.text = "Zona de intersecție cu dreptunghi roșu". altfel t.text = "Nicio zonă de intersecție."
Iată un demo. Trageți nava spațială mai mică.
(Nu vă faceți griji cu privire la caseta roșie care este "lăsată în urmă" atunci când nava este scos din cutia de legare a celuilalt.)
Deci, dacă există o zonă de intersecție a casetei, vom continua să verificăm dacă în zonă există pixeli suprapuși. Cu toate acestea, să încercăm mai întâi să desenați bitmap în această zonă de intersecție. Scenariul complet este în DetectVisible2.as
actualizarea funcției private (e: Event): void // determinarea casetei de intersecție a intersecției rec_e = enemy1.getBounds (stadiu); rec_m = myShipSp.getBounds (stadiu); intersec = rec_e.intersecție (rec_m); // redraw caseta de legare a ambelor sprites this.graphics.clear (); trage (enemy1.getBounds (stadiu), aceasta, 0); trageți (myShipSp.getBounds (stadiu), aceasta, 0); / / desenați numai o margine de intersecție a zonei de intersecție dacă există o if (! intersec.isEmpty ()) lines.graphics.clear (); desen (intersec, linii); // a desena zona de intersectie si verificarea suprapunerii zonei colorate var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nou 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 (inamic1, 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 = "Zona de intersecție cu dreptunghiul roșu. \ n" altceva t.text = "Nicio zonă de intersecție."
Rețineți că, din moment ce desenăm zona folosind o matrice, sunt luate în considerare toate modificările de scalare, înclinare și alte transformări pe ambele scheme. Iată un demo; verificați caseta din colțul din stânga jos.
Deci, cum să verificăm pixelul potrivit? În primul rând, dăm culoarea acestei cutii de intersecție o nuanță de negru (roșu = 0, verde = 0, albastru = 0). Apoi, umbra navetei mai mici va fi pictată în această cutie întunecată ca verde, cu modul de amestecare a ADD. În mod similar, umbra navei spatiale extraterestre mai mari va fi vopsita in rosu.
Deci, acum vor exista zone de roșu și verde pentru navele spațiale și negru dacă nu există o zonă suprapusă. Cu toate acestea, dacă există pixeli din aceste două diagrame bitmaps care se suprapun, acestea vor fi desenate în galben (roșu = 255, verde = 255, albastru = 0). Folosim metoda Bitmapdata.getColorBoundsRect
pentru a verifica existența acestei zone.
Iată fragmentul în Main.as
// a desena zona de intersectie si verificarea suprapunerii zonei colorate var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nou BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y // tweak culoare bdt_intersec.draw (inamic1, eM, noul ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, noul 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 = "Zona de intersecție cu dreptunghi roșu. \ n" // verificați existența culorii potrivite intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); if (! intersec_color.isEmpty ()) t.appendText ("Și există pixeli interesezi în zonă.");
Rețineți că suprimăm componentele Roșu și Albastru în linia 113 pentru a maximiza valoarea Green pentru micul nava spațială. Pe linia 112 facem același lucru cu nava extraterestră cu componentele Albastru și Verde.
Prin urmare, după ce am primit comentarii cu privire la problemele de performanță privind detectarea coliziunilor, am decis să fac niște teste rapide și murdare cu privire la aceste abordări. Am creat 20 de nave spațiale inamice și o navă spațială jucător și am verificat coliziunea de detectare între acea navă de jucător împotriva celorlalte 20. Aceste sprites sunt împachetate în aceeași apropiere pentru a forța detectarea coliziunii pentru toate abordările pentru a avea o rulare completă.
Prima abordare este cea mai simplă. BitmapData
este capturat la inițiere și pentru fiecare cadru, detecția coliziunii este verificată utilizând BitmapData.hitTest ()
. Pentru a doua abordare, BitmapData
este actualizat fiecare cadru și detectarea coliziunii se face pe baza acestora BitmapData
capturat. Al treilea se referă la abordarea sugerată de acest tutorial.
Rezultatul pentru unul dintre testele pe care le-am făcut este ca în cele de mai jos.
- bitmapdata fix (1000 iterații) Versiune jucător: WIN 11,1,102,55 (debug) - metoda ... ttl ms ... avg ms bitmapdata fix 168 0,17 - - actualizări bitmapdata (1000 iterații) - metodă ... ttl ms ... avg ms bitmapdata actualizări 5003 5.00 - - metoda personalizată (1000 iterații) Versiunea jucătorului: WIN 11,1,102,55 (depanare) - metoda ... ttl ms ... avg ms metoda personalizată 4408 4,41 -
Test de performanță
oferă rezultate diferite ori de câte ori execut test. Așa că l-am fugit de câteva ori și am obținut un timp mediu. Concluzie: cea mai rapidă metodă este prima, urmată de cea de-a treia și apoi cea de-a doua.
Deci, depozitarea departe BitmapData
pentru bitmap-uri atunci când sunt introduse pentru prima dată în scenă și verificate hitTest
fiecare cadru după este efectiv eficient, cu condiția ca aceste sprite să nu efectueze alte transformări decât traducerea (cum ar fi rotirea, înclinarea și scalarea) în timp. În caz contrar, veți fi nevoiți să adoptați cea de-a doua sau cea de-a treia abordare, iar a treia este mai eficientă, așa cum arată imaginea de mai sus.
Poți verifica Collisions.as
și Results.as
pentru scenariul complet.
M-am angajat după aceea să caut linii specifice de cod care au luat mai mult timp de calcul. A doua și a treia abordare au avut mai mult timp, așa că am obținut mai multe funcții de la ei, fiecare rupând în diferite puncte. Consultați unul dintre rezultatele de mai jos.
- implicit hitTest (1000 iterații) Versiunea jucătorului: WIN 11,1,102,55 (debug) include limite - metoda ... ttl ms ... avg ms implicit hitTest 189 0.19 - - implicit hitTest (1000 iterații) Versiune jucător: WIN 11,1,102,55 debug) include transform - metoda ... ttl ms ... avg ms implicit hitTest 357 0.36 - - implicit hitTest (1000 iterații) Versiune jucător: WIN 11,1,102,55 (debug) include hittest - metoda ... ttl ms ... avg ms implicit hitTest 4427 4.43 - metoda personalizată (1000 iterații) Versiunea jucătorului: WIN 11,1,102,55 (debug) limitele inlcude și metoda de transformare - metoda ... ttl ms ... avg ms metoda personalizată 411 0,41 - - metoda personalizată (1000 iterații) Versiunea jucătorului: 1,102,55 (depanare) include remiză și limite - metoda ... ttl ms ... avg ms metoda personalizată 3320 3,32 -
Prima, a doua și a treia oară se referă la cea de-a doua abordare la puncte de întrerupere diferite, iar a patra și a cincea se referă la a treia abordare. Privind la a treia și a cincea oară rezultatele, BitmapData.draw
pare să ia o mulțime de timp de calcul. Iar timpul necesar pentru a desena cu cea de-a doua abordare pare să fie mai costisitor în timpul de calcul, ceea ce mă face să cred că dimensiunile pentru BitmapData.draw
pentru a opera pe nu contează. Poți verifica Collisions2.as
și Results2.as
pentru scripturile complete.
Un lucru pe care mi se pare puțin deranjant este inconsecvența acestor teste - întotdeauna nu obțin rezultate în același timp, deși aproape că aceștia urmează aproape același rang. Deci, destul de bun pentru a face o simplă comparație între funcții.
Mulțumesc pentru timpul tău căutând acest mic sfat. Sper că a fost util. Nu lăsați comentarii dacă nu sunteți de acord cu nimic din acest tutorial. Mi-ar plăcea să răspund la feedback!