Chcesz oddzielić aktualizacje (tiki logiczne) i rysować (renderować tiki).
Twoje aktualizacje określą pozycję wszystkich obiektów na świecie do narysowania.
Omówię tutaj dwie różne możliwości: tę, o którą prosiłeś, ekstrapolację, a także inną metodę interpolacji.
1.
Ekstrapolacja polega na obliczeniu (przewidywanej) pozycji obiektu w następnej klatce, a następnie interpolacji między bieżącą pozycją obiektów a pozycją, w której obiekt będzie znajdował się w następnej klatce.
Aby to zrobić, każdy obiekt do narysowania musi być powiązany velocity
i position
. Aby znaleźć pozycję, w której obiekt będzie w następnej klatce, po prostu dodajemy velocity * draw_timestep
do bieżącej pozycji obiektu, aby znaleźć przewidywaną pozycję następnej klatki. draw_timestep
to czas, który upłynął od poprzedniego tyknięcia renderowania (czyli poprzedniego wywołania losowania).
Jeśli to zostawisz, zauważysz, że obiekty „migoczą”, gdy ich przewidywana pozycja nie odpowiada rzeczywistej pozycji w następnej klatce. Aby usunąć migotanie, możesz zapisać przewidywaną pozycję i długość między poprzednio przewidywaną pozycją a nową przewidywaną pozycją na każdym etapie losowania, wykorzystując czas, jaki upłynął od poprzedniej aktualizacji, jako czynnik lerp. Nadal będzie to powodować złe zachowanie, gdy szybko poruszające się obiekty nagle zmienią lokalizację, a ty możesz poradzić sobie z tym szczególnym przypadkiem. Wszystko, co zostało powiedziane w tym akapicie, jest powodem, dla którego nie chcesz korzystać z ekstrapolacji.
2)
Interpolacja polega na przechowywaniu stanu dwóch ostatnich aktualizacji i interpolacji między nimi w oparciu o bieżący czas, jaki upłynął od ostatniej aktualizacji. W tym ustawieniu każdy obiekt musi być powiązany position
i previous_position
. W takim przypadku nasz rysunek będzie w najgorszym przypadku reprezentował jeden tik aktualizacji za bieżącym stanem gry, aw najlepszym razie dokładnie w tym samym stanie, co bieżący tik aktualizacji.
Moim zdaniem, prawdopodobnie chcesz interpolacji, tak jak to opisałem, ponieważ jest to łatwiejsze do wdrożenia, a rysowanie ułamka sekundy (np. 1/60 sekundy) za twoim aktualnym stanem jest w porządku.
Edytować:
W przypadku, gdy powyższe nie wystarcza, aby wykonać implementację, oto przykład, jak wykonać opisaną przeze mnie metodę interpolacji. Nie będę omawiał ekstrapolacji, ponieważ nie mogę wymyślić żadnego scenariusza z prawdziwego świata, w którym powinieneś go preferować.
Gdy utworzysz obiekt do rysowania, będzie on przechowywał właściwości potrzebne do narysowania (tj. Informacje o stanie potrzebne do narysowania go).
W tym przykładzie przechowamy pozycję i obrót. Możesz także zapisać inne właściwości, takie jak położenie współrzędnych koloru lub tekstury (np. Gdy tekstura przewija się).
Aby zapobiec modyfikowaniu danych podczas rysowania wątku renderowania (tj. Położenie jednego obiektu jest zmieniane podczas rysowania wątku renderowania, ale wszystkie inne nie zostały jeszcze zaktualizowane), musimy wprowadzić pewien rodzaj podwójnego buforowania.
Obiekt przechowuje dwie jego kopie previous_state
. Umieszczę je w tablicy i będę odnosił się do nich jako previous_state[0]
i previous_state[1]
. Podobnie potrzebuje dwóch kopii current_state
.
Aby śledzić, która kopia podwójnego bufora jest używana, przechowujemy zmienną state_index
, która jest dostępna zarówno dla wątku aktualizacyjnego, jak i rysującego.
Wątek aktualizacji najpierw oblicza wszystkie właściwości obiektu na podstawie własnych danych (dowolnych struktur danych, które chcesz). Następnie kopiuje current_state[state_index]
do previous_state[state_index]
i kopiuje nowe dane istotne do rysowania, position
a rotation
do current_state[state_index]
. Następnie robi to state_index = 1 - state_index
, aby przerzucić aktualnie używaną kopię podwójnego bufora.
Wszystko w powyższym akapicie należy wykonać przy zdjętej blokadzie current_state
. Wątki aktualizacji i rysowania usuwają tę blokadę. Blokada jest wyjmowana tylko na czas kopiowania informacji o stanie, co jest szybkie.
W nici do renderowania interpolujesz liniowo pozycję i obrót w następujący sposób:
current_position = Lerp(previous_state[state_index].position, current_state[state_index].position, elapsed/update_tick_length)
Gdzie elapsed
jest czas, który upłynął w wątku renderowania od ostatniego tiku aktualizacji, i update_tick_length
czas, jaki zajmuje ustalona częstotliwość aktualizacji na tik (np. Przy aktualizacjach 20 FPS update_tick_length = 0.05
).
Jeśli nie wiesz, czym Lerp
jest powyższa funkcja, zapoznaj się z artykułem Wikipedii na ten temat: Interpolacja liniowa . Jeśli jednak nie wiesz, co to jest Lerping, prawdopodobnie nie jesteś gotowy do wdrożenia oddzielonej aktualizacji / rysunku z rysunkiem interpolowanym.