Prognozy ruchów dla osób niebędących strzelcami

35

Pracuję nad izometryczną grą 2D z umiarkowaną skalą dla wielu graczy, około 20-30 graczy jednocześnie połączonych z trwałym serwerem. Miałem pewne trudności z wdrożeniem dobrej implementacji przewidywania ruchu.

Fizyka / ruch

Gra nie ma prawdziwej implementacji fizyki, ale wykorzystuje podstawowe zasady do implementacji ruchu. Zamiast ciągłego sprawdzania danych wejściowych zmiany stanu (tj. / Zdarzenia myszy w dół / w górę / ruch) są używane do zmiany stanu jednostki postaci, którą kontroluje gracz. Kierunek gracza (tj. / Północny-wschód) jest łączony ze stałą prędkością i zamieniany w prawdziwy wektor 3D - prędkość istoty.

W głównej pętli gry przed aktualizacją nazywa się „Aktualizacja”. Logika aktualizacji uruchamia „zadanie aktualizacji fizyki”, która śledzi wszystkie byty z niezerową prędkością, wykorzystuje bardzo podstawową integrację do zmiany pozycji bytów. Na przykład: entity.Position + = entity.Velocity.Scale (ElapsedTime.Seconds) (gdzie „Seconds” jest wartością zmiennoprzecinkową, ale to samo podejście działałoby w przypadku wartości całkowitych milisekundowych).

Kluczową kwestią jest to, że do ruchu nie stosuje się interpolacji - podstawowy silnik fizyki nie ma pojęcia „poprzedniego stanu” lub „bieżącego stanu”, a jedynie pozycję i prędkość.

Pakiety zmiany stanu i aktualizacji

Gdy prędkość jednostki postaci kontrolowanej przez gracza zmienia się, do serwera wysyłany jest pakiet „awatar ruchu” zawierający typ akcji istoty (stan, chód, bieg), kierunek (północno-wschodni) i aktualną pozycję. Różni się to od działania gier 3D w pierwszej osobie. W grze 3D prędkość (kierunek) może zmieniać się klatka po klatce, gdy gracz się porusza. Wysłanie każdej zmiany stanu skutecznie przesłałoby pakiet na ramkę, co byłoby zbyt drogie. Zamiast tego gry 3D wydają się ignorować zmiany stanu i wysyłają pakiety „aktualizacji stanu” w ustalonych odstępach czasu - powiedzmy, co 80-150 ms.

Ponieważ aktualizacje prędkości i kierunku występują znacznie rzadziej w mojej grze, mogę uniknąć wysyłania każdej zmiany stanu. Chociaż wszystkie symulacje fizyki odbywają się z tą samą prędkością i są deterministyczne, opóźnienie jest nadal problemem. Z tego powodu wysyłam rutynowe pakiety aktualizacji pozycji (podobne do gry 3D), ale znacznie rzadziej - teraz co 250 ms, ale podejrzewam, że z dobrą prognozą mogę z łatwością zwiększyć je do 500 ms. Największy problem polega na tym, że odeszłam od normy - cała inna dokumentacja, przewodniki i próbki online wysyłają rutynowe aktualizacje i interpolują oba te stany. Wydaje się to niezgodne z moją architekturą i muszę wymyślić lepszy algorytm przewidywania ruchu, który jest bliższy (bardzo podstawowej) architekturze „fizyki sieciowej”.

Serwer następnie odbiera pakiet i określa prędkość gracza na podstawie jego typu ruchu na podstawie skryptu (Czy gracz jest w stanie uruchomić? Uzyskaj prędkość biegania gracza). Gdy osiągnie prędkość, łączy ją z kierunkiem uzyskania wektora - prędkości bytu. Występuje pewne wykrywanie oszustw i podstawowe sprawdzanie poprawności, a jednostka po stronie serwera jest aktualizowana o bieżącą prędkość, kierunek i pozycję. Podstawowe dławienie jest również wykonywane, aby uniemożliwić graczom zalanie serwera żądaniami ruchu.

