Jak często aktualizować klienta gry o świecie?


10

Korzystając z socket.io , mam komunikację podobną do innych MMORPG, stałe połączenie z wiadomościami.

W moim dotychczasowym projekcie klient przesyła pozycję odtwarzacza i ramkę animacji z każdą ramką aktualizacji. Gdy serwer otrzyma ten komunikat, rozgłasza go wszystkim klientom, którzy następnie odpowiednio przesuwają grafikę.

Czy lepiej byłoby „zbierać” je i transmitować, powiedzmy, co 1/10 sekundy?

Ponadto, czy klient powinien wysyłać wiele różnych wiadomości (zdobytych exp, klikniętych na przedmiot), gdy tylko się pojawią, czy raczej tylko jeden odebrany? Pierwszy zostałby wdrożony łatwiej.

Odpowiedzi:


16

Podejdę do tego z dyskusji na wysokim szczeblu, a następnie pracuję nad Twoimi pytaniami. Ze względu na ujawnienie, nie mam osobistego doświadczenia z używaniem socket.io, ale dużo ekspozycji na przestrzeń problemową w odniesieniu do MMORPG.

Projektowanie architektury sieci silnika MMORPG i / lub wybór projektu oprogramowania pośredniego lub open source w celu zapewnienia funkcjonalności jest jedną z trudniejszych decyzji, na które wpływ ma projekt gry, budżet i wiedza techniczna zespołu. Ostateczny wybór wpłynie na inne decyzje architektoniczne (a czasem także decyzje projektowe).

Jako twórcy gier MMORPG planujemy wielki sukces (często nazywany także katastrofalnym sukcesem), gdy duża liczba wyzwala lampki ostrzegawcze i syreny. Jedną z przerażających dużych liczb, które się pojawiają, są algorytmy, które są kwadratem N (dalej N ^ 2), w twoim pytaniu pierwszą rzeczą, która wyskoczyła do mnie, jest to, że brzmiało to tak, jakby projekt wymagał by jednostka transmitowała informacje, aby wszystkie inne powiązane podmioty. To klasyczny przykład problemu N ^ 2.

MMO zazwyczaj podchodzą do rozwiązywania problemów N ^ 2, atakując problem na kilka różnych sposobów; systemy świadomości (sytuacyjne, przestrzenne itp.), w których istota jest świadoma niektórych podzbiorów wszystkich innych bytów, podział graczy na różne „odłamki”, podział graczy na „strefy” i / lub instancja, wdrażanie mechaniki gry, która zniechęca zbyt wielu gracze ze zgromadzających się razem (burze teleportacyjne Asheron Call) itp.

Większość MMORPG i wiele silników FPS ma dość zaawansowane architektury sieciowe, które obsługują różne funkcje, w tym:

  • niezawodne i zawodne ścieżki komunikacji (TCP, niestandardowe implementacje niezawodnych pakietów UDP i UDP)
  • kształtowanie przepustowości (priorytety, czasy życia itp.)
  • automatyczna replikacja danych terenowych / różnych danych i wywołań funkcji
  • zestawy atomowe danych (tj. dane przekazywane razem)
  • dyskretne aktualizacje (tzn. gdy każde przejście jest ważne)
  • korekcja opóźnień
  • różnorodne sztuczki, aby klient czuł się responsywny

Uważam, że Dokumentacja Unreal Networking i Dokumentacja Valve Networking stanowią dobry grunt pod różnorodne problemy.

Więc teraz przejdźmy do pytań.

Czy lepiej byłoby „zbierać” je i transmitować, powiedzmy, co 1/10 sekundy?

Trudno podać tutaj proste tak lub nie ... ponieważ zależy to od skali (liczby obserwujących bytów), częstotliwości aktualizacji i wielkości aktualizacji. Na przykład zebranie ich wszystkich może być strasznie złe, jeśli rozmiar aktualizacji może gdzieś zniszczyć bufor.

Klient gier MMORPG i FPS jest ogólnie zaprojektowany w taki sposób, że wizualizuje coś, co „wygląda” dobrze, nawet jeśli nie otrzyma aktualizacji o wiele większej liczby ramek aktualizacji niż jest to „normalne”. Korzystając z nierzetelnej komunikacji (UDP), możesz po prostu spodziewać się utraty pewnej liczby aktualizacji w próżni, klienci mogą to zrekompensować, wysyłając częstsze aktualizacje, niż można by użyć z niezawodnym transportem.

Z pobieżnego przeglądu dokumentacji gniazda.io wynika, że ​​obsługuje ona zarówno niezawodne, jak i zawodne (zmienne w terminologii) ścieżki komunikacji.

Najpierw podejdę do tego, zastanawiając się, z jaką częstotliwością potrzebne są aktualizacje ...

Jeśli gracz porusza się po linii prostej ze stałą prędkością, niższa częstotliwość aktualizacji jest w porządku, ponieważ obserwujący klienci mogą z dużą dokładnością przewidzieć, gdzie będzie gracz w dowolnym momencie. Gdy gracz skręca w ciasnym kole lub dokonuje szybkich zmian kierunku, wymagane są znacznie częstsze aktualizacje. I odwrotnie, gdy gracz w ogóle się nie porusza, nie ma powodu, aby w ogóle wysyłać aktualizacje ruchu.

