Nota: Todos os itens a seguir assumem que a superfície da bola não possui atrito (portanto, ela não começará a girar ou se recuperar de forma diferente porque é).
No momento da colisão, a bola estará tocando o canto. Quando objetos sólidos colidem, uma força age ao longo da superfície normal, ou seja, perpendicular à superfície no ponto de colisão.
Como é uma bola, perpendicular à superfície está em direção ao centro da bola. Ok, então sabemos a direção da força, e quanto à sua magnitude? Assumindo uma colisão elástica (e que o retângulo não pode se mover), a bola deve se recuperar na mesma velocidade em que impactou.
Seja (nDx, nDy) a velocidade após a colisão, (oDx, oDy) a velocidade antes da colisão e (x, y) a posição da bola no ponto de colisão. Vamos assumir ainda que o canto com o qual a bola colide está em (0,0).
Expressando nossas idéias como fórmulas, temos:
(nDx, nDy) = (oDx, oDy) + c * (x, y)
length (nDx, nDy) = length (oDx, oDy)
O que equivale a:
nDx = oDx + c * x
nDy = oDy + c * y
nDx^2 + nDy^2 = oDx^2 + oDy^2
Substituindo as duas primeiras equações na última, obtemos:
(oDx + c * x)^2 + (oDy + c * y)^2 = oDx^2 + oDy^2
Expansão usando o torem binomial
(a+b)^2 = a^2 + 2ab + b^2
rendimentos:
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
Essa equação quadrática para c
tem duas soluções, uma das quais é 0. Obviamente, não é essa a solução em que estamos interessados, pois geralmente a direção da bola muda como resultado da colisão. Para obter a outra solução, dividimos os dois lados por ce obtemos:
(2 * oDx * x + 2 * oDy * y) + (x^2 + y^2) * c = 0
Isso é:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
Para resumir, temos:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
nDx = oDx + c * x
nDy = oDy + c * y
Editar : No 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;
}
Algumas considerações de implementação: Embora você possa se aproximar (x, y) da posição da bola após a etapa de simulação, essa aproximação alterará o ângulo de deflexão e, portanto, será muito perceptível, portanto, suas etapas de simulação precisam ser muito boas (talvez tal a bola não move mais de 1/20 do seu diâmetro por etapa). Para uma solução mais precisa, você pode calcular o tempo em que a colisão ocorre e dividir a etapa de simulação naquele momento, ou seja, executar uma etapa parcial até o ponto de colisão e outra etapa parcial para o restante da etapa.
Edit 2: Computando o ponto de impacto
Seja r o raio, (x0, y0) a posição e (dx, dy) a velocidade da bola no início do passo da simulação. Para simplificar, vamos supor que o canto em questão esteja localizado em (0,0).
Nós sabemos:
(x,y) = (x0, y0) + (dx, dy) * t
Nós queremos
length(x,y) = r
Isso é
(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
Essa é uma equação quadrática em t. Se seu discriminante
D = b^2 - 4 * a * c
é negativo, não tem soluções, ou seja, a bola nunca acertará o canto no seu percurso atual. Caso contrário, suas duas soluções são dadas por
t1 = (-b - sqrt(D)) / (2 * a)
t2 = (-b + sqrt(D)) / (2 * a)
Estamos interessados no momento em que a colisão começou, que é a anterior t1
.
Seu método se tornaria:
// 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;