Détection de collision au niveau des pixels pour les graphismes transformés

Dans le didacticiel précédent, nous avons présenté les bases de la détection de collision au niveau des pixels. Dans ce didacticiel, nous explorerons l’utilisation de matrices pour mieux définir la zone d’intérêt - très utile pour les graphiques pivotés, traduits ou asymétriques..


Détection de collision

Ceci est la dernière pièce que nous allons essayer de programmer. Cliquez sur la balle et accrocher pour lancer la démo interactive.

Notez que malgré la rotation et la transformation du graphique de la noix de coco, nous avons toujours une détection de collision parfaite au pixel près..


Étape 1: le dessiner Une fonction

La détection de collision au niveau des pixels nécessite que le graphique soit converti en un BitmapData objet. Nous avons examiné cela dans le didacticiel précédent en utilisant l'analogie d'une radiographie. Dans ce tutoriel, je vais expliquer ce processus de "radiographie".

Supposons que nous ayons un morceau de graphique (diagramme 1) et que nous voulions le convertir en BitmapData objet; nous devons définir les dimensions du BitmapData objet premier (diagramme 2). Dans ce cas, c'est assez simple car le graphique est largeur et la taille propriétés fournissent cela. Puis on appelle le dessiner() méthode; les pixels au moins à moitié occupés par le graphique seront remplis, en théorie (diagramme 3). Ce tableau de pixels sera comparé à un autre tableau de pixels d’un autre graphique pour vérifier la collision entre les deux (diagramme 4)..


Étape 2: Différents espaces de coordonnées

Différents espaces de coordonnées sont utilisés dans Flash IDE (diagrammes 1 et 2 ci-dessus). Je suis sûr que tous les lecteurs en auraient fait l'expérience - voir mon tutoriel sur les espaces affines pour un aperçu plus détaillé.

Dans l'IDE Flash, nous dessinons une image et la transformons en un symbole de type MovieClip. Quand on double clique sur le MovieClip, on arrive à un espace de coordonnées différent (diagramme 3). À partir de là, vous pouvez cliquer sur l'étiquette de la scène pour quitter l'espace de coordonnées local de ce graphique et arriver à l'espace de coordonnées de la scène. Ensuite, nous pouvons commencer à transformer l'instance de MovieClip sur la scène (schéma 4). En fait, nous pouvons créer plusieurs instances du MovieClip, chacun d'entre eux ayant différentes transformations.

Dans la bibliothèque, le graphique original reste inchangé malgré tous les ajustements apportés à ses copies sur la scène. C’est rationnel car chaque fois que nous faisons une nouvelle copie d’un MovieClip sur scène, ils sont toujours identiques à l'original de la bibliothèque. Maintenant, la question est: "Comment Flash capture-t-il toutes les transformations que nous avons apportées aux copies sur la scène?" Eh bien, ils utilisent chacun le MovieClip.transform.matrix propriété pour capturer toutes vos transformations (translation, rotation, biais, etc).

Retournons maintenant à l'endroit où nous nous sommes arrêtés. Il est crucial que nous comprenions cela car le dessiner() méthode de BitmapData ne fait pas référence à l'instance graphique sur la scène lors de l'exécution d'une "radiographie", mais plutôt à la source graphique inchangée dans la bibliothèque.

le BitmapData le premier pixel de l'objet s'aligne avec le point d'alignement (le point rouge) du MovieClip sur l’espace de coordonnées local (voir diagramme 3 à l’étape 1), puis capture le graphique sous forme de pixels avec les dimensions spécifiées.

Quand cela vient à hitTest vérifie, ActionScript aligne ce premier pixel (le pixel en haut à gauche) du BitmapData objet avec le point d’inscription de l’instance graphique sur la scène. Avec cela, tous les pixels de la BitmapData l'objet sera mappé sur l'espace de coordonnées de la scène et obtiendra ses coordonnées individuelles. Des vérifications peuvent être effectuées ultérieurement en comparant ces coordonnées entre deux bitmaps pour voir si des pixels se chevauchent.

Remarque: Cette explication suppose que MovieClip ou Lutin instance est ajoutée à la liste d'affichage de la scène. En ActionScript, nous pouvons en fait ajouter des objets d'affichage à la classe de document elle-même, car elle s'étend MovieClip ou Lutin.


Étape 3: le problème

Donc, si le graphique d'intérêt est tourné sur la scène, comment effectuons-nous dessiner()?

À partir du diagramme ci-dessus, nous pouvons clairement voir ces problèmes.

Diagramme Problème La description
1 Dimension de BitmapData objet Comme l'orientation de l'objet a changé, la dimension requise de BitmapData casting ne peut plus être facilement pris de la largeur et la taille propriétés de l'instance graphique.
2 Orientation de la source graphique L'instance du graphique sur la scène est pivotée, mais pas celle de la bibliothèque. Nous devons prendre un instantané de la source graphique transformée à partir de la bibliothèque..
3 Coordonnée du pixel en haut à gauche (pixel de départ) de BitmapData objet Nous ne pouvons pas aligner le BitmapData premier pixel de l'objet avec le point d'enregistrement de l'instance graphique. Ce sera incorrect.

Étape 4: la solution

