Petit conseil réaction de collision entre un cercle et un segment de ligne

Dans les précédents conseils, nous avons examiné la collision détection: détecte essentiellement le chevauchement de deux formes. Maintenant, nous sommes prêts à regarder collision réaction: faire que quelque chose arrive à cause d'une collision. Dans ce petit conseil, nous examinerons les réactions de réflexion et de glissement.


Aperçu du résultat final

Examinons le résultat final que nous obtiendrons à la fin de ce didacticiel. Chaque démo Flash a un bouton de redémarrage; cliquez dessus pour réinitialiser la position des cercles en haut de la scène.

La première démo se dévoile réflexion:

La deuxième montre glissement:


Étape 1: La formule de réflexion

J'ai parcouru ce sujet plusieurs fois avec les étudiants et l'expérience m'a appris que l'approche directe consistant à expliquer les mathématiques vectorielles aux nouveaux étudiants aboutit à des visages vides et à des esprits confus. Ainsi, au lieu de faire une conférence de maths ici, je référerai ceux qui sont intéressés à approfondir ce sujet à la page de réflexion de Wolfram.

Ici, je vais simplifier mes explications avec des schémas ci-dessous. Rappel de l'ajout de vecteur:

Observez maintenant le diagramme ci-dessous. A est la vitesse du cercle avant une collision et A 'sa ​​vitesse après la collision.

Il est évident que A '= A + 2 V (Ap), où VIRGINIEp) représente le vecteur de magnitude Ap, dans le sens de la gauche normale. (Vous pouvez le voir en suivant les lignes en pointillés.)

Afin d'obtenir VIRGINIEp), nous projetterons A sur la gauche normale.


Étape 2: mise en œuvre

Voici l'implémentation ActionScript de la réflexion. J'ai mis en évidence les parties importantes. La ligne 67 - 69 est à calculer VIRGINIEp) (v_leftNormSeg2) et la ligne 70 met en oeuvre la formule. Vous pouvez vous référer au texte complet Actionscript sous Réaction1.as.

(Vous devez reconnaître la plupart du code de la dernière astuce rapide.)

 rafraîchissement de fonction privée (e: événement): void pour (var i: int = 0; i < circles.length; i++)  //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); var c1_circle_onLine:Number = c1_circle.projectionOn(line); //if collision happened, undo movement if (Math.abs(c1_circle_onNormal) <= circles[i].radius && line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude()) //redefine velocity var v_leftNormSeg2:Vector2D = leftNormal.clone(); var leftNormSeg2_mag:Number = Math.abs(velos[i].projectionOn(leftNormal)) v_leftNormSeg2.setMagnitude(leftNormSeg2_mag); velos[i] = velos[i].add(v_leftNormSeg2.multiply(2));  circles[i].x += velos[i].x; circles[i].y += velos[i].y;  

Étape 3: une version interactive

Prenez note que cette formule de réflexion est applicable à la ligne de tout gradient. En fait, vous pouvez programmer votre ligne pour qu'elle soit ajustable à l'exécution et la voir refléter des cercles comme la présentation Flash ci-dessous. Il suffit de cliquer et de glisser vers le bas pour le redéfinir.


Étape 4: glisser le long de la ligne

Le concept de glissement le long de la ligne est presque identique à la réflexion. Observez le schéma ci-dessous.

Le vecteur de slide est A '= A + V (Ap) avec VIRGINIEp) représentant un vecteur de magnitude UNEp. Encore une fois, pour obtenir unp nous projetterons A sur la gauche normale.

Notez que lorsque le cercle glisse le long de la ligne, il entre en collision avec la ligne. Bien sûr, les points de collision diffèrent entre les cercles qui entrent en collision sur la ligne, de sorte que certains chevauchent la ligne au fur et à mesure de son déplacement. Cela ne semble pas bon, alors nous devrons les repositionner.


Étape 5: redéfinir l'emplacement

Maintenant, repositionnons les cercles sur la ligne tout en maintenant leur contact avec la ligne. Reportez-vous au schéma ci-dessous.

Une variable importante à calculer est la projection de A le long de la ligne. Le rayon de cercle est facilement disponible, et nous avons déjà B, nous pouvons donc former les vecteurs de B et C. Ajouter ces deux va nous donner A, l’emplacement exact du repositionnement du cercle. Simple!

La présentation Flash ci-dessous est codée selon l’idée mentionnée. Mais il y a un problème: les cercles tremblent le long de la ligne.

Il y a un dernier détail que nous avons manqué. Le diagramme ci-dessus montre que la magnitude de C devrait être équivalente au rayon du cercle. Cependant, cela positionnera le cercle au-dessus de la ligne. Comme il n'y a pas de collision détectée à cet endroit, le cercle tombera à nouveau sur la ligne, ce qui marquera à son tour la détection de collision et provoquera le repositionnement du cercle..

Ce cycle se répète jusqu'à la fin du segment de ligne; le résultat visuel de ce cycle est l'effet de scintillement.

La solution à ce problème consiste à définir la magnitude de C sur une valeur légèrement inférieure au rayon du cercle: (rayon de cercle - 1), dire. Observez la démo Flash ci-dessous qui utilise cette idée:


Étape 6: mise en œuvre

Alors, voici l'extrait de code ActionScript important pour glisser le long de la ligne. J'ai souligné les parties importantes.

 rafraîchissement de fonction privée (e: événement): void pour (var i: int = 0; i < circles.length; i++)  //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); var c1_circle_onLine:Number = c1_circle.projectionOn(line); //check for collision if (Math.abs(c1_circle_onNormal) <= circles[i].radius) //check if within segment //if within segment, reposition and recalculate velocity if (line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude())  //repostion circle var v_lineSeg:Vector2D = line.clone(); v_lineSeg.setMagnitude(c1_circle_onLine); var v_leftNormSeg1:Vector2D = leftNormal.clone(); v_leftNormSeg1.setMagnitude(circles[i].radius - 1); //v_leftNormSeg1.setMagnitude(circles[i].radius); //uncomment this to check out the error: jittering effect var reposition:Vector2D = v_lineSeg.add(v_leftNormSeg1) circles[i].x = x1+reposition.x; circles[i].y = y1+reposition.y; //redefine velocity var v_leftNormSeg2:Vector2D = leftNormal.clone(); var leftNormSeg2_mag:Number = Math.abs(velos[i].projectionOn(leftNormal)) v_leftNormSeg2.setMagnitude(leftNormSeg2_mag); var veloAlongLine:Vector2D = velos[i].add(v_leftNormSeg2); circles[i].x += veloAlongLine.x; circles[i].y += veloAlongLine.y;  //if not in segment (e.g. slide out of segment), continue to fall down else  circles[i].x += velos[i].x; circles[i].y += velos[i].y;   //No collision in the first place, fall down else  circles[i].x += velos[i].x; circles[i].y += velos[i].y;   

Conclusion

J'espère que c'est utile. Merci d'avoir lu. Demandez-moi s'il y a des questions, et je vous verrai prochain conseil rapide.