Kiedy powinienem zastosować stały lub zmienny krok czasowy?


256

Czy pętla gry powinna opierać się na stałych lub zmiennych krokach czasowych? Czy ktoś jest zawsze lepszy, czy właściwy wybór zależy od gry?

Zmienny krok czasowy

Aktualizacje fizyki są przekazywane argumentem „czas, jaki upłynął od ostatniej aktualizacji”, a zatem zależą od liczby klatek na sekundę. Może to oznaczać wykonywanie obliczeń jako position += distancePerSecond * timeElapsed.

Plusy : płynne, łatwiejsze do kodowania
Wady : niedeterministyczne, nieprzewidywalne przy bardzo małych lub dużych krokach

przykład deWiTERów :

while( game_is_running ) {
    prev_frame_tick = curr_frame_tick;
    curr_frame_tick = GetTickCount();
    update( curr_frame_tick - prev_frame_tick );
    render();
}

Naprawiono krok czasowy

Aktualizacje mogą nawet nie zaakceptować „upływu czasu”, ponieważ zakładają, że każda aktualizacja trwa przez określony czas. Obliczenia można wykonać w następujący sposób position += distancePerUpdate. Przykład obejmuje interpolację podczas renderowania.

Plusy : przewidywalny, deterministyczny (łatwiejszy do synchronizacji z siecią?), Wyraźniejszy kod obliczeniowy
Wady : niezsynchronizowany z monitorem v-sync (powoduje roztrzęsioną grafikę, chyba że interpolujesz), ograniczona maksymalna częstotliwość klatek (chyba, że ​​interpolujesz), ciężko pracować w ramach, które zakładaj zmienne przedziały czasowe (jak Pyglet lub Flixel )

przykład deWiTERów :

while( game_is_running ) {
    while( GetTickCount() > next_game_tick ) {
        update();
        next_game_tick += SKIP_TICKS;
    }
    interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
                    / float( SKIP_TICKS );
    render( interpolation );
}

Niektóre zasoby


6
Używaj zmiennych timepów w swojej grze i ustalonych kroków dla fizyki
Daniel Little

7
Nie powiedziałbym, że krok zmiennej czasu jest łatwiejszy do zakodowania właśnie dlatego, że dzięki stałemu krokowi czasu „nie trzeba mylić wszystkich obliczeń ze zmienną TimeElapted wszędzie”. Nie dlatego, że to takie trudne, ale nie dodałbym „łatwiejszego do kodowania” jako profesjonalisty.
pek

To prawda, myślę, że miałem na myśli to, że nie trzeba interpolować kroków w zmiennej długości.
Nick Sonneveld,

@pek Zgadzam się z tobą. Zmienny krok czasowy ma prostszą zakodowaną pętlę gry, ale masz więcej do zakodowania w swoich bytach, które radzą sobie z tą zmiennością, aby ją „stymulować”. Naprawiony krok czasowy ma bardziej skomplikowaną pętlę gry kodowej (ponieważ musisz dokładnie zrekompensować wariancje przybliżenia czasu i ponownie obliczyć, jakie dodatkowe opóźnienie dodać lub ile aktualizacji pominąć, aby to naprawić), ale ma prostsze kodowanie dla podmiotów, które będą zawsze muszę radzić sobie z tym samym przedziałem czasowym. Ogólnie rzecz biorąc, żadne z tych podejść nie jest wyraźnie prostsze od drugiego.
Shivan Dragon,

Możesz sprawdzić te elementy wizualne za pomocą tego narzędzia: s3.amazonaws.com/picobots/assets/unity/jerky-motion/…, chociaż nie daje to wyobrażenia o tym, jak będą wyglądać, gdy zmienia się liczba klatek na sekundę
Buddy

Odpowiedzi:


134

Istnieją dwa problemy związane z pytaniem.

  • Czy szybkość kroku fizyki powinna być powiązana z liczbą klatek na sekundę?
  • Czy fizyka powinna być stopniowana ze stałymi deltami?

