Nous avons couvert la détection de collision entre une ligne infinie et un cercle dans notre précédent conseil rapide. Cependant, le problème qui se posait était que la ligne s'étend plus loin que le segment de ligne visible; en fait, il se prolonge dans un hyperplan. Dans ce petit conseil, nous limiterons notre détection de collision à celle d’une ligne segment seulement.
Nous allons travailler vers ce résultat:
Cliquez sur le bouton Redémarrer pour repositionner les cercles en haut de la scène..
Il existe de nombreuses approches pour limiter la détection de collision à l'intérieur d'un segment de ligne. Nous allons examiner deux approches cette fois-ci. La première approche est mathématiquement un peu plus rigoureuse que la seconde, mais ce sont des concepts qui, si vous les maîtrisez bien, vous seront certainement utiles à l'avenir. Les deux approches manipulent la caractéristique du produit scalaire d’être une mesure de la façon dont deux vecteurs donnés sont parallèles..
Regardons la première approche. Supposons que A et B sont des vecteurs. Si A et B sont parallèles - ou du moins pointent dans la même direction - le produit scalaire entre A et B produira un nombre positif. Si A et B se dirigent directement l'un en face de l'autre - ou du moins dans des directions opposées - le produit scalaire entre A et B produira un nombre négatif. Si A et B sont orthogonaux (formant un angle de 90 °), le produit scalaire produira 0.
Le schéma ci-dessous résume cette description.
Nous aurons besoin de former les vecteurs B et C aux deux extrémités du segment de droite pour que leur produit scalaire avec le vecteur du segment de droite, A, puisse déterminer si le cercle est dans le segment..
Observez le diagramme ci-dessous. Si le cercle est dans le segment, la valeur du produit scalaire entre A et B est positive et celle entre A et C est négative..
Le diagramme ci-dessous montre l'évolution du produit scalaire selon que le cercle se situe au-delà ou à l'intérieur du segment de ligne. Notez les différences dans la valeur du produit scalaire.
Notez également que "dans le segment de ligne" ne signifie pas que le cercle coupe nécessairement le segment de ligne, mais qu'il se situe dans les deux lignes fines du diagramme ci-dessus..
Ainsi, comme nous l'avons vu dans le précédent conseil rapide, lorsque la collision se produit entre une ligne et un cercle, nous devons déterminer si le cercle est positionné dans le segment de ligne. Si tel est le cas, nous savons avec certitude qu'il existe une véritable intersection..
L'étape 2 explique le concept que nous utilisons pour limiter la détection de collision dans le segment de ligne. Cependant, il y a toujours un défaut dans la précision. Vous voyez, la zone définie est un peu inclinée; nous devrions viser à utiliser la zone définie selon le schéma ci-dessous.
C'est simple: nous calculons simplement D comme projection horizontale de A. Ensuite, au lieu d'utiliser A, nous utilisons un produit point à point avec B et C. Toutes les conditions décrites à l'étape 2 sont toujours valables, mais au lieu d'un segment incliné , nous avons défini une zone verticale.
Cette correction peut être appréciée visuellement si le cercle est grand; si le cercle était petit, son centre serait si proche de la ligne que ce défaut visuel serait difficile à détecter, de sorte que nous pourrions utiliser cette zone légèrement inclinée et économiser de la puissance de traitement..
Néanmoins, je vais essayer de faire les choses correctement. Vous pouvez choisir votre approche en modifiant légèrement la condition..
Le premier extrait Actionscript définit ici le vecteur D (v_line_onX
)
// Att2: obtenir le vecteur horizontal var line_onX: Number = line.projectionOn (new Vector2D (1, 0)); v_line_onX = new Vector2D (1, 0); v_line_onX.setMagnitude (line_onX);
Remarque: Nous utilisons les cours de mes tutoriels précédents ici. Vector2D a été introduit dans Gravity in Action, mais vous n'avez pas besoin de lire cela pour utiliser la classe, cela est inclus dans le téléchargement source.
Le deuxième extrait Actionscript configure ici B (c1_circle
) et C (c2_circle
) et vérifie la collision et si le cercle est ou non à l'intérieur du segment.
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); //Att2: get vector from c2 to circle var c2_circle:Vector2D = new Vector2D(circles[i].x - x2, circles[i].y - y2); circles[i].y += 2; if ( c1_circle_onNormal <= circles[i].radius && v_line_onX.dotProduct(c1_circle) > 0 && v_line_onX.dotProduct (c2_circle) < 0 ) //if collision happened, undo movement circles[i].y -= 2;
Voici le résultat de la première approche. Cliquez sur le bouton pour réinitialiser les positions de tous les cercles en haut de la scène..
La deuxième approche est beaucoup plus simple. Je vais essayer de travailler à l'envers depuis la fin cette fois-ci.
Observez le diagramme ci-dessous. Le segment de ligne est de C1 à C2. Il est clair que entrer en collision1
et entrer en collision3
sont à la fois en dehors du segment de ligne, et que seulement entrer en collision2
est dans le segment de ligne.
Soit v1, v2 et v3 des vecteurs allant de c1 aux cercles respectifs. Seules v2 et v3 sont parallèles - ou du moins pointent dans des directions similaires vers le vecteur ligne (c1 à c2). En recherchant une valeur positive dans le produit scalaire entre le vecteur ligne et chacun de ces vecteurs, de c1 aux centres de cercle correspondants (v1, v2, v3), nous pouvons facilement déterminer que la collision1 se situe au-delà du segment de ligne. En d'autres termes, c1. v1 .
Ensuite, nous allons concevoir une méthode pour déterminer que collision3 est en dehors du segment de ligne. Cela devrait être facile. Il est évident que la projection de v3 le long du vecteur de ligne dépassera la longueur du segment de ligne. Nous utiliserons cette caractéristique pour éliminer la collision3.
Alors permettez-moi de résumer la deuxième approche:
Voici l'implémentation ActionScript de ce qui précède:
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); //Att2: getting the relevant vectors var c1_circle_onLine:Number = c1_circle.projectionOn(line); circles[i].y += 2; if ( Math.abs(c1_circle_onNormal) <= circles[i].radius && line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude() ) //if collision happened, undo movement circles[i].y -= 2;
Essentiellement, cela produira le même résultat que la précédente, mais comme il y a quelques lignes de code plus courtes dans la deuxième approche, je suppose que c'est mieux.
J'espère que cela a aidé. Merci d'avoir lu. Ensuite, nous examinerons la réaction de collision.