Примечание: Все нижеприведенное предполагает, что поверхность шара не имеет трения (поэтому он не начнет вращаться или отскакивать по-другому, потому что это так).
В момент столкновения мяч будет касаться угла. Когда твердые объекты сталкиваются, сила действует на так называемую нормаль поверхности, то есть перпендикулярно поверхности в точке столкновения.
Поскольку это шар, перпендикулярно поверхности к центру шара. Итак, мы знаем направление силы, как насчет ее величины? Предполагая упругое столкновение (и то, что прямоугольник не может двигаться), шар должен отскочить с той же скоростью, с которой он воздействовал.
Пусть (nDx, nDy) - скорость после столкновения, (oDx, oDy) - скорость до столкновения и (x, y) - положение шара в точке столкновения. Далее предположим, что угол, с которым сталкивается шар, находится в точке (0,0).
Выражая наши идеи в виде формул, мы имеем:
(nDx, nDy) = (oDx, oDy) + c * (x, y)
length (nDx, nDy) = length (oDx, oDy)
Что эквивалентно:
nDx = oDx + c * x
nDy = oDy + c * y
nDx^2 + nDy^2 = oDx^2 + oDy^2
Подставляя первые два уравнения в последнее, получим:
(oDx + c * x)^2 + (oDy + c * y)^2 = oDx^2 + oDy^2
Расширение с использованием биномиального тора
(a+b)^2 = a^2 + 2ab + b^2
выходы:
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
Это квадратное уравнение для c
имеет два решения, одно из которых равно 0. Очевидно, что это не то решение, которое нас интересует, так как обычно направление шара изменится в результате столкновения. Чтобы получить другое решение, мы делим обе стороны на c и получаем:
(2 * oDx * x + 2 * oDy * y) + (x^2 + y^2) * c = 0
Это:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
Подводя итог, мы имеем:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
nDx = oDx + c * x
nDy = oDy + c * y
Редактировать : В коде:
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;
}
Несколько соображений по реализации: Хотя вы можете приблизить (x, y) положение шара после шага симуляции, это приближение изменит угол отклонения и, следовательно, будет очень заметным, поэтому ваши шаги симуляции должны быть очень хорошими (возможно, такими, чтобы мяч не перемещается более чем на 1/20 своего диаметра за шаг). Для более точного решения вы можете вычислить время, когда происходит столкновение, и разделить этот шаг моделирования в это время, то есть сделать частичный шаг до точки столкновения и еще один частичный шаг для оставшейся части шага.
Редактировать 2: Вычисление точки удара
Пусть r - радиус, (x0, y0) положение и (dx, dy) скорость шара в начале шага моделирования. Для простоты давайте далее предположим, что рассматриваемый угол расположен в точке (0,0).
Мы знаем:
(x,y) = (x0, y0) + (dx, dy) * t
Мы хотим
length(x,y) = r
Это
(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
Это квадратное уравнение в т. Если его дискриминант
D = b^2 - 4 * a * c
отрицательно, у него нет решений, то есть мяч никогда не достигнет угла на своем текущем курсе. В противном случае два его решения определяются
t1 = (-b - sqrt(D)) / (2 * a)
t2 = (-b + sqrt(D)) / (2 * a)
Нас интересует время начала столкновения, которое является более ранним временем t1
.
Ваш метод станет:
// 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;