Po zaktualizowaniu własnego bytu serwer rozgłasza pakiet „aktualizacja pozycji awatara” do wszystkich innych graczy w zasięgu. Pakiet aktualizacji pozycji służy do aktualizacji symulacji fizyki po stronie klienta (stan świata) zdalnych klientów oraz do wykonywania prognoz i kompensacji opóźnień.

Prognozowanie i kompensacja opóźnień

Jak wspomniano powyżej, klienci są wiarygodni dla swojej pozycji. Z wyjątkiem przypadków oszukiwania lub anomalii, awatar klienta nigdy nie zostanie zmieniony przez serwer. Awatar klienta nie wymaga ekstrapolacji („przenieś teraz i popraw później”) - to, co gracz widzi, jest poprawne. Jednak dla wszystkich poruszających się jednostek zdalnych wymagana jest pewna ekstrapolacja lub interpolacja. Pewne przewidywanie i / lub kompensacja opóźnień jest wyraźnie wymagana w lokalnym silniku symulacji / fizyki klienta.

Problemy

Walczę z różnymi algorytmami i mam wiele pytań i problemów:

  1. Czy powinienem ekstrapolować, interpolować, czy jedno i drugie? Moje „przeczucie” polega na tym, że powinienem stosować czystą ekstrapolację opartą na prędkości. Klient otrzymuje zmianę stanu, klient oblicza „przewidywaną” prędkość, która kompensuje opóźnienie, a normalny system fizyki zajmuje się resztą. Czuje się jednak w sprzeczności z innymi przykładowymi kodami i artykułami - wszystkie wydają się przechowywać szereg stanów i przeprowadzać interpolację bez silnika fizyki.

  2. Kiedy nadchodzi pakiet, próbowałem interpolować pozycję pakietu z prędkością pakietu w określonym czasie (powiedzmy 200 ms). Następnie biorę różnicę między pozycją interpolowaną a bieżącą pozycją „błędu”, aby obliczyć nowy wektor i umieścić go na bycie zamiast prędkości, która została wysłana. Zakłada się jednak, że w tym przedziale czasowym nadejdzie kolejny pakiet i niezwykle trudno jest „zgadnąć”, kiedy nadejdzie następny pakiet - zwłaszcza, że ​​nie wszystkie one docierają w ustalonych odstępach czasu (tj. Również zmiany stanu). Czy koncepcja jest zasadniczo wadliwa, czy jest poprawna, ale wymaga pewnych poprawek / korekt?

  3. Co się stanie, gdy zdalny odtwarzacz się zatrzyma? Mogę natychmiast zatrzymać byt, ale będzie on ustawiony w „złym” miejscu, dopóki nie ruszy się ponownie. Jeśli oszacuję wektor lub spróbuję interpolować, mam problem, ponieważ nie zapisuję poprzedniego stanu - silnik fizyki nie może powiedzieć „musisz zatrzymać się po osiągnięciu pozycji X”. Po prostu rozumie prędkość, nic bardziej złożonego. Nie chcę dodawać informacji o stanie ruchu pakietu do encji lub silnika fizyki, ponieważ narusza to podstawowe zasady projektowania i upuszcza kod sieciowy w pozostałej części silnika gry.

  4. Co powinno się stać, gdy byty zderzą się? Istnieją trzy scenariusze - kontrolujący gracz zderza się lokalnie, dwa byty zderzają się na serwerze podczas aktualizacji pozycji lub zdalna aktualizacja bytu zderza się z lokalnym klientem. We wszystkich przypadkach nie jestem pewien, jak poradzić sobie z kolizją - oprócz oszukiwania oba stany są „poprawne”, ale w różnych okresach. W przypadku jednostki zdalnej nie ma sensu rysować jej przechodząc przez ścianę, dlatego wykonuję wykrywanie kolizji na lokalnym kliencie i powoduję, że „zatrzymuje się”. W oparciu o punkt 2 powyżej mogę obliczyć „poprawiony wektor”, który nieustannie próbuje przenieść byt „przez ścianę”, co nigdy się nie powiedzie - zdalny awatar utknie tam, dopóki błąd nie stanie się zbyt wysoki i „zaskoczy” pozycja. Jak działają gry?

