Nie jest trudno stworzyć dość dobry ruch samochodowy (ale ten post będzie dość długi). Musisz „zasymulować” kilka podstawowych sił, aby samochód poruszał się fizycznie.
(Wszystkie próbki kodu są pseudokodami).
Przyśpieszenie
Po pierwsze, oczywiście potrzebujesz przyspieszenia. Zrobiłoby to coś tak prostego, jak następujący wiersz:
acceleration_vector = forward_vector * acceleration_input * acceleration_factor
forward_vector
- Wektor wskazujący w tym samym kierunku co samochód.
acceleration_input
- Dane wejściowe powinny znajdować się w przedziale [-1, 1].
acceleration_factor
- Wartość przyspieszenia (w pikselach na sekundę ^ 2 lub dowolnych jednostkach).
Sterowniczy
Sterowanie jest również dość proste. Zasadniczo wystarczy obrócić wektor do przodu samochodu, aby skierować go w innym kierunku.
steer_angle = steer_input * steer_factor
new_forward_vector = rotate_around_axis(forward_vector, up_vector, steer_angle)
Możesz jednak napotkać tutaj komplikację. Jeśli wprowadzasz dane za pomocą klawiatury, wartość będzie wynosić -1 lub 1, co oznacza, że samochód skręci natychmiast. Możesz to naprawić za pomocą bardzo prostej interpolacji liniowej (lerping):
amount = time_since_last_frame * steer_lerp_factor
forward_vector = lerp(forward_vector, new_forward_vector, amount)
Kwota powinna zależeć od czasu, tak aby ruch nie zależał od liczby klatek na sekundę. Ilość powinna wynosić między [0, 1] a im jest mniejsza, tym płynniejsze będzie przejście między starymi i nowymi wektorami.
(W tym momencie przekonasz się, że samochód będzie kierował, nawet jeśli stoi w miejscu. Aby temu zapobiec, pomnóż steer_angle
przez to current_speed / max_speed
, gdzie max_speed
zdefiniowana jest stała.)
W ruchu
Teraz zastosujemy przyspieszenie i przesuniemy samochód o określoną liczbę pikseli na podstawie jego prędkości, przyspieszenia i układu kierowniczego. Będziemy także chcieli ograniczyć prędkość samochodu, aby nie poruszał się nieskończenie szybko.
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Twój samochód się teraz ślizga
Jeśli mam rację, twój samochód powinien się teraz ślizgać za każdym razem, gdy skręcasz, jakby był na lodzie. Wynika to z braku tarcia. W prawdziwym samochodzie występuje duże tarcie boczne (z powodu niemożności obracania się kół na boki: P).
Musisz zmniejszyć prędkość boczną. Nie zmniejszając go całkowicie, możesz sprawić, że samochód będzie wyglądał, jakby dryfował.
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
Ponieważ mówimy o tarciu, możesz również chcieć mieć siłę (tarcia), która zmniejsza twoją prędkość, tak że gdy przestaniesz przyspieszać, twój samochód w końcu się zatrzyma.
backwards_friction = -velocity_vector * backwards_friction_factor
Twój kod do przemieszczania samochodu powinien teraz wyglądać następująco:
// Friction should be calculated before you apply the acceleration
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
backwards_friction = -velocity_vector * backwards_friction_factor
velocity_vector += (backwards_friction + lateral_friction) * time_since_last_frame
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Notatki końcowe
Wspomniałem, w jaki sposób powinieneś zastosować Lerping do sterowania; Myślę, że możesz potrzebować zrobić to samo dla przyspieszenia i ewentualnie także dla kąta skrętu (będziesz musiał zapisać ich wartości z poprzedniej klatki i lerp z tego). Również wszystkie wektory w stosunku do samochodu (do przodu, w prawo, w górę) powinny mieć długość 1.
Ponadto tarcie jest nieco bardziej skomplikowane niż tutaj pokazałem. Zawsze należy upewnić się, że jego długość nigdy nie jest większa niż przyspieszenie potrzebne do zatrzymania samochodu (w przeciwnym razie tarcie spowodowałoby, że samochód poruszałby się w przeciwnym kierunku). Więc powinieneś mieć coś takiego:
dt = time_since_last_frame
backwards_friction.resize(min(backwards_friction.norm(), velocity_vector.norm() / dt))
lateral_friction.resize(min(lateral_friction.norm(), lateral_velocity.norm() / dt))