Obecnie zaczynam uczyć się OpenGL w szkole, a pewnego dnia zacząłem tworzyć prostą grę (na własną rękę, nie dla szkoły). Używam freeglut i buduję go w C, więc w mojej pętli gry naprawdę korzystałem właśnie z funkcji, którą przekazałem, glutIdleFunc
aby zaktualizować cały rysunek i fizykę za jednym przejściem. To było w porządku dla prostych animacji, które nie przejmowały się zbytnio liczbą klatek na sekundę, ale ponieważ gra jest oparta głównie na fizyce, naprawdę chcę (muszę) powiązać, jak szybko się aktualizuje.
Tak więc moją pierwszą próbą było przekazanie mojej funkcji do glutIdleFunc
( myIdle()
), aby śledzić, ile czasu minęło od poprzedniego wywołania, i aktualizować fizykę (i obecnie grafikę) co tyle milisekund. Zwykłem timeGetTime()
to robić (za pomocą <windows.h>
). I to sprawiło, że pomyślałem, czy korzystanie z funkcji bezczynności jest naprawdę dobrym sposobem na obejście pętli gry?
Moje pytanie brzmi: jaki jest lepszy sposób na implementację pętli gry w OpenGL? Czy powinienem unikać korzystania z funkcji bezczynności?
Odpowiedzi:
Prosta odpowiedź brzmi: nie, nie chcesz używać wywołania zwrotnego glutIdleFunc w grze, która ma jakąś symulację. Powodem tego jest to, że ta funkcja rozdziela animację i rysuje kod z obsługi zdarzeń okna, ale nie asynchronicznie. Innymi słowy, odbieranie i przekazywanie zdarzeń na okno przeciąga kod (lub cokolwiek, co umieścisz w tym wywołaniu zwrotnym), jest to doskonale w przypadku aplikacji interaktywnej (interakcja, a następnie odpowiedź), ale nie w przypadku gry, w której stan fizyki lub gry musi się rozwijać niezależne od interakcji lub czasu renderowania.
Chcesz całkowicie oddzielić obsługę danych wejściowych, stan gry i narysować kod. Istnieje proste i czyste rozwiązanie tego problemu, które nie wymaga bezpośredniej biblioteki graficznej (tzn. Jest przenośne i łatwe do wizualizacji); chcesz, aby cała pętla gry produkowała czas, a symulacja pochłaniała wyprodukowany czas (w kawałkach). Kluczem jest jednak zintegrowanie ilości czasu zużytego przez symulację z animacją.
Najlepsze wyjaśnienie i samouczek, które znalazłem na ten temat, to Fix Your Timestep Glenna Fiedlera
Ten samouczek ma pełne podejście, jednak jeśli nie masz rzeczywistej symulacji fizyki, możesz pominąć prawdziwą integrację, ale podstawowa pętla nadal sprowadza się do (w pełnym pseudo-kodzie):
W ten sposób zatrzymanie kodu renderowania, obsługa danych wejściowych lub system operacyjny nie powodują pogorszenia stanu gry. Ta metoda jest również przenośna i niezależna od biblioteki grafiki.
GLUT jest dobrą biblioteką, ale jest ściśle sterowana zdarzeniami. Rejestrujesz połączenia zwrotne i odpalasz główną pętlę. Zawsze przekazujesz kontrolę nad swoją główną pętlą za pomocą GLUT. Można go obejść za pomocą hacków , można także sfałszować zewnętrzną pętlę za pomocą timerów i tym podobne, ale inna biblioteka jest prawdopodobnie lepszym (łatwiejszym) sposobem na przejście. Istnieje wiele alternatyw, oto kilka (z dobrą dokumentacją i szybkimi samouczkami):
źródło
Myślę, że
glutIdleFunc
jest w porządku; i tak właściwie to byś zrobił ręcznie, gdybyś go nie używał. Bez względu na to, co będziesz mieć ciasną pętlę, która nie jest wywoływana w ustalonym wzorze, musisz dokonać wyboru, czy chcesz ją przekształcić w pętlę o stałym czasie, czy zachować ją w pętli o zmiennym czasie i utworzyć upewnij się, że wszystkie twoje konta matematyczne interpolują czas. A nawet jakoś ich połączenie.Z pewnością możesz mieć
myIdle
funkcję, którą przekazujeszglutIdleFunc
, a w tym mierzyć upływający czas, dzielić go przez ustalony czas i wywoływać inną funkcję, która wiele razy.Oto czego nie robić:
fixedTimestep nie będzie wywoływany co 10 milisekund. W rzeczywistości działałby wolniej niż w czasie rzeczywistym, i możesz skończyć z fałszowaniem liczb, aby zrekompensować, gdy tak naprawdę przyczyną problemu jest utrata reszty po podzieleniu
timeDifference
przez 10.Zamiast tego zrób coś takiego:
Teraz ta struktura pętli zapewnia, że twoja
fixedTimestep
funkcja będzie wywoływana raz na 10 milisekund, bez utraty milisekund jako reszty lub czegoś podobnego.Ze względu na wielozadaniowość systemów operacyjnych nie można polegać na wywołaniu funkcji dokładnie co 10 milisekund; ale powyższa pętla nastąpiłaby na tyle szybko, że mam nadzieję, że byłaby wystarczająco blisko i można założyć, że każde wywołanie
fixedTimestep
zwiększy wartość symulacji o 10 milisekund.Przeczytaj również tę odpowiedź, a zwłaszcza linki podane w odpowiedzi.
źródło