Bez względu na wszystko, prawdopodobnie nie jest (generalnie) konieczne wysyłanie aktualizacji co ramkę od klienta do serwera. Sam serwer może wysyłać wiadomości do każdej ramki w miejscu, w którym je ma, lub opóźniać je (patrz kształtowanie przepustowości, priorytetyzacja i czas życia aktualizacji).

Inne typy aktualizacji mają różne cechy ... na przykład rozważ pole „zdrowia”, które jest modyfikowane, gdy gracz lub stworzenie zostanie uszkodzone. Jednym ze sposobów wdrożenia tego jest rozgłaszanie każdej zmiany natychmiast po jej wystąpieniu, ale prowadzi to do marnowania przetwarzania i przepustowości, jeśli wartość jest zmieniana wiele razy w ramce lub w kolejnych ramkach (architektury sieci, które implementują kształtowanie szerokości pasma rozwiązują ten problem, łącząc aktualizacje do tylko najnowsze są wysyłane do obserwującego klienta, gdy ma on dostępną przepustowość).

czy klient powinien wysyłać wiele różnych wiadomości (zdobytych exp, klikniętych na przedmiot), gdy tylko się pojawią, czy raczej tylko jeden zebrany?

Ponownie, tutaj nie zadziała żadna prosta odpowiedź tak lub nie. W zależności od tego, co dokładnie masz na myśli przez zebrane ... oba mogą być właściwe w różnych okolicznościach, a także zależą od implementacji warstwy sieciowej.

Zbieranie wiadomości dla określonej jednostki, która ma zostać wysłana, ponieważ jedna wiadomość może (w zależności od implementacji) zmniejszyć obciążenie przepustowości związane z wysyłaniem wiadomości (redukując koszty) odwrotnie (w zależności od implementacji, takiej jak mapowanie pola / wartości przekazywane przez łańcuchy) może zwiększyć przepustowość wymagania w porównaniu do prostszego określonego typu wiadomości.

Przeglądając dokumentację socket.io, wydaje mi się, że nadwyżka wiadomości znajduje się na wyższym końcu spektrum, co sprzyja gromadzeniu aktualizacji do wysłania jako zbiorczej wiadomości, w przeciwieństwie do wielu pojedynczych aktualizacji.

Polecam przejrzenie wszystkich aktualizacji, które rozważasz replikować, na przykład większość gier MMORPG i FPS nie zadaje sobie trudu, aby wysłać graczowi kliknięcie zdarzenia X do obserwujących klientów, chyba że spowodowałoby to zmianę stanu dla obiektu, o którym byli również świadomi .


3
+1 Świetna odpowiedź. Dodatkowo sugerowałbym rozpoczęcie od prostej i optymalizację od tego miejsca. Wiadomości spamowe, oszalej, i zobacz, jak przepustowość zaczyna maleć. Dlatego dobrze jest przeprowadzić etapowe testy warunków skrajnych. Wdrażasz możliwie najbardziej naiwne podejście i przeprowadzasz testy; dokonujesz optymalizacji; ponownie test warunków skrajnych, dalej optymalizujesz, płucz, powtarzaj. Jeśli wdrożenie optymalizacji pierwszego dnia jest całkowicie trywialne, śmiało; ale pamiętaj, że nawet najbardziej trywialne optymalizacje mogą zawierać założenia, które później mogą zostać obalone w środowisku produkcyjnym.
Inżynier

5

Oto prawdziwy przykład: RuneScape „tyka” raz na ~ 0,6 sekundy. Możesz przeczytać o tym tutaj . Wyobrażam sobie, że upraszcza to z ich perspektywy, ponieważ w swoich skryptach prawdopodobnie określają czasy i opóźnienia w tikach zamiast w milisekundach. I oczywiście, przy tak dużym / wolnym tempie tykania, użytkownicy z powolnymi połączeniami nie są w bardzo niekorzystnej sytuacji w porównaniu z innymi graczami.


0

Jednym ze sposobów działania jest oddzielenie rzeczywistych zmian danych od emisji tych zmian. Kiedy obiekt się zmienia, dokonaj modyfikacji i ustaw „brudną” flagę, a gdy przyjdzie czas na nadanie jakichkolwiek zmian, wyślij tylko dane dla oznaczonych obiektów i usuń flagę. Wielokrotnie zmieniane wartości są automatycznie scalane, ponieważ aktualizacja wysyła stan tylko w momencie emisji. Możesz także oflagować podobiekty lub pojedyncze właściwości, aby nadawać tylko to, co się zmieniło.

Aha, i zwykle nie wysyłałeś ramek animacji na serwer lub do innych klientów. Dokładny stan animacji jest zwykle uważany za szczegół prezentacji i pozostawiony każdemu klientowi do rozwiązania, a zamiast tego upewniasz się, że każdy klient wie, która animacja jest obecnie odtwarzana.


Nie zrobiłbym tego? Co z pozami czy coś, muszę upewnić się, że wszyscy klienci widzą to samo?
Lanbo,

Niepraktyczne jest upewnianie się, że każdy klient widzi dokładnie to samo, ponieważ informacje potrzebują czasu na podróż, różni klienci mają różne opóźnienia sieciowe, renderują przy różnych prędkościach itp. Więc próba nakłonienia wszystkich do wyświetlenia dokładnie tej samej ramki jest zwykle daremne przedsięwzięcie. Zamiast tego możesz po prostu spróbować upewnić się, że uruchamiają tę samą animację mniej więcej w tym samym czasie, i to wystarczy.
Kylotan,
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.