Uwaga: Wszystkie poniższe założenia zakładają, że powierzchnia piłki jest wolna od tarcia (więc nie zacznie się obracać ani odbijać inaczej, ponieważ jest).
W momencie zderzenia piłka dotknie rogu. Kiedy zderzą się obiekty stałe, siła będzie działać wzdłuż tak zwanej powierzchni normalnej, tj. Prostopadłej do powierzchni w punkcie zderzenia.
Ponieważ jest to piłka, prostopadła do powierzchni jest skierowana do środka piłki. Ok, więc znamy kierunek siły, a co z jej wielkością? Zakładając, że zderzenie sprężyste (i prostokąt nie może się poruszać), piłka musi odbić się z taką samą prędkością, z jaką uderzyła.
Niech (nDx, nDy) będzie prędkością po zderzeniu, (oDx, oDy) prędkością przed zderzeniem oraz (x, y) pozycją kuli w punkcie zderzenia. Załóżmy dalej, że róg, z którym zderza się piłka, wynosi (0,0).
Wyrażając nasze spostrzeżenia jako formuły, mamy:
(nDx, nDy) = (oDx, oDy) + c * (x, y)
length (nDx, nDy) = length (oDx, oDy)
Co odpowiada:
nDx = oDx + c * x
nDy = oDy + c * y
nDx^2 + nDy^2 = oDx^2 + oDy^2
Podstawiając dwa pierwsze równania w ostatnim, otrzymujemy:
(oDx + c * x)^2 + (oDy + c * y)^2 = oDx^2 + oDy^2
Rozwijanie za pomocą twierdzenia dwumianowego
(a+b)^2 = a^2 + 2ab + b^2
daje:
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
To równanie kwadratowe c
ma dwa rozwiązania, z których jedno wynosi 0. Oczywiście nie jest to rozwiązanie, które nas interesuje, ponieważ ogólnie kierunek piłki zmieni się w wyniku zderzenia. Aby uzyskać inne rozwiązanie, dzielimy obie strony przez c i otrzymujemy:
(2 * oDx * x + 2 * oDy * y) + (x^2 + y^2) * c = 0
To jest:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
Podsumowując, mamy:
c = -(2 * oDx * x + 2 * oDy * y) / (x^2 + y^2)
nDx = oDx + c * x
nDy = oDy + c * y
Edycja : W kodzie:
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;
}
Kilka uwag dotyczących implementacji: Chociaż można przybliżać (x, y) pozycją kuli po etapie symulacji, to przybliżenie zmieni kąt odchylenia, a zatem będzie bardzo zauważalne, więc kroki symulacji muszą być bardzo dokładne (być może takie, aby piłka nie porusza się więcej niż o 1/20 swojej średnicy na krok). Aby uzyskać dokładniejsze rozwiązanie, możesz obliczyć czas wystąpienia kolizji i podzielić ten krok symulacji w tym czasie, tj. Wykonać krok częściowy do momentu zderzenia i kolejny etap częściowy dla pozostałej części kroku.
Edycja 2: Obliczanie punktu uderzenia
Niech r będzie promieniem, (x0, y0) pozycją i (dx, dy) prędkością kuli na początku etapu symulacji. Dla uproszczenia załóżmy, że dany narożnik znajduje się w (0,0).
Wiemy:
(x,y) = (x0, y0) + (dx, dy) * t
Chcemy
length(x,y) = r
To jest
(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
To równanie kwadratowe wt. Jeśli jest dyskryminujący
D = b^2 - 4 * a * c
jest negatywny, nie ma rozwiązań, tzn. piłka nigdy nie uderzy w róg na swoim obecnym kursie. W przeciwnym razie jego dwa rozwiązania są podane przez
t1 = (-b - sqrt(D)) / (2 * a)
t2 = (-b + sqrt(D)) / (2 * a)
Interesuje nas czas rozpoczęcia kolizji, czyli wcześniejszy t1
.
Twoja metoda stałaby się:
// 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;