Détection de collision au niveau des pixels basée sur les couleurs des pixels

Dans ce didacticiel, je suivrai l'approche proposée par Richard Davey (Merci, Richard!) Et utilisée par lui et d'autres utilisateurs pour détecter les collisions entre des bitmaps avec une modification subtile. Je vais également comparer les performances de différentes approches de détection de collision bitmap à l'aide du harnais PerformanceTest de Grant Skinner..

Remarque: En plus de faire partie de la session Shoot-'Emm-Up, cet article fait également partie de Détection et réaction de collision..


Étape 1: aperçu

Je décris cette approche alternative en bref ici.

  1. Vérifiez s'il y a un chevauchement entre les deux bitmaps.
  2. Si c'est le cas, passez à l'étape 3. Sinon, abandonner.
  3. Vérifiez si des pixels opaques se chevauchent dans la zone de chevauchement.
  4. Si c'est le cas, les bitmaps se chevauchent. Sinon, abandonner.

Étape 2: Boîtes de délimitation

Premièrement, nous vérifions si les boîtes englobantes des deux bitmaps se chevauchent en utilisant Rectangle. Les scripts sont comme ci-dessous. Tout d'abord, les variables.

 private var ennemis1: Bitmap, myShip: Bitmap; private var myShipSp: Sprite; private var rec_e: Rectangle, rec_m: Rectangle; var privé intersec: Rectangle;
 ennemi1 = nouvel E1 en tant que bitmap; addChild (ennemi1); myShip = new My as Bitmap; myShipSp = nouveau Sprite; addChild (myShipSp); myShipSp.addChild (myShip); ennemi1.x = stage.stageWidth >> 1; myShipSp.x = stage.stageWidth >> 1; ennemi1.y = stage.stageHeight * 0.2; myShipSp.y = stage.stageHeight * 0.8; // dessinant des boîtes autour du sprite draw (ennemis1.getBounds (étape), this, 0); draw (myShipSp.getBounds (étape), this, 0);

Ici, nous vérifions toute zone de chevauchement entre les cases. Check-out DetectVisible.as dans le téléchargement source pour le script complet

 fonction privée refresh (e: Event): void // déterminant le cadre de délimitation de la zone d'intersection rec_e = ennemi1.getBounds (étape); rec_m = myShipSp.getBounds (étape); intersec = rec_e.intersection (rec_m); // redessine la boîte englobante des deux sprites this.graphics.clear (); dessine (ennemi1.getBounds (étape), this, 0); draw (myShipSp.getBounds (étape), this, 0); // dessine le cadre de sélection de la zone d'intersection s'il existe un if (! intersec.isEmpty ()) lines.graphics.clear (); dessiner (intersec, lignes); t.text = "Zone d'intersection par un rectangle rouge."  else t.text = "Pas de zone d'intersection." 

Voici une démo. Faites glisser le plus petit vaisseau spatial autour.

(Ne vous inquiétez pas de la boîte rouge qui est "laissée derrière" lorsque le navire est sorti de la boîte de sélection.)


Étape 3: Dessiner dans l'intersection

Donc, s'il y a une zone de cases qui se croisent, nous vérifions s'il y a des pixels qui se chevauchent dans la zone. Cependant, essayons d'abord de dessiner un bitmap dans cette zone d'intersection. Le script complet est en DetectVisible2.as

 fonction privée refresh (e: Event): void // déterminant le cadre de délimitation de la zone d'intersection rec_e = ennemi1.getBounds (étape); rec_m = myShipSp.getBounds (étape); intersec = rec_e.intersection (rec_m); // redessine la boîte englobante des deux sprites this.graphics.clear (); dessine (ennemi1.getBounds (étape), this, 0); draw (myShipSp.getBounds (étape), this, 0); // dessine le cadre de sélection de la zone d'intersection s'il existe un if (! intersec.isEmpty ()) lines.graphics.clear (); dessiner (intersec, lignes); // pour dessiner la zone d'intersection et vérifier le chevauchement de la zone colorée var eM: Matrix = ennemis1.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 (ennemi1, 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 = "Zone d’intersection par un rectangle rouge. \ n" else t.text = "Aucune zone d’intersection." 

Notez que puisque nous dessinons la zone à l'aide d'une matrice, toutes les transformations d'échelle, d'inclinaison et autres sur les deux bitmaps sont prises en compte. Voici une démo vérifier la case dans le coin inférieur gauche.


Étape 4: Vérifiez la couleur dans la zone d'intersection

Alors, comment pouvons-nous vérifier le bon pixel? Tout d’abord, nous donnons à la couleur de cette boîte d’intersection une nuance de noir (Rouge = 0, Vert = 0, Bleu = 0). Ensuite, la nuance du plus petit vaisseau spatial sera peinte dans cette zone sombre en vert, avec le mode de fusion ADD. De même, l'ombre du plus grand vaisseau spatial extraterrestre stationnaire sera peinte en rouge.

Alors maintenant, il y aura des zones rouges et vertes pour les vaisseaux spatiaux, et noires s'il n'y a pas de zones superposées. Toutefois, si des pixels de ces deux bitmaps se chevauchent, ils seront dessinés en jaune (rouge = 255, vert = 255, bleu = 0). Nous utilisons la méthode Bitmapdata.getColorBoundsRect vérifier l'existence de cette zone.