W Glen Field's Fix your time step mówi: „Uwolnij fizykę”. Oznacza to, że częstotliwość odświeżania fizyki nie powinna być powiązana z liczbą klatek na sekundę.

Na przykład, jeśli szybkość wyświetlania klatek wynosi 50 klatek na sekundę, a symulacja ma działać z prędkością 100 klatek na sekundę, musimy wykonać dwa kroki fizyki przy każdej aktualizacji wyświetlacza, aby fizyka była zsynchronizowana.

W zaleceniach Erin Catto dla Box2D również to popiera.

Więc nie wiąż kroku czasowego z liczbą klatek na sekundę (chyba że naprawdę, naprawdę musisz).

Czy częstotliwość kroków fizyki powinna być powiązana z liczbą klatek na sekundę? Nie.


Przemyślenia Erin na temat stałego i zmiennego kroku:

Box2D wykorzystuje algorytm obliczeniowy zwany integratorem. Integratory symulują równania fizyki w dyskretnych punktach czasu. ... Nie podoba nam się też czas, aby wiele zmienić. Zmienny krok czasowy daje zmienne wyniki, co utrudnia debugowanie.

Myśli Glena na temat stopniowego i zmiennego stopniowania:

Napraw swój czas lub wybuch

... Jeśli masz szereg naprawdę sztywnych ograniczeń sprężyn dla amortyzatorów w symulacji samochodowej, wówczas niewielkie zmiany w dt mogą spowodować, że symulacja wybuchnie. ...

Czy fizyka powinna być stopniowana ze stałymi deltami? Tak.


Sposobem na stopniowanie fizyki ze stałymi deltami i niepowiązanie szybkości aktualizacji fizyki z liczbą klatek wciąż jest użycie akumulatora czasu. W mojej grze posuwam się o krok dalej. Stosuję funkcję wygładzania do czasu przychodzącego. W ten sposób duże skoki FPS nie powodują, że fizyka przeskakuje za daleko, zamiast tego są one symulowane szybciej dla jednej lub dwóch klatek.

Wspominasz, że przy stałej szybkości fizyka nie zsynchronizuje się z wyświetlaczem. Dzieje się tak, jeśli docelowa częstotliwość fizyki jest zbliżona do docelowej częstotliwości klatek. Gorzej, liczba klatek na sekundę jest większa niż częstotliwość fizyki. Zasadniczo lepiej jest celować w aktualizację fizyki dwa razy większą niż docelowy FPS, jeśli możesz sobie na to pozwolić.

Jeśli nie możesz sobie pozwolić na dużą częstotliwość aktualizacji fizyki, zastanów się nad interpolacją pozycji grafiki między ramkami, aby narysowana grafika poruszała się płynniej niż fizyka faktycznie się porusza.


1
Grałem w The Floor is Jelly przed i po aktualizacji mojej maszyny i było to głupie: to nie było to samo, ponieważ fizyka była przywoływana z pętli gry (i tak związana z liczbą klatek na sekundę), a nie z minutnik. Moja stara maszyna była bardzo zła, więc ciągle przełączała się między zwolnionym a zbyt szybkim ruchem i miała wielki wpływ na rozgrywkę. Teraz jest w bardzo szybkim tempie. W każdym razie ta gra jest dobrym przykładem na to, jak problematyczny może być ten problem (choć wciąż jest urocza).
MasterMastic

55

Myślę, że są naprawdę 3 opcje, ale wymieniasz je tylko 2:

opcja 1

Nic nie robić. Próba aktualizacji i renderowania w określonych odstępach czasu, np. 60 razy na sekundę. Jeśli pozostanie w tyle, pozwól temu i nie martw się. Gry zwolnią, gdy procesor nie nadąży za twoją grą. Ta opcja w ogóle nie działa w przypadku gier dla wielu użytkowników w czasie rzeczywistym, ale jest dobra w przypadku gier dla jednego gracza i została z powodzeniem stosowana w wielu grach.

Opcja 2

