Примітка. Усі наведені нижче припущення передбачають, що поверхня м'яча не тертя (тому він не почне крутитися чи відскокувати інакше, тому що є).
У момент зіткнення м'яч буде торкатися кута. Коли тверді предмети стикаються, сила буде діяти уздовж так званої поверхні в нормі, тобто перпендикулярно поверхні в точці зіткнення.
Оскільки це кулька, перпендикулярна поверхні спрямована до центру кулі. Гаразд, ми знаємо напрямок сили, а як бути з її величиною? Припускаючи пружне зіткнення (і щоб прямокутник не міг рухатися), м'яч повинен відскочити з тією ж швидкістю, з якою він вплинув.
Нехай (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
Це квадратичне рівняння в t. Якщо його дискримінант
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;