Pracuję nad strzelanką 2D z góry i staram się kopiować koncepcje stosowane w grach sieciowych, takich jak Quake 3.
- Mam autorytatywny serwer.
- Serwer wysyła migawki do klientów.
- Migawki zawierają znacznik czasu i pozycje encji.
- Elementy są interpolowane między pozycjami migawek, dzięki czemu ruch wygląda płynnie.
- Z konieczności interpolacja bytu zachodzi nieco „w przeszłości”, dzięki czemu mamy wiele migawek, w których można interpolować.
Problem, przed którym stoję, to „synchronizacja zegara”.
- Dla uproszczenia, po prostu udawajmy przez chwilę, że nie ma żadnych opóźnień podczas przesyłania pakietów do i z serwera.
- Jeśli zegar serwera jest 60 sekund przed zegarem klienta, znacznik czasu migawki będzie 60000 ms przed lokalnym znacznikiem czasu klienta.
- Dlatego migawki jednostek będą zbierać się i siedzieć przez około 60 sekund, zanim klient zobaczy, że dana istota wykonuje swoje ruchy, ponieważ tak długo trwa, zanim zegar klienta dogoni.
Udało mi się temu zaradzić, obliczając różnicę między zegarem serwera i klienta za każdym razem, gdy otrzymywana jest migawka.
// For simplicity, don't worry about latency for now...
client_server_clock_delta = snapshot.server_timestamp - client_timestamp;
Określając, jak daleko od interpolacji jest jednostka, po prostu dodaję różnicę do bieżącego czasu klienta. Problem polega jednak na tym, że spowoduje to szarpnięcie, ponieważ różnica między dwoma zegarami gwałtownie się zmienia z powodu migawek przybywających szybciej / wolniej niż inne.
Jak mogę zsynchronizować zegary wystarczająco dokładnie, aby jedynym zauważalnym opóźnieniem było to, które jest zakodowane na sztywno do interpolacji i które jest spowodowane zwykłym opóźnieniem sieci?
Innymi słowy, w jaki sposób mogę zapobiec zbyt późnemu lub zbyt wczesnemu uruchomieniu interpolacji, gdy zegary ulegną znacznej desynchronizacji, bez powodowania szarpnięć?
Edycja: Według Wikipedii NTP można wykorzystać do synchronizacji zegarów przez Internet w ciągu kilku milisekund. Jednak protokół wydaje się skomplikowany, a może przesada w użyciu w grach?
Odpowiedzi:
Po przeszukaniu wydaje się, że synchronizacja zegarów 2 lub więcej komputerów nie jest trywialnym zadaniem. Protokół taki jak NTP wykonuje dobrą robotę, ale podobno jest powolny i zbyt skomplikowany, aby był praktyczny w grach. Ponadto używa UDP, który nie działa dla mnie, ponieważ pracuję z gniazdami internetowymi, które nie obsługują UDP.
Znalazłem tu jednak metodę , która wydaje się stosunkowo prosta:
Twierdzi, że synchronizuje zegary w odstępach 150 ms (lub lepszych).
Nie wiem, czy to wystarczy do moich celów, ale nie byłem w stanie znaleźć bardziej precyzyjnej alternatywy.
Oto algorytm, który zapewnia:
To rozwiązanie wydaje się dobrze odpowiadać na moje pytanie, ponieważ synchronizuje zegar, a następnie zatrzymuje się, umożliwiając liniowy przepływ czasu. Podczas gdy moja początkowa metoda ciągle aktualizowała zegar, powodując, że czas przeskakiwał nieco w miarę odbierania migawek.
źródło
Zasadniczo nie możesz naprawić [całego] świata i ostatecznie będziesz musiał narysować linię.
Jeśli serwer i wszyscy klienci współużytkują tę samą liczbę klatek na sekundę, muszą tylko zsynchronizować się po połączeniu, a czasami później, szczególnie po zdarzeniu opóźnienia. Opóźnienie nie wpływa na upływ czasu ani na zdolność komputera do jego pomiaru, dlatego w wielu przypadkach zamiast interpolować, należy dokonać ekstrapolacji. Stwarza to równie niepożądane efekty, ale znowu jest to, co to jest i musisz wybrać najmniej dostępne zło.
Weź pod uwagę, że w wielu popularnych grach MMO opóźnieni gracze są wizualnie oczywisti. Jeśli widzisz, jak biegną w miejscu, bezpośrednio do ściany, twój klient dokonuje ekstrapolacji. Gdy klient otrzymuje nowe dane, gracz (na swoim kliencie) mógł się przesunąć na znaczną odległość i będzie „gumką” lub teleportować się w nowe miejsce („szarpnięcie”, o którym wspominałeś?). Dzieje się tak nawet w dużych markowych grach.
Technicznie jest to problem z infrastrukturą sieciową gracza, a nie z grą. Punktem, w którym przechodzi od jednego do drugiego, jest właśnie linia, którą musisz narysować. Twój kod na 3 osobnych komputerach powinien rejestrować mniej więcej tyle samo czasu, który upłynął. Jeśli nie otrzymasz aktualizacji, nie powinno to wpłynąć na szybkość odświeżania Update (); jeśli już, to powinno być szybsze, ponieważ prawdopodobnie jest mniej do zaktualizowania.
„Jeśli masz kiepski internet, nie możesz grać w tę konkurencję”.
To nie jest przewrotność ani błąd.
źródło