Użyj czasu delta między każdą aktualizacją, aby zmienić ruch obiektów. Świetny w teorii, szczególnie jeśli nic w twojej grze nie przyspiesza ani nie zwalnia, a jedynie porusza się ze stałą prędkością. W praktyce wielu programistów źle to wdraża, co może prowadzić do niespójnego wykrywania kolizji i fizyki. Wygląda na to, że niektórzy programiści uważają, że ta metoda jest łatwiejsza niż jest. Jeśli chcesz skorzystać z tej opcji, musisz znacznie przyspieszyć grę i wydobyć matematykę i algorytmy wielkiej broni, na przykład używając integratora fizyki Verleta (zamiast standardowego Eulera, którego używa większość ludzi) i używając promieni do wykrywania kolizji zamiast zwykłych kontroli odległości w Pitagorasie. Jakiś czas temu zadałem pytanie na temat przepełnienia stosu i otrzymałem świetne odpowiedzi:

https://stackoverflow.com/questions/153507/calculate-the-position-of-an-accelerating-body-after-a-certain-time

Opcja 3

Skorzystaj z metody „napraw swój krok” Gaffera. Aktualizuj grę w ustalonych krokach, jak w opcji 1, ale rób to wiele razy na renderowaną klatkę - na podstawie upływu czasu - aby logika gry nadążała za czasem, pozostając w dyskretnych krokach. W ten sposób łatwa do zaimplementowania logika gry, taka jak integratory Eulera i proste wykrywanie kolizji, nadal działa. Masz również opcję interpolacji animacji graficznych w oparciu o czas delta, ale dotyczy to tylko efektów wizualnych i niczego, co nie wpływa na podstawową logikę gry. Możesz potencjalnie wpaść w kłopoty, jeśli twoje aktualizacje są bardzo intensywne - jeśli aktualizacje pozostaną w tyle, będziesz potrzebować ich coraz więcej, aby nadążyć, co może sprawić, że Twoja gra będzie jeszcze mniej responsywna.

Osobiście podoba mi się opcja 1, gdy mogę sobie z tym poradzić, oraz opcja 3, gdy muszę zsynchronizować z czasem rzeczywistym. Szanuję, że Opcja 2 może być dobrym rozwiązaniem, jeśli wiesz, co robisz, ale znam moje ograniczenia na tyle dobrze, aby trzymać się od niej z daleka.


co do opcji 2: Nie jestem pewien, czy raycast może być kiedykolwiek szybszy niż sprawdzanie odległości Pitagorasa, z wyjątkiem przypadków, gdy jesteś bardzo brutalny w stosowaniu pitagoras, ale raycast będzie również bardzo kosztowny, jeśli nie dodasz szerokofazowego.
Kaj

4
Jeśli używasz Verleta w nierównych odstępach czasu, wyrzucasz dziecko z kąpielą. Powodem, dla którego Verlet jest tak stabilny, jest to, że błędy anulują się w kolejnych krokach czasowych. Jeśli kroki czasowe nie są równe, tak się nie dzieje, a ty wracasz do eksplozji krainy fizyki.
drxzcl,

Opcja 3 - brzmi jak opis podejścia XNA autorstwa Joela Martineza.
topright

1
To jest naprawdę dobra odpowiedź. Wszystkie trzy opcje mają swoje miejsce i ważne jest, aby zrozumieć, kiedy są odpowiednie.
Adam Naylor,

We wszystkich MMO, nad którymi pracowałem (EQ, Landmark / EQNext [cry], PS2 [krótko] i ESO), zawsze stosowaliśmy zmienne ramy czasowe. Nigdy nie byłem stroną tej konkretnej decyzji, po prostu pojawiłem się po fakcie i skorzystałem z tego, co zdecydowali inni.
Mark Storer

25

Bardzo podoba mi się sposób, w jaki XNA Framework implementuje stały krok czasu. Jeśli dane losowanie trwa trochę za długo, będzie wielokrotnie wywoływać aktualizację, aż „dogoni”. Shawn Hargreaves opisuje to tutaj:
http://blogs.msdn.com/b/shawnhar/archive/2007/11/23/game-timing-in-xna-game-studio-2-0.aspx

