Problème de détection de collision en cercle


11

Je développe actuellement un clone de cassure et j'ai réussi à bloquer la détection de collision entre une balle (cercle) et une brique (polygone convexe) qui fonctionne correctement. J'utilise un test de détection de collision Circle-Line où chaque ligne représente et arête sur la brique polygone convexe.

La plupart du temps, le test Circle-Line fonctionne correctement et les points de collision sont résolus correctement.

La détection de collision fonctionne correctement.

Cependant, parfois, mon code de détection de collision retourne faux en raison d'un discriminant négatif lorsque la balle intersecte réellement la brique.

Échec de la détection de collision.

Je suis conscient de l'inefficacité de cette méthode et j'utilise des boîtes englobantes alignées sur l'axe pour réduire le nombre de briques testées. Ma principale préoccupation est s'il y a des bugs mathématiques dans mon code ci-dessous.

/* 
 * from and to are points at the start and end of the convex polygons edge.
 * This function is called for every edge in the convex polygon until a
 * collision is detected. 
 */

bool circleLineCollision(Vec2f from, Vec2f to)
{
    Vec2f lFrom, lTo, lLine;
    Vec2f line, normal;
    Vec2f intersectPt1, intersectPt2;
    float a, b, c, disc, sqrt_disc, u, v, nn, vn;
    bool one = false, two = false;

    // set line vectors
    lFrom = from - ball.circle.centre;      // localised
    lTo = to - ball.circle.centre;          // localised
    lLine = lFrom - lTo;                    // localised
    line = from - to;

    // calculate a, b & c values
    a = lLine.dot(lLine);
    b = 2 * (lLine.dot(lFrom));
    c = (lFrom.dot(lFrom)) - (ball.circle.radius * ball.circle.radius);

    // discriminant
    disc = (b * b) - (4 * a * c);

    if (disc < 0.0f)
    {
        // no intersections
        return false;
    }
    else if (disc == 0.0f)
    {
        // one intersection
        u = -b / (2 * a);

        intersectPt1 = from + (lLine.scale(u));
        one = pointOnLine(intersectPt1, from, to);

        if (!one)
            return false;
        return true;
    }
    else
    {
        // two intersections
        sqrt_disc = sqrt(disc);
        u = (-b + sqrt_disc) / (2 * a);
        v = (-b - sqrt_disc) / (2 * a);
        intersectPt1 = from + (lLine.scale(u));
        intersectPt2 = from + (lLine.scale(v));

        one = pointOnLine(intersectPt1, from, to);
        two = pointOnLine(intersectPt2, from, to);

        if (!one && !two)
            return false;
        return true;
    }
}

bool pointOnLine(Vec2f p, Vec2f from, Vec2f to)
{
    if (p.x >= min(from.x, to.x) && p.x <= max(from.x, to.x) && 
        p.y >= min(from.y, to.y) && p.y <= max(from.y, to.y))
        return true;
    return false;
}

Je ne trouve aucune différence entre lLine et line ...
FxIII

Le test pointOnLine peut être simplifié et effectué avant de calculer le point réel.
FxIII

comment sqrt_disc est calculé?
FxIII

Désolé FxIII, je devais être un peu confus quand je localisais mes vecteurs, je ne savais pas que les vecteurs seraient identiques quand ils étaient soustraits les uns des autres. J'étais en train de nettoyer mon code un peu avant de poster et j'ai oublié de le remettre sqrt_disc = sqrt(disc);. Merci beaucoup pour votre réponse ci-dessous, cela m'a beaucoup aidé.
jazzdawg

Réponses:


20

Le segment allant de A à B peut être calculé comme

P (t) = A + D · t où D est B - A et t va de 0 à 1

Maintenant, le cercle est centré sur l'origine (déplacez A et B si nécessaire pour mettre le centre à l'origine) et a un rayon r .

Vous avez une intersection si pour certains t vous obtenez que le P a la même longueur de r ou, de manière équivalente, que la longueur de P au carré est équivalente à

La longueur au carré d'un vecteur est obtenue en faisant le produit scalaire d'un vecteur par lui-même (c'est si vrai que si l'on trouve une opération appropriée pour le produit scalaire, il peut définir un nouveau concept cohérent de longueur)

