Zignoruj czas reakcji. W sieci LAN ping jest nieznaczny. W Internecie 60-100 ms w obie strony jest błogosławieństwem. Módlcie się do bogów lagów, abyście nie dostali skoków> 3K. Twoje oprogramowanie musiałoby działać z bardzo małą liczbą aktualizacji / s, aby mógł to stanowić problem. Jeśli wykonujesz zdjęcia z szybkością 25 aktualizacji / s, masz maksymalnie 40 ms pomiędzy otrzymaniem pakietu a działaniem na nim. I to jest jednowątkowa obudowa ...
Zaprojektuj swój system pod kątem elastyczności i poprawności. Oto mój pomysł na podłączenie podsystemu sieciowego do kodu gry: Wiadomości. Rozwiązaniem wielu problemów może być „przesyłanie wiadomości”. Myślę, że wiadomości wyleczyły raka u szczurów laboratoryjnych. Wiadomości oszczędzają mi 200 USD lub więcej na moim ubezpieczeniu samochodu. Ale poważnie, przesyłanie wiadomości jest prawdopodobnie najlepszym sposobem na dołączenie dowolnego podsystemu do kodu gry przy jednoczesnym utrzymaniu dwóch niezależnych podsystemów.
Wiadomości należy używać do wszelkiej komunikacji między podsystemem sieciowym a silnikiem gry oraz w tym przypadku między dowolnymi dwoma podsystemami. Przesyłanie komunikatów między podsystemami może być proste jak kropla danych przekazywanych przez wskaźnik za pomocą std :: list.
Wystarczy mieć kolejkę wiadomości wychodzących i odniesienie do silnika gry w podsystemie sieciowym. Gra może zrzucać wiadomości, które chce wysłać do kolejki wychodzącej, i automatycznie wysyłać je automatycznie, a może po wywołaniu funkcji „flushMessages ()”. Jeśli silnik gry ma jedną dużą, współdzieloną kolejkę komunikatów, wówczas wszystkie podsystemy, które musiały wysyłać wiadomości (logika, sztuczna inteligencja, fizyka, sieć itp.), Mogą zrzucić do niej wszystkie wiadomości, w których główna pętla gry może następnie odczytać wszystkie wiadomości i działaj odpowiednio.
Powiedziałbym, że uruchamianie gniazd w innym wątku jest w porządku, choć nie jest wymagane. Jedynym problemem związanym z tym projektem jest to, że jest on generalnie asynchroniczny (nie wiadomo dokładnie, kiedy pakiety są wysyłane) i może to utrudnić debugowanie i sprawić, że problemy związane z czasem pojawią się / znikną losowo. Mimo to, jeśli wykonane prawidłowo, żadne z nich nie powinno stanowić problemu.
Z wyższego poziomu powiedziałbym osobne połączenie sieciowe z samym silnikiem gry. Silnik gry nie dba o gniazda ani bufory, dba o zdarzenia. Zdarzenia to takie rzeczy, jak „Gracz X oddał strzał” „Wybuch w grze T”. Mogą być one interpretowane bezpośrednio przez silnik gry. Nie ma znaczenia, skąd są generowane (skrypt, działanie klienta, odtwarzacz AI itp.).
Jeśli traktujesz swój podsystem sieciowy jako sposób wysyłania / odbierania zdarzeń, zyskujesz szereg korzyści w porównaniu z wywoływaniem recv () na gnieździe.
Można na przykład zoptymalizować przepustowość, biorąc na przykład 50 małych wiadomości (o długości 1-32 bajtów), a podsystem sieciowy pakuje je w jeden duży pakiet i wysyła. Może to skompresuje je przed wysłaniem, jeśli to była wielka sprawa. Z drugiej strony kod może ponownie rozpakować / rozpakować duży pakiet do 50 dyskretnych zdarzeń, aby silnik gry mógł go odczytać. Wszystko to może odbywać się w przejrzysty sposób.
Inne fajne rzeczy to tryb gry dla jednego gracza, który ponownie wykorzystuje kod sieci, mając czystego klienta + czysty serwer działający na tej samej maszynie, komunikujący się za pośrednictwem wiadomości w pamięci współdzielonej. Następnie, jeśli Twoja gra dla pojedynczego gracza działa poprawnie, działałby również zdalny klient (tj. Prawdziwy multiplayer). Ponadto zmusza Cię do rozważenia z wyprzedzeniem, jakie dane są potrzebne klientowi, ponieważ gra dla jednego gracza wyglądałaby dobrze lub byłaby całkowicie błędna. Miksuj i dopasowuj, uruchamiaj serwer ORAZ zostań klientem w grze wieloosobowej - wszystko działa równie łatwo.
Nie ty nie.
Nie musi to mieć znaczenia. Kiedy twoja logika się aktualizuje? Nie ma sensu ściągać danych z sieci, jeśli nie możesz nic z tym zrobić. Podobnie nie ma sensu odpowiadać, jeśli nie masz jeszcze nic do powiedzenia.
Jeśli Twoja gra jest tak szybka, że oczekiwanie na wyświetlenie następnej klatki jest znaczącym opóźnieniem, oznacza to, że będzie wysyłała wystarczającą ilość danych, że nie musisz wysyłać osobnych pakietów ACK - po prostu dołącz wartości ACK do swoich normalnych danych ładunki, jeśli w ogóle ich potrzebujesz.
W przypadku większości gier sieciowych istnieje możliwość stworzenia takiej pętli:
Możesz oddzielić aktualizacje od renderowania, co jest wysoce zalecane, ale wszystko inne może pozostać takie proste, chyba że masz konkretną potrzebę. Dokładnie jaką grę tworzysz?
źródło
To wcale nie jest prawda. Wiadomość przechodzi przez sieć, podczas gdy odbiornik renderuje bieżącą ramkę. Opóźnienie sieci jest ograniczone do pełnej liczby ramek po stronie klienta; tak - ale jeśli klient ma tak mało FPS, że jest to wielka sprawa, mają większe problemy.
źródło
Komunikacja sieciowa powinna być grupowana. Powinieneś dążyć do tego, aby pojedynczy pakiet wysyłany był przy każdym tyknięciu gry (co często zdarza się, gdy ramka jest renderowana, ale tak naprawdę powinna być niezależna).
Twoje podmioty gry rozmawiają z podsystemem sieci (NSS). NSS grupuje wiadomości, ACK itp. I wysyła kilka (miejmy nadzieję) optymalnych rozmiarów pakietów UDP (zwykle ~ 1500 bajtów). NSS emuluje pakiety, kanały, priorytety, ponownie wysyła itp., Jednocześnie wysyłając tylko pojedyncze pakiety UDP.
Przeczytaj poradnik na temat gier lub po prostu skorzystaj z ENet, który implementuje wiele pomysłów Glenna Fiedlera.
Lub możesz po prostu użyć TCP, jeśli twoja gra nie wymaga reakcji drgań. Następnie wszystkie problemy związane z grupowaniem, wysyłaniem i ACK znikają. Jednak nadal chciałbyś, aby NSS zarządzał przepustowością i kanałami.
źródło
Nie całkowicie „ignoruj czas reakcji”. Dodanie kolejnych opóźnień 40 ms do już opóźnionych pakietów niewiele zyskuje. Jeśli dodajesz kilka klatek (przy 60 klatkach na sekundę), opóźniasz przetwarzanie pozycji, zaktualizuj kolejną parę klatek. Lepiej jest szybko akceptować pakiety i szybko je przetwarzać, aby zwiększyć dokładność symulacji.
Odniosłem wielki sukces w optymalizacji przepustowości, myśląc o minimalnych informacjach o stanie potrzebnych do przedstawienia tego, co jest widoczne na ekranie. Następnie patrząc na każdy kawałek danych i wybierając dla niego model. Informacje o pozycji mogą być wyrażane jako wartości delta w czasie. Możesz albo użyć do tego własnych modeli statystycznych i spędzić wieki na debugowaniu ich, albo możesz skorzystać z biblioteki, która ci pomoże. Wolę używać modelu zmiennoprzecinkowego tej biblioteki DataBlock_Predict_Float Ułatwia to optymalizację przepustowości wykorzystywanej na wykresie sceny gry.
źródło