Nota: Todo lo siguiente supone que la superficie de la pelota no tiene fricción (por lo que no comenzará a girar o rebotar de manera diferente porque sí lo es).
En el momento de la colisión, la pelota estará tocando la esquina. Cuando los objetos sólidos colisionan, una fuerza actuará a lo largo de la llamada superficie normal, es decir, perpendicular a la superficie en el punto de colisión.
Como es una pelota, perpendicular a la superficie es hacia el centro de la pelota. Ok, entonces sabemos la dirección de la fuerza, ¿qué pasa con su magnitud? Suponiendo una colisión elástica (y que el rectángulo no puede moverse), la pelota debe rebotar a la misma velocidad con la que impactó.
Sea (nDx, nDy) la velocidad después de la colisión, (oDx, oDy) la velocidad antes de la colisión y (x, y) la posición de la pelota en el punto de colisión. Supongamos además que la esquina con la que colisiona la pelota está en (0,0).
Expresando nuestras ideas como fórmulas, tenemos:
(nDx, nDy) = (oDx, oDy) + c * (x, y)
length (nDx, nDy) = length (oDx, oDy)
Lo que es equivalente a:
nDx = oDx + c * x
nDy = oDy + c * y
nDx^2 + nDy^2 = oDx^2 + oDy^2
Sustituyendo las dos primeras ecuaciones en la última, obtenemos:
(oDx + c * x)^2 + (oDy + c * y)^2 = oDx^2 + oDy^2
Expansión utilizando el teorema binomial
(a+b)^2 = a^2 + 2ab + b^2
rendimientos:
oDx^2 + 2 * oDx * c * x + (c * x) ^ 2 + oDy^2 + 2 * oDy * c * y + (c * y) ^ 2 = oDx^2 + oDy^2
2 * oDx * c * x + 2 * oDy * c * y + (c * x) ^ 2 + (c * y) ^ 2 = 0
(2 * oDx * x + 2 * oDy * y) * c + (x^2 + y^2) * c^2 = 0
Esta ecuación cuadrática para c
tiene dos soluciones, una de las cuales es 0. Obviamente, esa no es la solución que nos interesa, ya que generalmente la dirección de la pelota cambiará como resultado de la colisión. Para obtener la otra solución, dividimos ambos lados entre c y obtenemos:
(2 * oDx * x + 2 * oDy * y) + (x^2 + y^2) * c = 0
Es decir:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
Para resumir, tenemos:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
nDx = oDx + c * x
nDy = oDy + c * y
Editar : en el código:
if (collision) {
float x = ballX - cornerX;
float y = ballY - cornerY;
float c = -2 * (ballDx * x + ballDy * y) / (x * x + y * y);
ballDx = ballDx + c * x;
ballDy = ballDy + c * y;
}
Algunas consideraciones de implementación: si bien puede aproximarse (x, y) con la posición de la pelota después del paso de simulación, esta aproximación cambiará el ángulo de desviación y, por lo tanto, será muy notable, por lo que sus pasos de simulación deben ser muy finos (tal vez la pelota no se mueve más de 1/20 de su diámetro por paso). Para una solución más precisa, puede calcular el momento en que ocurre la colisión y dividir ese paso de simulación en ese momento, es decir, hacer un paso parcial hasta el punto de colisión, y otro paso parcial para el resto del paso.
Edición 2: Calcular el punto de impacto
Sea r el radio, (x0, y0) la posición y (dx, dy) la velocidad de la pelota al comienzo del paso de simulación. Para simplificar, supongamos que la esquina en cuestión se encuentra en (0,0).
Sabemos:
(x,y) = (x0, y0) + (dx, dy) * t
Queremos
length(x,y) = r
Es decir
(x0 + dx * t) ^ 2 + (y0 + dy * t) ^ 2 = r^2
x0^2 + 2 * x0 * dx * t + dx^2 * t^2 + y0^2 + 2 * y0 * dy * t + dy^2 * t^2 = r ^ 2
(dx^2 + dy^2) * t^2 + (2 * x0 * dx + 2 * y0 * dy) * t + (x0^2 + y0^2 - r^2) = 0
\____ _____/ \____________ ___________/ \_______ ________/
\/ \/ \/
a b c
Esa es una ecuación cuadrática en t. Si es discriminante
D = b^2 - 4 * a * c
es negativo, no tiene soluciones, es decir, la pelota nunca golpeará la esquina en su curso actual. De lo contrario, sus dos soluciones están dadas por
t1 = (-b - sqrt(D)) / (2 * a)
t2 = (-b + sqrt(D)) / (2 * a)
Estamos interesados en el momento en que comenzó la colisión, que es el momento anterior t1
.
Tu método se convertiría en:
// compute a,b,c and D as given above
if (D >= 0) {
t = (-b - sqrt(D)) / (2 * a);
if (0 < t && t <= ts) {
// collision during this timestep!
x = x + t * dx;
y = y + t * dy;
ts = ts - t;
// change dx and dy using the deflection formula
}
}
x = x + ts * dx;
y = y + ts * dy;