ShadowChaser
źródło
1
Co gra 3D lub 2D ma wspólnego z tym, jakiego serwera używasz? i dlaczego nieautoryzowany serwer nie działa dla twojej gry?
AttackingHobo
1
@Roy T. kompromisy przepustowości. Przepustowość jest najcenniejszym zasobem we współczesnych systemach komputerowych.
FxIII
1
To po prostu nieprawda, gry online są w dużej mierze zdominowane przez czas reakcji, na przykład na linii 10 Mb / s (1,25 MB / s) opóźnienie między serwerem a klientem wynosi 20 ms, wysyłanie pakietu 1,25 kb zajmie 20 ms + 1 ms. Wysłanie pakietu 12,5 kb zajmie 30 ms. Na linii dwa razy szybszej pakiet 1,25 kb nadal zajmie 20 ms + 0,5 ms, a 20 ms + 5 ms dla pakietu 12 KB. Opóźnienie jest czynnikiem ograniczającym, a nie przepustowością. W każdym razie nie wiem, ile jest danych, ale wysłanie 50 wektorów3 (pozycja 25x + obrót 25x) to tylko 600 bajtów, wysyłanie tego co 20ms będzie kosztować 30kb / s. (+ narzut pakietów).
Roy T.
2
Silnik Quake ma przewidywania od pierwszej wersji. Prognozy dotyczące trzęsień są opisane tam i w niektórych innych miejscach. Sprawdź to.
user712092,
1
Czy robisz tę pozycję + = prędkość * deltatime dla każdej jednostki równolegle (koniecznie: w kodzie masz 2 tablice parametrów fizycznych jednostek, jedna ramka od siebie, aktualizujesz starszą, aby była nowsza i zamieniasz je)? Istnieją pewne problemy z iteracją Seana Barreta, który stworzył bazę silnika Thief 1 .
user712092,

Odpowiedzi:

3

Jedyną rzeczą do powiedzenia jest to, że 2D, izometryczny, 3D, wszystkie są takie same, jeśli chodzi o ten problem. Ponieważ widzisz wiele przykładów 3D i używasz jedynie systemu wprowadzania 2D z ograniczeniem oktantowym o natychmiastowej prędkości, nie oznacza to, że możesz wyrzucić zasady sieciowe, które ewoluowały przez ostatnie 20 lat.

Zasady projektowania niech będą przeklęte, gdy gra jest zagrożona!

Wyrzucając poprzednie i bieżące, odrzucasz kilka informacji, które mogłyby rozwiązać Twój problem. Do tych danych dodałbym znaczniki czasu i obliczone opóźnienie, aby ekstrapolacja mogła lepiej przewidzieć, gdzie będzie ten gracz, a interpolacja może lepiej wygładzić zmiany prędkości w czasie.

Powyższe jest głównym powodem, dla którego serwery wydają dużo informacji o stanie i nie kontrolują danych wejściowych. Innym ważnym powodem jest protokół, którego używasz. UDP z zaakceptowaną utratą pakietu i dostawą poza kolejnością? TCP z zapewnioną dostawą i ponownymi próbami? Z dowolnym protokołem będziesz otrzymywać pakiety w dziwnych momentach, opóźnione lub ułożone jeden na drugim w przypływie aktywności. Wszystkie te dziwne pakiety muszą pasować do kontekstu, aby klient mógł dowiedzieć się, co się dzieje.