Voici l'extrait dans Main.as

 // pour dessiner la zone d'intersection et vérifier le chevauchement de la zone colorée var eM: Matrix = ennemis1.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 // modification de la couleur bdt_intersec.draw (ennemi1, eM, nouveau ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, nouveau 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 = "Zone d’intersection par un rectangle rouge. \ n" // recherche l’existence de la couleur droite intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); if (! intersec_color.isEmpty ()) t.appendText ("Et il y a des pixels qui se croisent dans la zone.");

Notez que nous supprimons les composants rouge et bleu de la ligne 113 afin de maximiser le vert pour le petit vaisseau spatial. Sur la ligne 112, nous faisons la même chose avec le vaisseau spatial extraterrestre avec les composants bleu et vert.


Comparer les approches

Ainsi, après avoir reçu des commentaires sur les problèmes de performances concernant la détection des collisions, j'ai décidé de faire des tests rapides et sales sur ces approches. J'ai créé 20 vaisseaux spatiaux ennemis et un vaisseau spatial joueur et vérifié la détection de collision entre ce navire joueur contre les 20 autres. Ces sprites sont emballés dans le même voisinage pour forcer la détection de collision pour que toutes les approches aient une course complète..

La première approche est la plus simple. BitmapData est capturé à l’initiation et pour chaque trame, la détection de collision est vérifiée en utilisant BitmapData.hitTest (). Pour la seconde approche, BitmapData est mis à jour chaque image et la détection de collision est faite sur la base de ceux BitmapData capturé. Le troisième fait référence à l'approche proposée par ce tutoriel.

Donc, le résultat pour l'un des tests que j'ai faits est comme ci-dessous.

 - bitmapdata fixé (1000 itérations) Version du joueur: WIN 11,1,102,55 (debug) - méthode… ttl ms… avg ms bitmapdata fixé 168 0,17 - - mises à jour bitmapdata (1000 itérations) Version du joueur: WIN 11,1.102,55 (debug) - méthode… ttl ms… avg ms bitmapdata mises à jour 5003 5.00 - - méthode personnalisée (1000 itérations) Version du joueur: WIN 11,1,102,55 (debug) - méthode… ttl ms… avg ms méthode personnalisée 4408 4.41 -

le Test de performance donne des résultats différents chaque fois que je lance le test. Alors je l'ai couru plusieurs fois et calculé un temps moyen. Conclusion: la méthode la plus rapide est la première, suivie de la troisième et de la seconde.

Donc stocker loin BitmapData pour les bitmaps quand ils sont introduits pour la première fois dans la scène et la vérification de hitTest chaque image après est réellement efficace, à condition que ces images-objets n'effectuent aucune transformation autre que la traduction (telle que la rotation, l'inclinaison et la mise à l'échelle) dans le temps. Sinon, vous serez obligé d'adopter la deuxième ou la troisième approche, et la troisième est plus efficace, comme indiqué par l'image ci-dessus..

Vous pouvez vérifier Collisions.as et Résultats pour le script complet.


Recherche de méthodes coûteuses

Je me suis ensuite embarqué pour rechercher les lignes de code spécifiques qui demandaient plus de temps de calcul. Les deuxième et troisième approches ont pris plus de temps; j'en ai donc tiré plusieurs fonctions, chacune se séparant à des moments différents. Découvrez l'un des résultats ci-dessous.

 - hitTest par défaut (1000 itérations) Version du joueur: WIN 11,1,102,55 (débogage) include bounds - méthode… ttl ms… avg ms défaut hitTest 189 0.19 - - hitTest par défaut (1000 itérations) Version du joueur: WIN 11,1.102,55 ( debug) include transform - méthode… ttl ms… avg ms défaut hitTest 357 0.36 - - hitTest par défaut (1000 itérations) Version du joueur: WIN 11,1.102,55 (débogage) include hittest - méthode… ttl ms… avg ms teste par défaut 4427 4.43 - - Méthode personnalisée (1000 itérations) Version du lecteur: WIN 11,1,102,55 (debug) - Inlcude les limites et la transformation - Méthode… ttl ms… avg ms méthode personnalisée 411 0,41 - - méthode personnalisée (1000 itérations) Version du lecteur: WIN 11, 1.102,55 (debug) inclut draw et bounds - méthode… ttl ms… avg ms méthode personnalisée 3320 3.32 -

Les première, deuxième et troisième fois se réfèrent à la deuxième approche à différents points d'arrêt, et les quatrième et cinquième fois à la troisième approche. En regardant les troisième et cinquième résultats, BitmapData.draw semble prendre beaucoup de temps de calcul. Et le temps pris pour dessiner avec la deuxième approche semble être plus coûteux en temps de calcul, ce qui me porte à penser que les tailles pour BitmapData.draw opérer importe. Vous pouvez vérifier Collisions2.as et Résultats2.as pour les scripts complets.

Ce qui me préoccupe un peu, c’est l’incohérence de ces tests: je n’obtiens pas toujours les mêmes résultats, bien qu’ils suivent presque le même classement à tout moment. Donc, il est assez bon de faire une simple comparaison entre les fonctions.

Conclusion

Merci d'avoir lu cette petite astuce. J'espère que cela a été utile. Ne laissez des commentaires si vous n'êtes pas d'accord avec quoi que ce soit dans ce tutoriel. J'aimerais réagir aux commentaires!