Jak zaimplementować siłę tarcia w grze
Jakiś czas temu oglądałem wideo na YouTube, w którym indie game deweloper @randy pokazywał jak zaimplementował fizykę w swojej grze. Moją uwagę zwróciła niepoprawnie (niezgodnie z zasadami fizyki) zaimplementowana siła tarcia. W tym wpisie odpowiem dlaczego.
Najpierw zobaczmy jak zaimplementowana została podstawowa kinematyka ruchu przez randiego. Poniżej przedstawiam pseudo kod:
new_velocity = old_velocity + acceleration * delta_time // prędkość v(t)
new_position = old_position + new_velocity * delta_time // przmieszczenie d(t)
Wszystko tutaj wygląda dobrze. Zmiana przemieszczenia w czasie, to jest fizyczna interpretacja prędkości, natomiast zmiana prędkości w czasie, to jest przyspieszenie.
A teraz popatrzmy jak @randy zaimplementował siłę tarcia:
new_acceleration = old_acceleration - velocity * friction * delta_time
Choć powyższy kod generuje efekt spowalniania ciała, które jest w ruchu, to jest to implementacja niezgodna z tym jak tarcie działa w rzeczywistości. Dodam tylko, że my tutaj mówimy o tarciu dynamicznym, które działa na ciało poruszające się z prędkością v. Siła taka nie zależy od prędkości, a jedynie od nacisku ciała na podłoże (masa + siła grawitacji). Powyższy wzór zakłada, że im szybciej porusza się ciało sunące po podłożu, tym bardziej tarcie je spowalnia.
Tarcie jako „siła” powinno wpływać na (zmieniać) prędkość ciała. A ponieważ zarówno masa jak i siła grawitacji są stałe, to tarcie powinno być stałe przez cały okres trwania ruchu.
W każdym przedziale czasu o długości delta_time
bierzemy wartość prędkości (która jest średnią prędkością w tym przedziale czasu, bo w grach czas nie jest ciągły jak w rzeczywistości) i odejmujemy od niej wartość siły tarcia. Tarcie jest siłą, a więc zmianą prędkości w czasie, więc również mnożymy ją przez delta_time
.
new_velocity = old_velocity + acceleration * delta_time - friction * delta_time
= old_velocity + (acceleration - friction) * delta_time
new_position = old_position + new_velocity * delta_time
Wychodzi na to, że tarcie jest zwykłą stałą, która zmniejsza przyspieszenie.
Ponadto zauważmy, jak w rezultacie, do wyliczenia new_position
mnożymy tarcie friction
przez delta_time
do kwadratu. Ponieważ tarcie reprezentuje zmianę prędkości w czasie, a prędkość reprezentuje zmianę przemieszczenia w czasie, to oznacza, że tarcie jest w tej sytuacji drugą pochodną od przemieszczenia po czasie.
d"(t) = v'(t) = a(t) <- tarcie w chwili t
Oczywiście w grach nie mamy prawdziwej „chwili” t
o długości dążącej do zera, więc takie idealne wzory stanowią jedynie model do aproksymowania praw fizyki w przedziałach czasu między jedną wyświetloną klatką gry a drugą.
Całkując powyższy wzór po czasie (dt) i zakładając, że tarcie a(t)
jest stałe i równe a
(co wcześniej ustaliliśmy, że jest prawdą), otrzymujemy:
v(t) = v0 + a * t
d(t) = d0 + v0 * t + (a * t * t) / 2
Zatem wyszły klasyczne wzory na prędkość i przemieszczenie w ruchu jednostajnie przyspieszonym. Zero zaskoczenia, bo tarcie to po prostu siła, a więc przyspieszenie (spowolnienie) w kierunku przeciwnym do kierunku ruchu.
Na koniec dodam, że istnieje znana siła hamująca proporcjonalna do prędkości i jest to opór powietrza. Więc w rzeczywistości do wyliczenia spowolnienia i tak należy tę prędkość uwzględnić. Może się zatem okazać, że tarcie zaimplementowane przez @randiego wcale nie jest aż taką złą aproksymacją jak się na pierwszy rzut oka wydaje :)