W 2.0 zachowanie Draw się zmieniło:

  • Zadzwoń Aktualizuj tyle razy, ile potrzeba, aby dogonić bieżący czas
  • Zadzwoń do Draw jeden raz
  • Poczekaj, aż nadejdzie czas na kolejną aktualizację

Moim zdaniem największy profesjonalista to ten, o którym wspomniałeś, że sprawia, że ​​wszystkie obliczenia kodu gry są o wiele prostsze, ponieważ nie musisz uwzględniać tej zmiennej czasowej w dowolnym miejscu.

Uwaga: xna obsługuje również zmienny timepep, to tylko ustawienie.


Jest to najczęstszy sposób wykonywania pętli gry. Jednak nie działa świetnie na żywotność baterii podczas pracy z urządzeniami mobilnymi.
knight666,

1
@ knight666; czy sugerujesz, że stosując dłuższy czas, zmniejszona liczba iteracji uratuje życie baterii?
falstro

To wciąż zmienna aktualizacja - delta aktualizacji zmienia się w zależności od tego, ile czasu zajęło renderowanie ramki, niż pewnej stałej wartości (tj. 1/30 sekundy).
Dennis Munsie

1
@Dennis: jak rozumiem, funkcja aktualizacji jest wywoływana ze stałą
różnicą

3
@ knight666 Uh - jak to wymyślić? Jeśli masz włączoną vsync i nie jąkasz się - te metody powinny być identyczne! A jeśli masz Vsync off jesteś aktualizowanie bardziej często niż trzeba i prawdopodobnie marnowania CPU (a więc baterii) przez nie pozwalając jej na biegu jałowym!
Andrew Russell

12

Jest jeszcze jedna opcja - oddzielenie aktualizacji gry i aktualizacji fizyki. Próba dostosowania silnika fizyki do czasu gry prowadzi do problemu, jeśli naprawisz swój czas (problem wymknięcia się spod kontroli, ponieważ integracja potrzebuje więcej kroków, które wymagają więcej czasu, co wymaga więcej kroków) lub zmienisz go i uzyskasz dziwną fizykę.

Rozwiązanie, które bardzo często widzę, polega na tym, aby fizyka działała w określonym czasie, w innym wątku (na innym rdzeniu). Gra interpoluje lub ekstrapoluje, biorąc pod uwagę dwie ostatnie prawidłowe klatki, które może pobrać. Interpolacja dodaje pewne opóźnienie, ekstrapolacja dodaje pewnej niepewności, ale twoja fizyka będzie stabilna i nie wywróci kontroli nad czasem.

Nie jest to łatwe do wdrożenia, ale może okazać się sprawdzone w przyszłości.


8

Osobiście używam wariacji zmiennej krok czasowy (myślę, że jest to rodzaj hybrydy stałej i zmiennej). Ten system pomiaru czasu przetestowałem na kilka sposobów i używam go do wielu projektów. Czy polecam to do wszystkiego? Prawdopodobnie nie.

Moje pętle gry obliczają liczbę ramek do aktualizacji (nazwijmy to F), a następnie wykonują F dyskretne aktualizacje logiczne. Każda aktualizacja logiki zakłada stałą jednostkę czasu (która w moich grach często wynosi 1/100 sekundy). Każda aktualizacja jest wykonywana po kolei, aż zostaną wykonane wszystkie dyskretne aktualizacje logiki F.

Po co dyskretne aktualizacje w krokach logicznych? Cóż, jeśli spróbujesz zastosować ciągłe kroki i nagle będziesz mieć problemy z fizyką, ponieważ obliczone prędkości i odległości do podróży są mnożone przez ogromną wartość F.

Zła implementacja tego spowoduje po prostu F = aktualny czas - aktualizacje czasu ostatniej klatki. Ale jeśli obliczenia są zbyt daleko w tyle (czasami z powodu okoliczności, na które nie masz wpływu, takich jak inny proces kradnący czas procesora), szybko zobaczysz okropne pominięcie. Szybko ten stabilny FPS, który próbujesz utrzymać, staje się SPF.

