In deze tutorial volg ik de benadering van Richard Davey (bedankt, Richard!) En gebruik hem en anderen bij het detecteren van botsingen tussen bitmaps met een subtiele aanpassing. Ik zal ook de prestaties vergelijken tussen verschillende benaderingen van bitmapbotsingsdetectie met Grant Skinner's PerformanceTest harnas.
Opmerking: dit artikel maakt niet alleen deel uit van de sessie Shoot-'Em-Up, maar maakt ook deel uit van Collision Detection and Reaction.
Ik beschrijf deze alternatieve benadering in het kort hier.
Eerst controleren we of de begrenzingsvakken van de twee bitmaps elkaar overlappen met behulp van Rectangle. De scripts zijn zoals hieronder. Eerst de variabelen.
private var enemy1: Bitmap, myShip: Bitmap; privé var myShipSp: Sprite; private var rec_e: Rectangle, rec_m: Rectangle; private var intersec: Rectangle;
enemy1 = nieuwe E1 als bitmap; addChild (enemy1); myShip = nieuwe My als Bitmap; myShipSp = nieuwe Sprite; addChild (myShipSp); myShipSp.addChild (MijnVerzend); enemy1.x = stage.stageWidth >> 1; myShipSp.x = stage.stageWidth >> 1; enemy1.y = stage.stageHeight * 0.2; myShipSp.y = stage.stageHeight * 0.8; // tekenvakken rond de sprite-trekking (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0);
Hier controleren we op overlappende gebieden tussen de vakken. Uitchecken DetectVisible.as
in de brondownload voor het volledige script
verversing van persoonlijke functies (e: Event): void // bepaling van het selectiekader van het snijpunt rec_e = enemy1.getBounds (stage); rec_m = myShipSp.getBounds (stage); intersec = rec_e.intersection (rec_m); // herschrijf het selectiekader van beide sprites this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // teken alleen het selectiekader van het kruisingsgebied als er een if is (! intersec.isEmpty ()) lines.graphics.clear (); tekenen (intersec, lijnen); t.text = "Snijgebied met rode rechthoek." else t.text = "Geen kruisingsgebied."
Hier is een demo. Sleep het kleinere ruimteschip rond.
(Maak je geen zorgen over het rode vak dat "achterblijft" wanneer het schip uit het selectiekader van de ander wordt gesleept.)
Dus als er een kruisend vakgebied is, gaan we verder met controleren of er overlappende pixels in het gebied zijn. Laten we echter eerst proberen om bitmap in dit kruisingsgebied te tekenen. Het volledige script is binnen DetectVisible2.as
verversing van persoonlijke functies (e: Event): void // bepaling van het selectiekader van het snijpunt rec_e = enemy1.getBounds (stage); rec_m = myShipSp.getBounds (stage); intersec = rec_e.intersection (rec_m); // herschrijf het selectiekader van beide sprites this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // teken alleen het selectiekader van het kruisingsgebied als er een if is (! intersec.isEmpty ()) lines.graphics.clear (); tekenen (intersec, lijnen); // om het snijpuntgebied te tekenen en te controleren op overlapping van gekleurd gebied var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nieuwe 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 (enemy1, 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 = "Snijgebied op rode rechthoek. \ n" else t.text = "Geen kruisingsgebied."
Merk op dat, aangezien we het gebied tekenen met behulp van een matrix, rekening wordt gehouden met schaling, schuintrekken en andere transformaties op beide bitmaps. Hier is een demo; bekijk het vakje in de linkerbenedenhoek.
Dus hoe controleren we de juiste pixel? In de eerste plaats geven we de kleur van dit kruispuntvak een schaduw van zwart (rood = 0, groen = 0, blauw = 0). Vervolgens wordt de schaduw van het kleinere ruimteschip in deze donkere doos geverfd als groen, met de mengmodus van ADD. Evenzo zal de schaduw van het grotere stationaire buitenaardse ruimteschip rood worden geverfd.
Dus nu zijn er gebieden met rood en groen voor de ruimteschepen en zwart als er geen overlappend gebied is. Als er echter pixels van deze twee bitmaps zijn die elkaar overlappen, worden deze in geel weergegeven (rood = 255, groen = 255, blauw = 0). We gebruiken de methode Bitmapdata.getColorBoundsRect
om te controleren op het bestaan van dit gebied.
Hier is het fragment in Main.as
// om het snijpuntgebied te tekenen en te controleren op overlapping van gekleurd gebied var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nieuwe BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y // tweak color bdt_intersec.draw (enemy1, eM, new ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, nieuwe 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 = "Snijgebied op rode rechthoek. \ n" // controleer of de juiste kleur aanwezig is intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); if (! intersec_color.isEmpty ()) t.appendText ("En er zijn onderling kruisende pixels in het gebied.");
Merk op dat we de rode en blauwe componenten in regel 113 onderdrukken om Groen voor het kleine ruimteschip maximaal te maken. Op regel 112 doen we hetzelfde met het buitenaards ruimteschip met de blauwe en groene componenten.
Dus na het ontvangen van opmerkingen over prestatieproblemen met betrekking tot botsingsdetectie, besloot ik enkele snelle en vuile tests uit te voeren voor deze benaderingen. Ik heb 20 vijandelijke ruimteschepen en een ruimteschip van een speler gemaakt en botsingdetectie tussen dat ene spelersschip vergeleken met de andere 20. Deze sprites zijn in dezelfde omgeving gepakt om botsingdetectie voor alle benaderingen te forceren om een volledige run te hebben.
De eerste benadering is de eenvoudigste. BitmapData
wordt vastgelegd bij het starten en voor elk frame wordt de botsingsdetectie gecontroleerd met behulp van BitmapData.hitTest ()
. Voor de tweede benadering, BitmapData
wordt bijgewerkt elk frame en botsing detectie wordt gedaan op basis van die BitmapData
gevangen genomen. De derde verwijst naar de benadering die in deze zelfstudie wordt voorgesteld.
Dus het resultaat voor een van de tests die ik heb gedaan, is zoals hieronder.
- bitmapdata fixed (1000 iteraties) Player-versie: WIN 11,1.102,55 (debug) - methode ... ttl ms ... avg ms bitmapdata fixed 168 0.17 - - bitmapdata-updates (1000 herhalingen) Player-versie: WIN 11,1102,55 (debug) - methode ... ttl ms ... gem. bitmapdata-updates 5003 5.00 - - aangepaste methode (1000 herhalingen) Player-versie: WIN 11,1.102,55 (debug) - methode ... ttl ms ... avg ms custom-methode 4408 4.41 -
De Prestatie test
geeft verschillende resultaten als ik de test doe. Dus ik heb het een paar keer geprobeerd en heb een gemiddelde tijd afgeleid. Conclusie: de snelste methode is de eerste, gevolgd door de derde en vervolgens de tweede benadering.
Dus opbergen BitmapData
voor bitmaps wanneer ze voor het eerst worden geïntroduceerd en gecontroleerd hitTest
elk frame na is feitelijk efficiënt, op voorwaarde dat deze sprites geen andere transformaties uitvoeren dan translatie (zoals rotatie, scheeftrekken en schalen) in de loop van de tijd. Anders wordt u gedwongen om de tweede of derde benadering toe te passen, en de derde is efficiënter, zoals aangegeven door de afbeelding hierboven.
U kunt uitchecken Collisions.as
en Results.as
voor het volledige script.
Ik ben daarna begonnen met het zoeken naar de specifieke coderegels die meer rekentijd in beslag namen. De tweede en derde benadering kostten meer tijd, dus ik ontleende er verschillende functies aan, die elk op verschillende punten braken. Bekijk een van de onderstaande resultaten.
- default hitTest (1000 iteraties) Player-versie: WIN 11,1.102,55 (debug) include bounds - methode ... ttl ms ... avg ms default hitTest 189 0.19 - - standaard hitTest (1000 iteraties) Player-versie: WIN 11,1.102,55 ( debug) include transform - methode ... ttl ms ... avg ms default hitTest 357 0.36 - - standaard hitTest (1000 iteraties) Player-versie: WIN 11,1.102,55 (debug) include hittest - methode ... ttl ms ... avg ms default hitTest 4427 4.43 - - aangepaste methode (1000 iteraties) Player-versie: WIN 11,1,102,55 (debug) inclusief grenzen en transformatie - methode ... ttl ms ... avg ms aangepaste methode 411 0.41 - - aangepaste methode (1000 herhalingen) Player-versie: WIN 11, 1,102,55 (debug) omvatten draw and bounds - methode ... ttl ms ... avg ms custom methode 3320 3.32 -
De eerste, tweede en derde keer verwijzen naar de tweede benadering op verschillende breekpunten, en de vierde en vijfde keer verwijzen naar de derde benadering. Kijkend naar de resultaten van de derde en de vijfde keer, BitmapData.draw
lijkt veel rekentijd te vergen. En de tijd die het kost om te tekenen met de tweede benadering lijkt in de rekentijd duurder te zijn, wat me doet denken dat de grootte voor BitmapData.draw
werken doet er toe. U kunt uitchecken Collisions2.as
en Results2.as
voor de volledige scripts.
Een ding dat ik een beetje verontrustend vind, is de inconsistentie van deze tests - ik krijg altijd dezelfde resultaten niet, hoewel ze bijna altijd dezelfde rangorde volgen. Het is dus goed genoeg om een eenvoudige vergelijking tussen functies te maken.
Nou, bedankt voor je tijd om deze kleine tip te lezen. Ik hoop dat het nuttig is geweest. Laat reacties achter als je het in deze tutorial niet eens bent met iets. Ik zou graag reageren op feedback!