Wreszcie, mimo że Twoje nakłady są bardzo ograniczone do 8 kierunków, faktyczna zmiana może nastąpić w dowolnym momencie - wymuszenie cyklu 250 ms po prostu frustruje szybkich graczy. 30 graczy to nic wielkiego do poradzenia sobie z jakimkolwiek serwerem. Jeśli mówisz o tysiącach ... nawet wtedy ich grupy są podzielone na wiele boxen, więc pojedyncze serwery przenoszą tylko rozsądne obciążenie.

Czy kiedykolwiek profilowałeś silnik fizyki, taki jak Havok lub Bullet? Są naprawdę bardzo zoptymalizowane i bardzo, bardzo szybkie. Możesz wpaść w pułapkę zakładając, że operacja ABC będzie powolna i optymalizuje coś, co jej nie potrzebuje.

Patrick Hughes
źródło
Zdecydowana rada mędrca tutaj! Łatwo jest stracić z oczu duży obraz. W tym przypadku używam TCP. Problem „8 kierunków” nie jest tak dużym problemem pod względem nakładów - jest raczej problemem z interpolacją i ekstrapolacją. Grafika jest ograniczona do tych kątów i wykorzystuje animowane duszki - rozgrywka „wygląda dziwnie”, jeśli gracz porusza się pod innym kątem lub prędkością, która jest zbyt daleko od normy.
ShadowChaser,
1

Więc twój serwer jest w zasadzie „sędzią”? W tym przypadku uważam, że wszystko w twoim kliencie musi być deterministyczne; musisz upewnić się, że wszystko na każdym kliencie zawsze da taki sam rezultat.

Jeśli chodzi o twoje pierwsze pytanie, gdy lokalny gracz osiągnie kierunek innych graczy, poza tym, że będzie w stanie spowolnić swój ruch w czasie i zastosować kolizje, nie widzę, w jaki sposób można przewidzieć, w którym kierunku następny gracz się obróci, szczególnie w 8-kierunkowe środowisko.

Po otrzymaniu aktualizacji „rzeczywistej pozycji” każdego gracza (że być może możesz spróbować zataczania się na serwerze) tak, będziesz musiał interpolować pozycję i kierunek gracza. Jeśli pozycja „zgadnięta” jest bardzo błędna (tj. Gracz całkowicie zmienił kierunek zaraz po wysłaniu pakietu z ostatnim kierunkiem), będziesz miał ogromną lukę. Oznacza to, że albo gracz przeskakuje na pozycję, albo możesz interpolować do następnej odgadniętej pozycji. Zapewni to płynniejszą interpolację w czasie.

Gdy byty zderzają się, jeśli możesz stworzyć system deterministyczny, każdy gracz może symulować kolizję lokalnie, a ich wyniki nie powinny być zbyt dalekie od rzeczywistości. Każda lokalna maszyna powinna symulować kolizję dla obu graczy, w takim przypadku upewniając się, że stan końcowy będzie nieblokujący i akceptowalny.

Po stronie serwera serwer sędziowski może nadal wykonywać proste obliczenia, aby na przykład sprawdzić prędkość gracza w krótkim czasie i wykorzystać go jako prosty mechanizm zapobiegający oszukiwaniu. Jeśli przejdziesz przez monitorowanie każdego gracza przez 1 s na raz, wykrywanie oszustów będzie skalowalne, ale znalezienie oszustów potrwa dłużej.

