In diesem Tutorial verfolge ich die von Richard Davey (Danke, Richard!) Vorgeschlagene und von ihm und anderen verwendete Methode, um Kollisionen zwischen Bitmaps mit einer subtilen Modifikation zu erkennen. Ich werde auch die Leistung zwischen verschiedenen Ansätzen der Bitmap-Kollisionserkennung mit dem PerformanceTest-Kabelbaum von Grant Skinner vergleichen.
Hinweis: Dieser Artikel ist nicht nur Teil der Shoot-'Em-Up-Sitzung, sondern auch Teil der Kollisionserkennung und -reaktion.
Ich beschreibe diesen alternativen Ansatz hier kurz.
Zuerst prüfen wir mit Rectangle, ob sich die Begrenzungsrahmen der beiden Bitmaps überlappen. Die Skripte sind wie folgt. Zuerst die Variablen.
private var enemy1: Bitmap, myShip: Bitmap; private var myShipSp: Sprite; private var rec_e: Rechteck, rec_m: Rechteck; private var intersec: Rechteck;
Feind1 = neues E1 als Bitmap; addChild (enemy1); myShip = new My as Bitmap; myShipSp = neuer Sprite; 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; // Zeichnen von Boxen um das Sprite-Draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0);
Hier überprüfen wir, ob sich die Felder überschneiden. Auschecken DetectVisible.as
im Quelldownload für das vollständige Skript
private function refresh (e: Event): void // Bestimmung des Begrenzungsrahmens des Schnittbereichs rec_e = enemy1.getBounds (stage); rec_m = myShipSp.getBounds (Stufe); intersec = rec_e.intersection (rec_m); // Zeichne die Begrenzungsbox beider Sprites neu: this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // Zeichne nur den Begrenzungsrahmen des Schnittbereichs, wenn es einen gibt wenn (! intersec.isEmpty ()) lines.graphics.clear (); zeichnen (intersec, linien); t.text = "Überschneidungsbereich durch rotes Rechteck." else t.text = "Kein Schnittbereich."
Hier ist eine Demo. Ziehe das kleinere Raumschiff herum.
(Machen Sie sich keine Sorgen über das rote Kästchen, das zurückgelassen wird, wenn das Schiff aus dem Begrenzungsrahmen des anderen gezogen wird.)
Wenn sich also ein Schnittfeldbereich befindet, prüfen wir, ob sich in dem Bereich überlappende Pixel befinden. Versuchen wir jedoch zuerst, eine Bitmap in diesen Schnittbereich zu zeichnen. Das vollständige Skript ist in DetectVisible2.as
private function refresh (e: Event): void // Bestimmung des Begrenzungsrahmens des Schnittbereichs rec_e = enemy1.getBounds (stage); rec_m = myShipSp.getBounds (Stufe); intersec = rec_e.intersection (rec_m); // Zeichne die Begrenzungsbox beider Sprites neu: this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // Zeichne nur den Begrenzungsrahmen des Schnittbereichs, wenn es einen gibt wenn (! intersec.isEmpty ()) lines.graphics.clear (); zeichnen (intersec, linien); // den Schnittbereich zeichnen und auf Überlappung des farbigen Bereichs prüfen var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = new 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 = "Schnittpunkt durch rotes Rechteck. \ n" else t.text = "Kein Schnittbereich."
Beachten Sie, dass, da wir den Bereich mithilfe einer Matrix zeichnen, alle Skalierungen, Schräglagen und andere Transformationen in beiden Bitmaps berücksichtigt werden. Hier ist eine Demo; check out das Kästchen in der unteren linken Ecke.
Wie prüfen wir das richtige Pixel? Zunächst einmal geben wir der Farbe dieses Schnittfelds einen schwarzen Farbton (Rot = 0, Grün = 0, Blau = 0). Dann wird der Farbton des kleineren Raumschiffes mit dem Mischmodus ADD als grün in dieses dunkle Kästchen gezeichnet. In ähnlicher Weise wird der Schatten des größeren stationären außerirdischen Raumschiffes rot gezeichnet.
Für die Raumschiffe gibt es jetzt Bereiche mit roten und grünen Flächen, und Schwarz, wenn keine überlappenden Bereiche vorhanden sind. Wenn sich jedoch Pixel aus diesen beiden überlappenden Bitmaps befinden, werden diese gelb gezeichnet (Rot = 255, Grün = 255, Blau = 0). Wir benutzen die Methode Bitmapdata.getColorBoundsRect
um zu prüfen, ob dieser Bereich vorhanden ist.
Hier ist der Ausschnitt Main.as
// den Schnittbereich zeichnen und auf Überlappung des farbigen Bereichs prüfen var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = new BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y // Tweak-Farbe bdt_intersec.draw (enemy1, eM, neue ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, neue 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 = "Schnittbereich durch rotes Rechteck. \ n" // Auf Vorhandensein der richtigen Farbe prüfen intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); if (! intersec_color.isEmpty ()) t.appendText ("Und in dem Bereich gibt es miteinander verbundene Pixel.");
Beachten Sie, dass wir die roten und blauen Komponenten in Zeile 113 unterdrücken, um Grün für das kleine Raumschiff maximal auszusuchen. In Zeile 112 machen wir dasselbe mit dem Alien-Raumschiff mit den blauen und grünen Komponenten.
Nachdem ich Kommentare zu Leistungsproblemen bei der Kollisionserkennung erhalten hatte, entschied ich mich dazu, einige schnelle und schmutzige Tests mit diesen Ansätzen durchzuführen. Ich habe 20 feindliche Raumschiffe und ein Spieler-Raumschiff erstellt und die Kollisionserkennung zwischen diesem einen Spielerschiff gegen das andere 20 überprüft. Diese Sprites werden in derselben Umgebung gepackt, um die Kollisionserkennung für alle Ansätze zu erzwingen, um einen vollständigen Lauf zu erzielen.
Der erste Ansatz ist der einfachste. BitmapData
wird beim Start erfasst und für jeden Frame wird die Kollisionserkennung mit überprüft BitmapData.hitTest ()
. Für den zweiten Ansatz, BitmapData
wird für jeden Rahmen aktualisiert, und die Kollisionserkennung wird basierend auf diesen durchgeführt BitmapData
gefangen. Der dritte bezieht sich auf den Ansatz, der in diesem Tutorial vorgeschlagen wird.
Das Ergebnis für einen der von mir durchgeführten Tests ist wie folgt.
- Bitmapdata behoben (1000 Iterationen) Player-Version: WIN 11,1,102,55 (Debug) - Methode… TTL ms… Durchschnitt ms Bitmapdata behoben 168 0.17 - - Bitmapdata-Updates (1000 Iterationen) Player-Version: WIN 11,1,102,55 (Debug) - Methode… ttl ms… avg ms Bitmapdata-Aktualisierungen 5003 5.00 - - benutzerdefinierte Methode (1000 Iterationen) Spielerversion: WIN 11,1,102,55 (Debug) - Methode… ttl ms… avg ms benutzerdefinierte Methode 4408 4.41 -
Das Leistungstest
gibt bei jedem Test unterschiedliche Ergebnisse. Also habe ich es ein paar Mal ausgeführt und eine durchschnittliche Zeit ermittelt. Fazit: Die schnellste Methode ist die erste, gefolgt von der dritten und dann der zweiten Annäherung.
So einlagern BitmapData
für Bitmaps, wenn sie zum ersten Mal in die Bühne eingefügt werden und auf hitTest
Jeder Frame danach ist tatsächlich effizient, vorausgesetzt, diese Sprites führen keine anderen Transformationen als die Translation (wie Rotation, Skewing und Skalierung) über die Zeit aus. Andernfalls müssen Sie entweder den zweiten oder den dritten Ansatz wählen, und der dritte ist effizienter, wie das Bild oben zeigt.
Sie können auschecken Kollisionen.as
und Ergebnisse.als
für das vollständige Skript.
Danach machte ich mich auf die Suche nach bestimmten Codezeilen, die mehr Rechenzeit benötigten. Der zweite und dritte Ansatz hat mehr Zeit in Anspruch genommen, so dass ich mehrere Funktionen ableitete, von denen jede an verschiedenen Punkten brach. Sehen Sie sich eines der Ergebnisse unten an.
- default hitTest (1000 Iterationen) Player-Version: WIN 11,1,102,55 (Debug) Include Bounds - Methode… ttl ms… avg ms default hitTest 189 0.19 - - Standard-hitTest (1000 Iterationen) Player-Version: WIN 11,1,102,55 ( debug) include transform - method… ttl ms… avg ms default hitTest 357 0.36 - - default hitTest (1000 Wiederholungen) Spielerversion: WIN 11,1,102,55 (debug) include hittest - method… ttl ms… avg ms default hitTest 4427 4.43 - - benutzerdefinierte Methode (1000 Iterationen) Player-Version: WIN 11,1,102,55 (Debug) Inlcude-Grenzen und Transformationsmethode… ttl ms… avg ms benutzerdefinierte Methode 411 0.41 - - benutzerdefinierte Methode (1000 Iterationen) Player-Version: WIN 11, 1.102,55 (debug) include draw and bounds - Methode… ttl ms… avg ms benutzerdefinierte Methode 3320 3.32 -
Das erste, zweite und dritte Mal bezieht sich auf die zweite Annäherung an unterschiedlichen Haltepunkten, und die vierte und fünfte Mal beziehen sich auf die dritte Annäherung. Betrachtet man das dritte und fünfte Ergebnis, BitmapData.draw
scheint viel Rechenzeit zu benötigen. Und die Zeit, die für das Zeichnen mit dem zweiten Ansatz benötigt wird, scheint in der Berechnungszeit teurer zu sein BitmapData.draw
zu operieren ist wichtig. Sie können auschecken Collisions2.as
und Results2.as
für die vollständigen Skripte.
Etwas beunruhigend finde ich die Inkonsistenz dieser Tests - ich bekomme nicht immer die gleichen Ergebnisse, obwohl sie fast immer das gleiche Ranking haben. Es ist also gut genug, um einige Funktionen einfach zu vergleichen.
Vielen Dank für Ihre Zeit beim Lesen dieses kleinen Tipps. Ich hoffe es war nützlich. Hinterlassen Sie Kommentare, wenn Sie mit nichts in diesem Tutorial übereinstimmen. Ich würde gerne auf Feedback antworten!