W mojej grze pozwalam na „płynne” (spowolnienie) spowolnienie, aby ograniczyć ilość logicznego doładowania, które powinno być możliwe między dwoma losowaniami. Robię to poprzez zaciśnięcie: F = min (F, MAX_FRAME_DELTA), który zwykle ma MAX_FRAME_DELTA = 2/100 * s lub 3/100 * s. Dlatego zamiast pomijać klatki, gdy są zbyt daleko w tyle za logiką gry, odrzuć masową utratę klatek (co spowalnia rzeczy), odzyskaj kilka klatek, narysuj i spróbuj ponownie.

Robiąc to, upewniam się również, że elementy sterujące odtwarzacza są bliżej zsynchronizowane z tym, co faktycznie widać na ekranie.

Pseudokod produktu końcowego jest mniej więcej taki (wspomniano wcześniej o delcie F):

// Assume timers have 1/100 s resolution
const MAX_FRAME_DELTA = 2
// Calculate frame gap.
var delta = current time - last frame time
// Clamp.
delta = min(delta, MAX_FRAME_RATE)
// Update in discrete steps
for(i = 0; i < delta; i++)
{
    update single step()
}
// Caught up again, draw.
render()

Tego rodzaju aktualizacja nie jest odpowiednia do wszystkiego, ale w przypadku gier zręcznościowych wolałbym raczej spowolnić grę, ponieważ dzieje się wiele rzeczy, niż przegapianie ramek i utrata kontroli nad graczem. Wolę to również od innych podejść ze zmiennym czasem, które kończą się nieodwracalnymi usterkami spowodowanymi utratą ramki.


Zdecydowanie zgadzam się z tym ostatnim punktem; w prawie wszystkich grach, wejście powinno „spowolnić”, gdy spada ilość klatek na sekundę. Chociaż nie jest to możliwe w niektórych grach (np. Dla wielu graczy), byłoby lepiej, gdyby było to możliwe. : P To po prostu lepiej niż mieć długą ramkę, a potem „przeskoczyć” świat gry do „prawidłowego” stanu.
Ipsquiggle,

Bez ustalonego sprzętu, takiego jak automat zręcznościowy, posiadanie gier zręcznościowych spowalnia symulację, gdy sprzęt nie jest w stanie nadążyć, sprawia, że ​​gra na wolniejszym oszukiwaniu maszyny.

Joe to ma znaczenie tylko wtedy, gdy zależy nam na „oszukiwaniu”. Większość współczesnych gier nie polega na rywalizacji między graczami, a jedynie na czerpaniu przyjemności.
Iain

1
Iain, mówimy tutaj w szczególności o grach arkadowych, które tradycyjnie napędzane są listą wyników / rankingami. Gram mnóstwo shmupów i wiem, że jeśli znalazłem kogoś, kto publikuje wyniki ze sztucznym spowolnieniem w tabelach wyników, chciałbym, aby ich wyniki zostały usunięte.

Nie próbuję pomniejszać twojej odpowiedzi, ale zinterpretowałbym to jako stały krok, w którym renderowanie nie jest powiązane bezpośrednio z częstotliwością aktualizacji fizyki, z tym wyjątkiem, że nadrabianie zaległości fizycznych ma pierwszeństwo przed renderowaniem. Zdecydowanie ma dobre cechy.
AaronLS

6

To rozwiązanie nie dotyczy wszystkiego, ale istnieje inny poziom zmiennego timepep - zmienny timepep dla każdego obiektu na świecie.