Jonathan Connell
źródło
Dzięki - to brzmi dość blisko tego, czego potrzebuję, szczególnie po stronie serwera. Ciekawym punktem jest to, że chociaż gracze są zablokowani w 8 kierunkach, ruch wewnętrzny jest wektorem 3D. Myślałem o tym trochę więcej w ciągu ostatniego dnia i myślę, że mam problem z tym, że w ogóle nie mam interpolacji - po prostu używam bardzo podstawowej integracji, ustawiając prędkość i aktualizując pozycję w oparciu o wektor każdej aktualizacji.
ShadowChaser
Nie jestem pewien, jak połączyć to z interpolacją lub prognozowaniem. Próbowałem wziąć zaktualizowaną pozycję wysłaną w pakiecie, zintegrować ją w ustalonym czasie (powiedzmy 200 ms), a następnie określić wektor (prędkość) potrzebny do osiągnięcia tego punktu w 200 ms. Innymi słowy, niezależnie od bieżącej nieprawidłowej pozycji gracza po stronie klienta, powinni oni nadal osiągać tę samą „szacunkową prawidłową pozycję” w ciągu 200 ms. Skończyło się to wysyłaniem mojej postaci w szalonych kierunkach - zakładam, ponieważ 200 ms naprawdę powinno być czasem na następny pakiet, którego nie mogę oszacować.
ShadowChaser
Czy upewniłeś się, że najpierw zintegrowałeś prawidłową pozycję od t do t + 1, zanim zintegrowałeś niewłaściwą pozycję z odgadniętą właściwą pozycją w t + 1?
Jonathan Connell,
Tak - dwukrotnie sprawdziłem, czy używam prawidłowej pozycji dla oryginalnej integracji. Początkowo był to błąd, ale naprawienie go nadal nie powodowało zauważalnej poprawy. Podejrzewam, że „+1” - musi być bardzo zależne od czasu między pakietami. Istnieją dwa problemy: wysyłaj zmiany stanu oprócz zwykłych (250 ms) aktualizacji i nie mogę przewidzieć, kiedy one wystąpią. Nie chcę też blokować się w określonym przedziale czasu, ponieważ serwer ma sens wysyłania mniejszej liczby aktualizacji dla podmiotów, które są dalej od odtwarzacza. Czas między pakietami może ulec zmianie.
ShadowChaser
1
Tak, w tym ustalony rodzaj pomiaru czasu prawdopodobnie nie jest dobrym pomysłem. Martwię się jednak, że nieregularność ruchu w 8 kierunkach będzie bardzo trudna (jeśli nie niemożliwa?) Do przewidzenia. Mimo to możesz spróbować użyć średniego czasu oczekiwania klienta, aby przewidzieć t + 1 i mieć próg, powyżej którego zawsze „teleportujesz” innych graczy do ich nowych pozycji.
Jonathan Connell,
0

Czy nie możesz uwzględnić prędkości w komunikatach o zmianie stanu i użyć jej do przewidywania ruchu? np. zakładasz, że prędkość się nie zmienia, dopóki nie pojawi się komunikat o zmianie? Myślę, że już wysyłasz pozycje, więc jeśli coś „przeskakuje” z tego powodu, masz poprawną pozycję od następnej aktualizacji. Następnie możesz przesuwać pozycje podczas aktualizacji, jak to już robisz, używając prędkości z ostatniej wiadomości i zastępując pozycję za każdym razem, gdy wiadomość zostanie odebrana z nową pozycją. Oznacza to również, że jeśli pozycja się nie zmienia, ale prędkość musi wysłać wiadomość (jeśli jest to nawet słuszny przypadek w grze), ale nie wpłynie to znacząco na wykorzystanie przepustowości, jeśli w ogóle.

Interpolacja nie powinna mieć tutaj znaczenia, to znaczy np. Kiedy wiesz, gdzie coś będzie w przyszłości, czy masz ją, jakiej metody używasz itp. Czy może mylisz się z ekstrapolacją? (dla których opisuję jedno, proste podejście)

jheriko
źródło
-1

Moje pierwsze pytania brzmiałyby: co jest złego w korzystaniu z modelu, w którym serwer ma uprawnienia? Dlaczego ma to znaczenie, czy środowisko jest 2D czy 3D? Ułatwiłoby to ochronę przed oszustwami, gdyby serwer był autorytatywny.

Większość próbek, które widziałem, ściśle łączy prognozowanie ruchu bezpośrednio w samych bytach. Na przykład zapisywanie poprzedniego stanu wraz z bieżącym stanem. Chciałbym tego uniknąć i utrzymywać byty tylko w ich „obecnym stanie”. Czy istnieje lepszy sposób, aby sobie z tym poradzić?