Pour résoudre ces problèmes, nous allons d’abord définir un rectangle délimitant étroitement l’instance graphique tournée sur la scène. Les dispositions ActionScript pour cela via le getBounds () une fonction. Cela résoudra notre premier problème. Observez l'image ci-dessous. Notez qu'il y a une différence entre le point d'enregistrement de l'instance graphique et celui du rectangle..

J'ai inclus la présentation Flash ci-dessous pour montrer le cadre de sélection rectangulaire (zone rouge) et le cadre de sélection local (zone noire)


Étape 5: Transformation

Ensuite, nous apporterons un instantané de la source graphique transformée dans ce rectangle sur scène. Observez l'image ci-dessous.

Nous commençons par aligner le point d’enregistrement de la source graphique sur celui de la zone rectangulaire (diagramme 1). Ensuite, nous faisons pivoter (diagramme 2) et décalons (diagramme 3) avant de prendre la capture "image aux rayons X" de l'image sur le BitmapData objet (diagramme 4).

Nous pouvons le faire manuellement ou choisir de faire une copie de l'instance graphique. transformer.matrix propriété. Lors de l’utilisation de la seconde approche, il convient de ne pas utiliser le transformer.matrix Traduction property - sinon, les points d'alignement ne seront pas alignés comme vous le voyez dans le diagramme 1. Dans les deux cas, nous devrons calculer la distance x et y à compenser..


Étape 6: Implémentation d'ActionScript

Après cette longue explication, j'espère qu'il est plus facile de comprendre le code. J'ai souligné les lignes importantes et ajouté des commentaires:

 noix de coco de var privé: CTree, hk: Crochet; private var bdat1: BitmapData, bdat2: BitmapData; private var t1: TextField; private var angle: Nombre = 45 private var coconutBox: Rectangle; fonction publique Matrix_Bitmap4 () coco = new CTree (); addChild (noix de coco); noix de coco.rotation = angle; noix de coco.x = stage.stageWidth * 0,3; noix de coco.y = stage.stageHeight * 0,2; coconutBox = coconut.getBounds (this); // récupère une boîte rectangulaire à l'étape 2 var coconut_newX: Number = coco.x - coconutBox.x // obtient un offset x à l'étape 3 var coconut_newY: Number = coco.y - coconutBox.y // obtient un offset y à l'étape 3 var m : Matrix = new Matrix (); m.rotate (angle / 180 * Math.PI); // fait pivoter le graphique à l'étape 3 // var m: Matrix = coconut.transform.matrix // recommandé si de nombreuses transformations ont eu lieu. //m.tx = 0; m.ty = 0; // Dans ce cas, il fait le même travail que la ligne précédente. m.translate (coco_newX, coco_newY); // implémente l'offset bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); bdat1.draw (noix de coco, m);

En outre, ne pas oublier de changer l’emplacement du premier pixel (en haut à gauche) dans BitmapData objet à celui de la boîte rectangulaire

 vérification de fonction privée (e: événement): void var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) // var point1: Point = nouveau Point (coconut.x, coconut.y); // maintenant que nous avons une boîte différente avec un emplacement différent pour le pixel de départ, // nous devrions faire référence à coconutBox en tant que point de départ var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = nouveau Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Au moins un pixel est entré en collision" else t1.text = "Pas de collision"

Et voici un exemple du travail.


Étape 7: Mise à jour constante

Si la forme d’intérêt, en l’occurrence le cocotier, est en constante transformation (rotation, mise à l’échelle, rétrécissement, inclinaison, etc.), BitmapData L'objet doit être mis à jour sur chaque image, ce qui nécessitera un traitement. Notez également que j'ai opté pour l'approche alternative mentionnée à l'étape 4. Voici le script permettant de mettre à jour la copie radiographique du graphique pour chaque image:

 fonction privée updateBmp (): void coconutBox = coconut.getBounds (this); // récupère une boîte rectangulaire à l'étape 2 var coconut_newX: Number = coco.x - coconutBox.x // obtient un offset x à l'étape 3 var coconut_newY: Number = coco.y - coconutBox.y // obtient un offset y à l'étape 3 // var m: Matrix = new Matrix (); //m.rotate(angle / 180 * Math.PI); // rotation du graphique à l'étape 3 var m: Matrix = coconut.transform.matrix // recommandé si de nombreuses transformations se sont produites, m.tx = 0; m.ty = 0; // dans ce cas, il fait le même travail que la ligne précédente. m.translate (coco_newX, coco_newY); // implémente l'offset bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); b = new Bitmap (bdat1); addChild (b); b.x = stage.stageWidth * 0,3; b.y = stage.stageHeight * 0,2; bdat1.draw (noix de coco, m); 

La fonction suivante est effectuée à chaque image:

 vérification de fonction privée (e: événement): void coconut.rotation + = angle; // changements dynamiques à l'exécution coconut.scaleX + = 0.01 var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) updateBmp (); var point1: Point = nouveau Point (coconutBox.x, coconutBox.y); var point2: Point = nouveau Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Au moins un pixel est entré en collision" else t1.text = "Aucune collision" bdat1.dispose (); 

Et c'est la sortie. Commencez à faire glisser le crochet pour voir l'animation.


Conclusion

Je comprends que ce tutoriel n’est peut-être pas très rapide à lire, mais il est essentiel de bien comprendre ce qui se passe. J'espère que cela vous a été utile et si vous souhaitez mieux manipuler cette matrice 2x2, visitez mon article sur le sujet. Merci.