Wydaje się to skomplikowane i może być, ale pomyśl o tym jako o modelowaniu gry jako o dyskretnej symulacji zdarzeń. Każdy ruch gracza może być reprezentowany jako zdarzenie, które rozpoczyna się, gdy zaczyna się ruch, i kończy, gdy ruch się kończy. Jeśli wystąpi jakakolwiek interakcja, która wymaga podzielenia zdarzenia (na przykład kolizji), zdarzenie jest anulowane, a kolejne zdarzenie jest wypychane do kolejki zdarzeń (prawdopodobnie kolejka priorytetowa posortowana według czasu zakończenia zdarzenia).

Renderowanie jest całkowicie odłączone od kolejki zdarzeń. Silnik wyświetlania interpoluje punkty między czasem rozpoczęcia / zakończenia zdarzenia, jeśli to konieczne, i może być tak dokładny lub tak niechlujny w tym oszacowaniu, jak potrzeba.

Aby zobaczyć złożoną implementację tego modelu, zobacz symulator kosmiczny EXOFLIGHT . Używa innego modelu wykonania niż większość symulatorów lotu - modelu opartego na zdarzeniach, a nie tradycyjnego modelu ustalonego przedziału czasu. Podstawowa główna pętla tego typu symulacji wygląda następująco w pseudokodzie:

while (game_is_running)
{
   world.draw_to_screen(); 
   world.get_player_input(); 
   world.consume_events_until(current_time + time_step); 
   current_time += time_step; 
}

Głównym powodem użycia jednego w symulatorze kosmicznym jest konieczność zapewnienia dowolnego przyspieszenia czasowego bez utraty dokładności. Niektóre misje w EXOFLIGHT mogą trwać lata gry, a nawet opcja przyspieszenia 32x byłaby niewystarczająca. Potrzebujesz użytecznego przyspieszenia o wartości ponad 1 000 000 razy, co jest trudne w modelu z odcinkami czasu. Dzięki modelowi opartemu na zdarzeniach otrzymujemy dowolne szybkości czasu, od 1 s = 7 ms do 1 s = 1 rok.

Zmiana szybkości czasu nie zmienia zachowania karty SIM, co jest krytyczną cechą. Jeśli nie ma wystarczającej mocy procesora, aby uruchomić symulator z żądaną szybkością, zdarzenia będą się nakładać i możemy ograniczyć odświeżanie interfejsu użytkownika, dopóki kolejka zdarzeń nie zostanie wyczyszczona. Podobnie możemy przesuwać kartę SIM tak szybko, jak chcemy i mieć pewność, że nie marnujemy procesora ani nie poświęcamy dokładności.

Podsumowując: możemy modelować jeden pojazd na długiej, spokojnej orbicie (wykorzystując integrację Runge-Kutta) i inny pojazd jednocześnie odbijając się od podłoża - oba pojazdy będą symulowane z odpowiednią dokładnością, ponieważ nie mamy globalnego pomiaru czasu.

Minusy: złożoność i brak jakichkolwiek gotowych silników fizycznych obsługujących ten model :)


5

Ustalony krok czasowy jest użyteczny, biorąc pod uwagę dokładność zmiennoprzecinkową i aby zapewnić spójność aktualizacji.

Jest to prosty fragment kodu, więc warto go wypróbować i sprawdzić, czy działa w twojej grze.

now = currentTime
frameTime = now - lastTimeStamp // time since last render()
while (frameTime > updateTime)
    update(timestep)
    frameTime -= updateTime     // update enough times to catch up
                                // possibly leaving a small remainder
                                // of time for the next frame

lastTimeStamp = now - frameTime // set last timestamp to now but
                                // subtract the remaining frame time
                                // to make sure the game will still
                                // catch up on those remaining few millseconds
render()

Głównym problemem związanym ze stosowaniem stałego kroku czasowego jest to, że gracze z szybkim komputerem nie będą mogli korzystać z tej prędkości. Renderowanie przy 100 fps, gdy gra jest aktualizowana tylko przy 30 fps, jest tym samym, co renderowanie przy 30 fps.