Podczas wykonywania przewidywania konieczne jest utrzymanie kilku stanów (lub przynajmniej delt) na kliencie, aby po otrzymaniu z serwera autorytatywnego stanu / delty można go porównać z stanami klienta i dokonać niezbędnych korekty. Chodzi o to, aby zachować jak najwięcej determinizmu, aby zminimalizować liczbę wymaganych poprawek. Jeśli nie utrzymasz poprzednich stanów, nie będziesz wiedział, czy coś innego wydarzyło się na serwerze.

Co powinno się stać, gdy gracz się zatrzyma? Nie mogę interpolować do prawidłowej pozycji, ponieważ może być konieczne przejście do tyłu lub inny dziwny kierunek, jeśli ich pozycja jest zbyt daleko do przodu.

Dlaczego potrzebujesz interpolować? Autorytatywny serwer powinien zastąpić wszelkie błędne ruchy.

Co powinno się stać, gdy byty zderzą się? Jeśli aktualny gracz się z czymś zderzy, odpowiedź jest prosta - po prostu powstrzymaj gracza przed poruszaniem się. Ale co się stanie, jeśli dwa podmioty zajmą to samo miejsce na serwerze? Co się stanie, jeśli lokalne prognozy spowodują zderzenie odległej jednostki z odtwarzaczem lub inną jednostką - czy ja też je zatrzymam? Jeśli przepowiednia miała nieszczęście przykleić ją przed ścianą, którą obrócił gracz, przepowiednia nigdy nie będzie w stanie tego zrekompensować, a gdy błąd osiągnie wysoki poziom, jednostka przeskoczy na nową pozycję.

Są to sytuacje, w których wystąpiłby konflikt między serwerem a klientem i dlatego należy utrzymywać stany na kliencie, aby serwer mógł poprawić wszelkie błędy.

Przepraszam za szybkie odpowiedzi, muszę iść. Przeczytaj ten artykuł , wspomina o strzelankach, ale powinien działać w każdej grze wymagającej sieci w czasie rzeczywistym.

Gyan alias Gary Buyn
źródło
Kilka odpowiedzi: * Jeśli serwer ma uprawnienia, będzie odpowiedzialny za śledzenie wszystkich poruszających się podmiotów i aktualizowanie ich pozycji w regularnych odstępach czasu. Innymi słowy, musi uruchomić silnik fizyki - co może być drogie. Skalowalność jest moim głównym celem projektowym. * Muszę interpolować po stronie klienta, w przeciwnym razie każda aktualizacja serwera wysyłana do klientów spowoduje skoki jednostek. W tej chwili moja interpolacja odbywa się w silniku fizyki - po prostu ustawia prędkość. Nie ma stanów ani delt.
ShadowChaser
Przeczytałem wszystkie artykuły Glenna, ale w komentarzach stwierdza, że ​​są one przeznaczone tylko dla strzelców (tj. / Wysokich częstotliwości aktualizacji). Kilka jego artykułów mówi o autorytatywnych klientach, czyli o implementacji, o którą dążę. Nie chcę wykonywać interpolacji / fizyki na serwerze, ale jestem gotów zmienić zdanie, jeśli to naprawdę jedyny sposób :)
ShadowChaser
1
-1. To, co napisałeś, niejasno dotyka tematu; czuje się dwuznacznie. Odpowiedzi wydają się niejednoznaczne, gdy są zasadniczo „czytać ten długi artykuł”, ale nie zawierają przydatnych informacji z danego artykułu.
AttackingHobo
1
@AttackingHobo musiałbym się z tobą zgodzić. Wspomniałem, że się spieszyłem, ale to nie jest wymówka. Gdybym nie miał czasu, lepiej zostawić go w spokoju. Wyciągnięta lekcja.
Gyan alias Gary Buyn