Tout d'abord, dans le cas des rectangles alignés sur les axes, la réponse de Kevin Reid est la meilleure et l'algorithme est le plus rapide.
Deuxièmement, pour les formes simples, utilisez les vitesses relatives (voir ci-dessous) et le théorème de séparation des axes pour la détection de collision. Il va vous dire si une collision se produit dans le cas de mouvement linéaire (pas de rotation). Et s’il existe une rotation, vous avez besoin d’un peu de temps pour qu’elle soit précise. Maintenant, pour répondre à la question:
Comment savoir dans le cas général si deux formes convexes se croisent?
Je vais vous donner un algorithme qui fonctionne pour toutes les formes convexes et pas seulement les hexagones.
Supposons que X et Y sont deux formes convexes. Elles se croisent si et seulement si elles ont un point commun, c'est-à-dire qu'il existe un point x ∈ X et un point y ∈ Y tel que x = y . Si vous considérez l'espace comme un espace vectoriel, cela revient à dire x - y = 0 . Et maintenant nous arrivons à cette affaire Minkowski:
La somme de Minkowski de X et Y est l'ensemble de tout x + y pour x ∈ X et y ∈ Y .
Un exemple pour X et Y
X, Y et leur somme de Minkowski, X + Y
En supposant que (-Y) est l'ensemble de tout -y pour y ∈ Y , alors vu le paragraphe précédent, X et Y se croisent si et seulement si X + (-Y) contient 0 , c'est-à-dire l'origine .
Remarque secondaire: pourquoi écris-je X + (-Y) au lieu de X - Y ? Eh bien, parce qu’en mathématiques, il existe une opération appelée différence de Minkowski de A et B qui s’écrit parfois X - Y et qui n’a pourtant rien à voir avec l’ensemble des x - y pour x ∈ X et y Y (le vrai Minkowski). la différence est un peu plus complexe).
Nous aimerions donc calculer la somme de Minkowski de X et -Y et déterminer si elle contient l'origine. L'origine n'est pas spéciale par rapport à un autre point, de sorte que pour déterminer si l'origine est dans un certain domaine, nous utilisons un algorithme qui pourrait nous indiquer si un point donné appartient à ce domaine.
La somme de Minkowski de X et Y a une propriété cool, qui est que si X et Y sont convexes, alors X + Y est aussi. Et il est beaucoup plus facile de savoir si un point appartient à un ensemble convexe que si cet ensemble n'était pas (connu pour être) convexe.
Nous ne pouvons pas calculer tous les x - y pour x ∈ X et y Y car il existe une infinité de tels points x et y , alors espérons-le, puisque X , Y et X + Y sont convexes, nous pouvons simplement utiliser les points "les plus à l'extérieur" définissant les formes X et Y , qui sont leurs sommets, et nous obtiendrons les points les plus à l'extérieur de X + Y , ainsi que d'autres.
Ces points supplémentaires sont "entourés" par les points les plus externes de X + Y, de sorte qu'ils ne contribuent pas à définir la forme convexe obtenue. Nous disons qu'ils ne définissent pas la " coque convexe " de l'ensemble des points. Donc, ce que nous faisons, c'est que nous nous en débarrassons en préparation de l'algorithme final qui nous indique si l'origine est dans la coque convexe.
La coque convexe de X + Y. Nous avons supprimé les sommets "intérieurs".
Nous avons donc
Un premier algorithme naïf
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
Les boucles ont évidemment une complexité O (mn) où m et n sont le nombre de sommets de chaque forme. L' minkoswki
ensemble contient au plus mn éléments. L' convexHull
algorithme a une complexité qui dépend de l'algorithme que vous avez utilisé , et vous pouvez viser O (k log (k)), où k est la taille de l'ensemble des points. Dans notre cas, nous obtenons donc O (mn log (mn) ) . L' contains
algorithme a une complexité linéaire avec le nombre d'arêtes (en 2D) ou de faces (en 3D) de la coque convexe, donc cela dépend vraiment de vos formes de départ, mais ne sera pas supérieur à O (mn). .
Je vais vous laisser google pour l' contains
algorithme pour les formes convexes, c'est un assez commun. Je peux le mettre ici si j'en ai le temps.
Mais c’est la détection de collision que nous effectuons afin de pouvoir l’optimiser beaucoup
À l’origine, deux corps A et B se déplaçaient sans rotation pendant un dt de temps (d'après ce que je peux dire en regardant vos images). Appelons v A et v B les vitesses respectives de A et B , qui sont constantes pendant notre pas de temps de durée dt . Nous obtenons ce qui suit:
et, comme vous le signalez dans vos images, ces corps balayent des zones (ou des volumes en 3D) au fur et à mesure qu'ils se déplacent:
et ils finissent comme A ' et B' après le pas de temps.
Pour appliquer notre algorithme naïf ici, il suffirait de calculer les volumes balayés. Mais nous ne faisons pas cela.
Dans le repère de B , B ne bouge pas (duh!). Et A a une certaine vitesse par rapport à B que vous obtenez en calculant v A - v B (vous pouvez faire l’inverse, calculer la vitesse relative de B dans le repère de A ).
De gauche à droite: vitesses dans le repère de base; vitesses relatives; calcul des vitesses relatives.
En ce qui concerne B comme immobile dans son propre cadre de référence, il suffit de calculer le volume A balaye comme il se déplace au cours dt avec sa vitesse relative v A - v B .
Cela diminue le nombre de sommets à utiliser dans le calcul de la somme de Minkowski (parfois considérablement).
Une autre optimisation possible est au point où vous calculez le volume balayé par l’un des corps, disons A. Il n’est pas nécessaire de traduire tous les sommets composant A. Seuls ceux qui appartiennent à des arêtes (faces en 3D) dont extérieur normal "face" la direction du balayage. Vous avez sûrement déjà remarqué cela lorsque vous avez calculé vos zones balayées pour les carrés. Vous pouvez déterminer si une normale se situe dans le sens de balayage en utilisant son produit scalaire avec le sens de balayage, ce qui doit être positif.
La dernière optimisation, qui n'a rien à voir avec votre question concernant les intersections, est vraiment utile dans notre cas. Il utilise les vitesses relatives mentionnées ci-dessus et la méthode dite de séparation des axes. Vous le savez sûrement déjà.
Supposons que vous connaissiez les rayons de A et B par rapport à leurs centres de masse (c’est-à-dire la distance entre le centre de masse et le sommet le plus éloigné de celui-ci), comme ceci:
Une collision peut se produire que s'il est possible que le cercle de délimitation de A rencontre celui de B . Nous voyons ici que ce ne sera pas, et la façon de dire l'ordinateur qui est de calculer la distance de C B à I comme dans l'image ci - dessous et assurez - vous qu'il est plus grand que la somme des rayons de A et B . Si c'est plus gros, pas de collision. Si c'est plus petit, alors collision.
Cela ne fonctionne pas très bien avec les formes qui sont plutôt longues, mais dans le cas de carrés ou d'autres formes similaires, c'est une très bonne heuristique pour éliminer les collisions. .
Le théorème des axes séparateurs appliqué à B et le volume balayé par A vous indiquent toutefois si la collision se produit ou non. La complexité de l'algorithme associé est linéaire avec la somme des nombres de sommets de chaque forme convexe, mais elle est moins magique au moment de gérer réellement la collision.
Notre nouvel algorithme, plus performant, utilise les intersections pour détecter les collisions, mais n'est pas aussi performant que le théorème des axes séparateurs pour indiquer si une collision se produit ou non.
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}