P · P = ( A + D · t) · ( A + D · t) =

A · A + 2 A · D t + D · D

On veut trouver pour quel t on obtient P · P = r² donc on finit par se demander quand

A · A + 2 A · D t + D · D t² = r²

ou lorsque

D · D t² + 2 A · D t + A · A -r² = 0

c'est la très célèbre équation quadratique

at² + bt + c = 0

avec

a = D · D ; b = 2 A · D et c = A · A -r²

Nous devons vérifier si le déterminant b² - 4ac est positif et nous trouvons donc 2 valeurs de t qui nous donnent les points d'intersections P (t).

t doit être compris entre 0 et 1 sinon nous avons trouvé des solutions qui se situent sur la droite passant par A et B mais qui sont avant A ou après B

[ÉDITER]

Étant donné que d'autres questions peuvent trouver une aide à cette réponse, j'ai décidé d'essayer de simplifier le raisonnement dans cette modification en utilisant quelques images. condition de départ Ceci est la condition de départ. Maintenant, concentrez-vous sur le segment A_B

Segment allant de A à B

D est le vecteur qui déplace A dans B donc si t est compris entre 0 et 1, D · t est une "fraction propre" de D donc le point A + D · t se situe dans le segment A_B : les points bruns viennent quand t est entre 0 et 1 et le vert foncé est lorsque t> 1.

Maintenant, nous pouvons simplifier les choses si nous déplaçons le centre du cercle dans l'origine. Cela peut toujours être fait car il s'agit simplement d'un changement de système de coordonnées qui préserve la géométrie, les angles, l'intersection, les mesures, etc.

cercle se déplaçant vers le centre

Nous avons maintenant un moyen simple de calculer la longueur de P lorsque t varie et de dire pour laquelle t P franchit les limites du cercle.

Exemples

Comme vous le voyez, P ' est de longueur supérieure à r tandis que P " est inférieur à r. Puisque la longueur du vecteur et r sont des nombres positifs, la relation d'ordre supérieur ou inférieur à ce qui est conservé est que nous calculons la relation entre les longueurs carré et le rayon carré. P * 1 et P * 2 sont le point qui rend le | P | ² égal à r²

Comme mentionné dans la section de pré-édition, nous arrivons à obtenir une équation quadratique où t est notre variable. Comme on le sait, les valeurs de solution de t vont du cas où t est un couple de nombres complexes - cela signifie pas d'intersection; le cas où t sont deux solutions égales - cela signifie qu'il y a une intersection; le cas où il y a deux solutions distinctes - cela signifie qu'il y a deux intersections.

Le discriminant est utilisé pour discriminer la condition précédente et un test de validité est effectué sur t pour voir s'il s'agit d'une intersection valide mais en dehors de notre segment - c'est-à-dire que la solution t doit être réelle et entre 0 et 1 pour être considérée comme une intersection appropriée qui tombe dans le segment A_B


3
C'est le bon algorithme à utiliser. Une très bonne description de son fonctionnement peut être trouvée dans Real Time Rendering Third Edition , pages 787 à 791. Si vous pouvez le trouver dans une bibliothèque, ça vaut le coup d'œil.
Darcy Rayner du

4
Avec le 8ème vote positif à cette réponse, j'ai atteint 2k points de réputation. J'apprécie énormément la confiance que vous m'accordez. C'est à la fois une reconnaissance de mes efforts et une incitation à continuer à faire de mon mieux pour produire une réponse de la plus haute qualité possible. Merci
FxIII

Attendez, est-ce que cela explique les deux cas d'angle correctement? Par exemple, un cercle peut traverser le plan défini par la ligne à l'extérieur de t0 <= t <= t1, mais toucher les points d'extrémité du segment de ligne un peu plus tard. Vous devez vérifier la distance minimale entre les extrémités de ligne et le chemin des cercles. Si cette distance est inférieure au rayon du cercle, alors la ligne a été atteinte.
Darcy Rayner

@DarcyRayner voulez-vous dire le cas où les deux points sont à l'intérieur de la zone du cercle?
FxIII
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.