Actualmente estoy desarrollando un clon de ruptura y me he topado con un obstáculo para que la detección de colisión entre una bola (círculo) y un ladrillo (polígono convexo) funcione correctamente. Estoy usando una prueba de detección de colisión Circle-Line donde cada línea representa y borde en el ladrillo de polígono convexo.
Para la mayoría de las veces, la prueba Circle-Line funciona correctamente y los puntos de colisión se resuelven correctamente.
Detección de colisión funcionando correctamente.
Sin embargo, de vez en cuando mi código de detección de colisión devuelve falso debido a un discriminante negativo cuando la bola realmente cruza el ladrillo.
Error de detección de colisión.
Soy consciente de la ineficiencia con este método y estoy usando cuadros delimitadores alineados con ejes para reducir la cantidad de ladrillos probados. Mi principal preocupación es si hay algún error matemático en mi código a continuación.
/*
* 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;
}
fuente
sqrt_disc = sqrt(disc);
volver a colocarlo . Muchas gracias por su respuesta a continuación, me ayudó mucho.Respuestas:
El segmento que va de A a B se puede calcular como
P (t) = A + D · t donde D es B - A y t va de 0 a 1
Ahora el círculo está centrado en el origen (mueve A y B si es necesario para poner el centro en el origen) y tiene un radio r .
Tienes una intersección si para alguna t obtienes que P tiene la misma longitud de r o, de manera equivalente, que la longitud de P al cuadrado es equivalente a r²
La longitud al cuadrado de un vector se obtiene haciendo el producto punto de un vector por sí mismo (esto es tan cierto que si uno encuentra una operación adecuada para el producto punto puede definir un concepto nuevo y consistente de longitud)
P · P = ( A + D · t) · ( A + D · t) =
A · A + 2 A · D t + D · D t²
Queremos encontrar para qué t obtenemos P · P = r², así que terminamos preguntándonos cuándo
A · A + 2 A · D t + D · D t² = r²
o cuando
D · D t² + 2 A · D t + A · A -r² = 0
esta es la famosa ecuación cuadrática
at² + bt + c = 0
con
a = D · D ; b = 2 A · D yc = A · A -r²
Tenemos que verificar si el determinante b² - 4ac es positivo, por lo que encontramos 2 valores de t que nos dan los puntos de intersección P (t).
t debe estar entre 0 y 1; de lo contrario, encontramos soluciones que se encuentran en la línea que pasa por A y B pero que están antes de A o después de B
[EDITAR]
Como otras preguntas pueden encontrar alguna ayuda para esta respuesta, decidí intentar simplificar el razonamiento en esta edición usando algunas imágenes. Esta es la condición inicial. Ahora concéntrese en el segmento A_B
D es el vector que mueve A hacia B, por lo que si t está entre 0 y 1, D · t es una "fracción propia" de D, por lo que el punto A + D · t se encuentra en el segmento A_B : los puntos marrones aparecen cuando t es entre 0 y 1 y el verde oscuro es cuando t> 1.
Ahora podemos simplificar las cosas si movemos el centro del círculo al origen. Esto siempre se puede hacer porque es un simple cambio de sistema de coordenadas que preserva la geometría, los ángulos, la intersección, las medidas, etc.
Ahora tenemos una manera simple de calcular la longitud de P cuando t varía y decir para qué t P cruza los límites del círculo.
Como puede ver, P ' tiene una longitud mayor que r, mientras que P " es menor que r. Dado que tanto la longitud del vector como r son números positivos, la relación de orden de ser mayor o menor que la preservada es si calculamos la relación entre las longitudes al cuadrado y el radio al cuadrado. P * 1 y P * 2 son el punto que hace que | P | ² sea igual a r²
Como se mencionó en la sección previa a la edición, llegamos para obtener una ecuación cuadrática donde t es nuestra variable. Como se sabe, los valores de solución de t varían desde el caso en que t es un par de números complejos, lo que significa que no hay intersección; el caso cuando t son dos soluciones iguales, eso significa que hay una intersección; el caso cuando hay dos soluciones distintas, eso significa que hay dos intersecciones.
El Discriminante se usa para discriminar la condición anterior y se realiza una prueba de validez en t para ver si es una intersección válida pero fuera de nuestro segmento, es decir, la solución t debe ser real y entre 0 y 1 para considerarse una intersección adecuada que cae en el segmento A_B
fuente