Biorąc to pod uwagę, może być możliwe zastosowanie więcej niż jednego ustalonego kroku czasowego. 60 klatek na sekundę można wykorzystać do aktualizacji trywialnych obiektów (takich jak interfejs użytkownika lub animowane duszki), a 30 klatek na sekundę do aktualizacji trywialnych systemów (takich jak fizyka i), a nawet wolniejszych timerów do zarządzania za kulisami, takich jak usuwanie nieużywanych obiektów, zasobów itp.


2
Jeśli gra jest starannie wykonana, metoda renderowania może wykonać interpolację, aby aktualizacje w 30 klatkach na sekundę nie były identyczne z renderowaniem przy 30 klatkach na sekundę.
Ricket

3

Oprócz tego, co już powiedziałeś, może sprowadzać się do poczucia, że ​​chcesz, aby gra była. Jeśli nie możesz zagwarantować, że zawsze będziesz mieć stałą liczbę klatek, prawdopodobnie gdzieś zwolnisz, a stałe i zmienne przedziały czasowe będą wyglądać zupełnie inaczej. Naprawiono efekt, przez który twoja gra działała przez chwilę w zwolnionym tempie, co czasem może być zamierzonym efektem (spójrz na strzelankę w starym stylu szkolnym, taką jak Ikaruga, w której ogromne wybuchy powodują spowolnienie po pokonaniu bossa). Zmienne stopnie czasowe utrzymują rzeczy w ruchu z odpowiednią prędkością pod względem czasu, ale możesz zobaczyć nagłe zmiany pozycji itp., Które mogą utrudnić graczowi dokładne wykonywanie akcji.

Naprawdę nie widzę, że ustalony krok czasowy ułatwi rzeczy w sieci, wszystkie byłyby nieco niezsynchronizowane na początku i spowolnienie na jednym komputerze, ale żaden inny nie wypchnąłby rzeczy bardziej zsynchronizowanych.

Zawsze skłaniałem się ku podejściu zmiennemu, ale w tych artykułach jest kilka interesujących rzeczy do przemyślenia. Nadal uważam stałe kroki za dość powszechne, szczególnie na konsolach, gdzie ludzie myślą, że liczba klatek na sekundę wynosi 60 klatek na sekundę w porównaniu z bardzo wysokimi prędkościami osiągalnymi na PC.


5
Zdecydowanie powinieneś przeczytać link do gry Gaffer w grach w oryginalnym poście. Nie sądzę, że to zła odpowiedź sama w sobie, więc nie oddam głosu, ale nie zgadzam się z żadnym z twoich argumentów .
falstro

Nie sądzę, aby spowolnienie w grze w wyniku ustalonego czasu mogło być celowe, ponieważ jest to spowodowane brakiem kontroli. Brak kontroli jest z definicji poddaniem się przypadkowi, a zatem nie może być celowy. Może się zdarzyć, że miałeś na myśli, ale o to mi chodzi. Jeśli chodzi o ustalony czas w sieci, istnieje wyraźny plus, ponieważ utrzymywanie silników fizyki na dwóch różnych maszynach w synchronizacji bez tego samego czasu jest prawie niemożliwe. Ponieważ jedyną opcją synchronizacji byłoby wysłanie wszystkich transformacji encji, byłoby to zbyt duże obciążenie przepustowości.
Kaj

0

Skorzystaj z metody „napraw swój krok” Gaffera. Aktualizuj grę w ustalonych krokach, jak w opcji 1, ale rób to wiele razy na renderowaną klatkę - na podstawie upływu czasu - aby logika gry nadążała za czasem, pozostając w dyskretnych krokach. W ten sposób łatwa do zaimplementowania logika gry, taka jak integratory Eulera i proste wykrywanie kolizji, nadal działa. Masz również opcję interpolacji animacji graficznych w oparciu o czas delta, ale dotyczy to tylko efektów wizualnych i niczego, co nie wpływa na podstawową logikę gry. Możesz potencjalnie wpaść w kłopoty, jeśli twoje aktualizacje są bardzo intensywne - jeśli aktualizacje pozostaną w tyle, będziesz potrzebować ich coraz więcej, aby nadążyć, co może sprawić, że Twoja gra będzie jeszcze mniej responsywna.

