Robię prostą małą MOBA dla zabawy. Robiłem wszystko dla jednego gracza, a potem zdałem sobie sprawę, „cholera, prawdopodobnie powinienem dodać tryb wieloosobowy, huh”.
Nigdy wcześniej nie robiłem nic z siecią, więc nauka integracji Lidgrena z moją grą była fajna i niesamowita. Chodzi o to, że właściwie wiem, że sposób, w jaki robię, jest zły, ponieważ, o ile wiem, nie jest wystarczająco solidny, aby korzystać z gier głównego nurtu, ale co jest z tym nie tak?
Zasadniczo za każdym razem, gdy gracz wykonuje akcję, wysyła komunikat do serwera z informacją: „hej, właśnie to zrobiłem”. Serwer i klient uruchamiają tę samą symulację. Serwer wysyła następnie wiadomość do wszystkich innych klientów, informując ich, że ten facet to zrobił.
W większości przypadków, z wyjątkiem kilku przypadków, gdy gracz coś robi, klient zakłada, że jest fajny i sam go realizuje. Więc kiedy klikniesz gdzieś prawym przyciskiem myszy, aby się tam przenieść, klient tego gracza po prostu zaczyna tam przenosić swojego faceta, a następnie wysyła wiadomość na serwer z informacją o tym.
Więc w zasadzie:
- Gracz 1 rzuca zaklęcie, dzięki czemu porusza się o 100% szybciej przez sześć sekund
- Lokalny klient Gracza 1 dodaje to wzmocnienie do swojego obiektu Jednostki
- Klient Gracza 1 wysyła do serwera wiadomość „hej, właśnie rzuciłem to zaklęcie”
- Serwer upewnia się, że naprawdę miał wystarczająco dużo many, aby rzucić to zaklęcie, a jeśli tak, dodaje to wzmocnienie do kopii serwera tego obiektu Jednostki
- Serwer wysyła wiadomość do wszystkich pozostałych klientów, mówiąc: „hej, ten facet właśnie rzucił to zaklęcie”
- Każdy inny klient otrzymuje wiadomość i „och dobrze” i dodaje to wzmocnienie do lokalnego obiektu Jednostki dla tego gracza
Przeglądałem różne rzeczy, aby zobaczyć, jak duże gry radzą sobie w trybie dla wielu graczy, i jest to trochę mylące dla kogoś, kto dopiero zaczyna bawić się w te rzeczy, ale wygląda na to, że silnik Source wysyła pakiet zawierający wszystkie zmiany we wszystkim w świat co tyka? Znowu, zupełnie nowy w tych rzeczach, ale czy naprawdę możesz tak często przesyłać tak dużo danych?
Przepraszam, jeśli jest to nieco chaotyczny, ale w zasadzie zastanawiałem się, dlaczego mój prostszy system nie jest właściwą drogą, ponieważ gdyby tak było, inne gry mogłyby z niego skorzystać, prawda?
źródło
Odpowiedzi:
Oprócz odpowiedzi Byte56 jest jeszcze kilka rzeczy do rozważenia:
Jak zamierzasz komunikować się między klientami na temat ruchu gracza? W przeciwieństwie do większości innych akcji gracza, które są zwykle odosobnionymi zdarzeniami i prawdopodobnie dość rzadko, ruch jest ciągły. Istnieje limit szybkości, z jaką możesz (i chcesz w tym przypadku) wysyłać i odbierać aktualizacje. Prognozy po stronie klienta, o których wspomniał Byte56, zazwyczaj obejmują okresowe aktualizacje dotyczące pozycji i prędkości klienta. Następnie klient interpoluje lokalnie między nimi, używając czegoś w rodzaju splajnu sześciennego .
Drugim problemem, który łączy się z poprzednimi, jest to, że UDP nie ma gwarantowanej dostawy. Nie możesz być pewien, że każda wysłana wiadomość dotrze, a nawet dotrze we właściwej kolejności. Możesz wysłać pakiety 1, 2 i 3, a serwer otrzyma 3, a następnie 1, a nie 2. Często będą one w odpowiedniej kolejności i często będą przychodzić, ale nie zawsze. Potrzebujesz systemu odpornego na utratę pakietów. Wystarczy prosty system potwierdzania. Zazwyczaj używane jest pole bitowe informujące drugi węzeł, czy otrzymał ostatnie 32 wiadomości (dla 32-bitowej liczby int) i jaka była ostatnia otrzymana wiadomość. W ten sposób możesz oznaczać wiadomości jako krytyczne lub nie i wysyłać je ponownie, jeśli nie otrzymają krytycznych. Jest tutaj całkiem przyzwoita dyskusja na ten temat .
Musisz również pamiętać, że robiąc to, Twoi klienci nie będą ze sobą zsynchronizowani. Każdy będzie pokazywał innym graczom interpolację między ich poprzednimi dwiema ramkami sieci podczas pracy nad kolejną, w najlepszym przypadku. Potrzebujesz więc systemu, który weźmie pod uwagę (dość) to, że to, co gracz zobaczył podczas wykonywania akcji, nie było faktycznym stanem gry, kiedy to zrobił. Reagował na stary, niezsynchronizowany stan gry.
Wreszcie, jeśli ta gra ma być konkurencyjna, musisz również martwić się oszukiwanie. Dlatego, w możliwym zakresie (i uzasadnionym), musisz nie ufać klientom i sprawdzić, czy ich działania były możliwe. „Nie, nie możesz przejść przez tę ścianę. Nie, nie możesz chodzić szybciej niż prędkość biegu. Nie, nie możesz twierdzić, że trafiłeś w ten cel.” itp.
Aby uzyskać więcej pomysłów, polecam przeglądanie innych artykułów w tym drugim łączu.
Powodzenia!
źródło
To, co opisujesz, jest zasadniczo prognozą po stronie klienta , ale nie mówisz, co się stanie, gdy serwer i klient się nie zgodzą.
Ewoluował od czasów, gdy klient był tylko głupim terminalem, wysyłając swoje dane do serwera, a serwer powiedział klientowi o wyniku. Jednak gdy gry przeniosły się poza sieć LAN (i często wcześniej), w tej sytuacji zauważalne było opóźnienie. To, co opisujesz, przewidywanie po stronie klienta zostało wprowadzone, aby to naprawić. Teraz klient symuluje również ruch podczas oczekiwania na odpowiedź serwera. Następnie dochodzą do porozumienia w sprawie wyniku. Serwer jest autorytetem.
Kolejne kroki to sposób, w jaki reagujesz na nieporozumienia. Jeśli nie masz nic do tego, dostaniesz gumowanie lub teleportowanie klienta, gdy klient i serwer się nie zgodzą.
źródło
Wyczucie czasu. Inne odpowiedzi nie wspominają o czasie zdarzeń na serwerze i różnych klientach. W zależności od gry może to być coś, na co należy uważać. Opóźnienie (inaczej Lag) wprowadza zmienne opóźnienie między momentem wysłania pakietu a momentem jego otrzymania. Czas może być trudny, ale postaram się wyjaśnić potencjalne problemy najlepiej, jak potrafię. Istnieje kilka gier, które można obejść bez martwienia się o to, ale oto prosty przykład, w którym może powodować problemy.
Załóżmy, że każdy działa na pakiety, jak tylko dotrą.
Załóżmy, że czas 0 (T0) i T1 są blisko siebie.
To, co będzie dalej, zależy od tego, kto na to patrzy, oraz od kolejności, w której pakiety docierają na serwer. Serwer powinien zawsze mieć ostatnie słowo. JEŻELI serwer otrzyma pakiety w podanej wyżej kolejności, to serwer zastosuje tarczę przed strzałem i gracz 1 (P1) przeżyje. Z perspektywy P1, po założeniu tarczy sam zobaczy tarczę przed strzałem i przeżyje. Ale co z P2? Od razu zobaczą strzał, ponieważ strzelili, ale czy najpierw zobaczą tarczę? Jeśli
(T0 + latency between P1 and the server + the latency between the server and P2) > T1
po strzale pojawi się tarcza, a P2 pomyśli, że ich strzał zabił P1. Serwer będzie musiał jakoś skorygować tę sytuację.Jeśli jednak serwer odbierze pakiety w odwrotnej kolejności (co jest całkiem możliwe, nawet jeśli T0 <T1), nastąpi odwrotność. P1 jest zły (i martwy), a P2 jest poprawny (i zwycięski).
Jest kilka sposobów radzenia sobie z takimi sytuacjami, ale najłatwiej jest pozwolić serwerowi zrobić wszystko. Możesz spróbować to zasymulować na kliencie, ale nie zezwalaj na żadne stałe działania, takie jak śmierć. Gracze będą musieli poczekać na ważne potwierdzenia zmiany gry z serwera.
Wysyłanie znacznika czasu z działaniami może być korzystne. JEŻELI w większości ufasz klientom, możesz użyć tych znaczników czasu, aby określić, które działanie nastąpiło jako pierwsze, zamiast postępować zgodnie z naszym pierwszym założeniem. Może to być trudne, ponieważ odbieranie wiadomości z przeszłości zwykle oznacza, że musisz mieć możliwość cofnięcia czasu.
Czas jest fajny, prawda? Niezależnie od tego, co ostatecznie zrobisz, warto zdawać sobie sprawę z tych problemów czasowych.
źródło