Osobiście podoba mi się opcja 1, gdy mogę sobie z tym poradzić, oraz opcja 3, gdy muszę zsynchronizować z czasem rzeczywistym. Szanuję, że opcja 2 może być dobrym rozwiązaniem, jeśli wiesz, co robisz, ale znam moje ograniczenia na tyle dobrze, aby trzymać się od niej z daleka


Jeśli chcesz ukraść odpowiedzi, przynajmniej uznaj tę osobę!
PrimRock,

0

Przekonałem się, że ustalone czasy synchronizacji do 60 klatek na sekundę zapewniają płynną animację lustrzaną. Jest to szczególnie ważne w przypadku aplikacji VR. Wszystko inne jest mdłe fizycznie.

Zmienne kroki czasowe nie są odpowiednie dla VR. Rzuć okiem na kilka przykładów Unity VR, które wykorzystują różne przebiegi czasowe. To jest nieprzyjemne

Zasadą jest, że jeśli gra 3D jest płynna w trybie VR, będzie doskonała w trybie innym niż VR.

Porównaj te dwa (aplikacje Cardboard VR)

(Zmienne czasy)

(Naprawiono timepeps)

Twoja gra musi być wielowątkowa, aby osiągnąć spójny czas / ilość klatek na sekundę. Fizyka, interfejs użytkownika i rendering muszą być podzielone na dedykowane wątki. Synchronizowanie ich przez PITA jest obrzydliwe, ale rezultatem jest lustrzane, płynne renderowanie, które chcesz (szczególnie dla VR).

Gry mobilne są szczególnie. wyzwanie, ponieważ wbudowane procesory i procesory graficzne mają ograniczoną wydajność. Używaj GLSL (slang) oszczędnie, aby odciążyć jak najwięcej pracy z procesora. Należy pamiętać, że przekazywanie parametrów do GPU zużywa zasoby magistrali.

Zawsze miej wyświetlaną liczbę klatek na sekundę podczas programowania. Prawdziwa gra polega na utrzymaniu stałej prędkości 60 klatek na sekundę. Jest to natywna szybkość synchronizacji dla większości ekranów, a także dla większości gałek ocznych.

Środowisko, którego używasz, powinno być w stanie powiadomić Cię o żądaniu synchronizacji lub użyć timera. Nie wprowadzaj opóźnienia uśpienia / oczekiwania, aby to osiągnąć - nawet niewielkie różnice są zauważalne.


0

Zmienne przedziały czasowe dotyczą procedur, które powinny być uruchamiane tak często, jak to możliwe: cykle renderowania, obsługa zdarzeń, rzeczy sieciowe itp.

Stałe przedziały czasowe dotyczą sytuacji, gdy potrzebujesz czegoś, co będzie przewidywalne i stabilne. Obejmuje to między innymi fizykę i wykrywanie kolizji.

W praktyce wykrywanie fizyki i kolizji powinno być oddzielone od wszystkiego innego, na jego własnym etapie czasowym. Powodem wykonywania takich procedur na niewielkim stałym etapie jest zachowanie ich dokładności. Wielkość impulsu jest wysoce zależna od czasu, a jeśli interwał staje się zbyt duży, symulacja staje się niestabilna, a szalone rzeczy dzieją się jak odbijająca się piłka przechodząca przez ziemię lub odbijająca się od świata gry, z których żadne nie jest pożądane.

Cała reszta może działać w zmiennym przedziale czasowym (choć profesjonalnie rzecz biorąc, często dobrym pomysłem jest zezwolenie na blokowanie renderowania w określonym przedziale czasu). Aby silnik gry był responsywny, takie rzeczy jak komunikaty sieciowe i dane wejściowe użytkownika powinny być obsługiwane jak najszybciej, co oznacza, że ​​odstęp między odpytywaniem powinien być możliwie jak najkrótszy. Ogólnie oznacza to zmienną.

Cała reszta może być obsługiwana asynchronicznie, dzięki czemu synchronizacja jest kwestią sporną.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.