Vanar.io
Koniec sesji, upały oraz skręcona kostka poskutkowały nadmiarem wolnego czasu, więc postanowiłem spróbować czegoś nowego. Chociaż właściwie jako bordo i tak bym to napisał bez względu na okoliczności, bo po prostu lubię programować ;) Ostatnie projekty na studiach wybitnie mnie zanudziły, więc szukałem sobie czegoś przyjemnego i rozwijającego. Jako że gry multiplayer w przeglądarce ostatnio są całkiem popularne, nigdy czegoś takiego nie pisałem i lubię tworzyć aplikacje sieciowe, wybór był dość prosty. W efekcie powstała gra //vanar.io, w której wcielasz się w kosmonautę, zasiadasz za sterami statku kosmicznego i niszczysz innych. Na pierwszy rzut oka nic skomplikowanego, ale nie o to chodziło.
//vanar.io
Dlaczego taka gra?
Nie pasjonuję się grami na tyle, żeby tworzyć coś o rozbudowanej fabule, nie jestem też grafikiem. Jako programista szukałem czegoś, co koncepcyjnie będzie banalnie proste, lecz nietrywialne do napisania. Przypomniało mi się, że z dobre 10 lat temu w jakiejś starej dystrybucji Mandrake'a była dołączona gra, w której latało się statkiem kosmicznym i strasznie mnie wciągnęła. A więc padło na dynamiczną grę multiplayer i patrząc z perspektywy czasu, była to dość dobra decyzja - problemów było sporo i był to całkiem niezły wstęp do gamedevu. Wyszło około 13 000 linii kodu, więcej niż się spodziewałem.
Wybór technologii
Tutaj sprawa była prosta - skoro gra po stronie klienta działa w JavaScripcie a spora część kodu miała być współdzielona, najwygodniej na serwerze użyć tego samego - czyli NodeJS. Do tego jeszcze Redis do trzymania informacji o podłączonych serwerach, jakieś reverse-proxy i można działać.
Problemy z wydajnością
Niby V8 i inne nowe silniki JavasScript są szybkie, ale nadal zupełnie się nie nadają do takiej ilości obliczeń. Uzyskanie płynnej animacji wymagało spędzenia prawie tygodnia na jsperf.com (chwilowo leży, trzeba wchodzić przez cache google), przepisywania każdego fragmentu kodu po kilka-kilkanaście razy i użycia masy różnych sztuczek żeby poprawić wydajność i zminimalizować czas działania garbage collectora. Zaskoczeniem było, że po stronie serwera udało mi się uzyskać prawie dwukrotny wzrost wydajności, gdy zacząłem intensywnie używać nowości z EcmaScript 6. Tak czy inaczej strasznie syzyfowa praca, ale na szczęście jak się już raz przez to przejdzie, to później już się pamięta czego się wystrzegać...
Sam canvas również nie powala wydajnością, zwłaszcza gdy włączy się tryb pełnoekranowy. Szybkie rysowanie dynamicznych obiektów jest właściwie niemożliwe i wszystko trzeba intensywnie cache'ować. Może sytuacja się poprawi wraz z rozwojem WebGL.
Problemy z lagami
To bardzo fajny temat i jedno z głównych wyzwań w tym projekcie. W internecie można znaleźć sporo ciekawych artykułów jak radzono sobie z tym w Quake'u (który podobno przecierał szlaki) i jak się to robi obecnie. Trochę zdziwił mnie na początku fakt, że model symulacji
//vanar.io najbardziej przypomina ten z fpsów i ostatecznie skorzystałem z podobnych tricków. Jakby ktoś chciał o tym więcej poczytać to dobry wstęp daje ten artykuł:
//www.gabrielgambetta.com/fpm1.html
Boli mnie tylko, że w moim przypadku do skaczącego pingu dokłada się jeszcze podła wydajność JavaScriptu i jednowątkowe przetwarzanie danych. Pakiety są przetwarzane z jeszcze większym, mocno losowym opóźnieniem a przy dynamicznych zmianach przewidywanie stanów nie działa tak dobrze jak powinno i czasami widać lekkie lagi nawet na dobrym łączu.
Wnioski
Przyjemnie się to pisało, ale przy obecnej wiedzy pewnie nie zdecydowałbym się na napisanie takiej gry. Jest zbyt dynamiczna jak na możliwości JavaScriptu i mam wrażenie, że nie działa tak dobrze jak powinna. Ale może to tylko ja jestem przewrażliwiony, bo gapiłem się na to przez ostatnie 2.5 tygodnia ;) Chętnię usłyszę Wasze opinie i odpowiem na pytania.
I na koniec bonus dla graczy CS:GO i innych fpsów
Nie używajcie magicznych, "poprawiających" wydajność configów, które wyłaczają interpolację i inne mechanizmy walczące z lagami. Na pierwszy rzut oka może wyglądać to lepiej, ale w praktyce będziecie znacznie rzadziej trafiać przeciwnika (mimo animacji trafienia, którą zobaczycie na ekranie). Jednym z podstawowych tricków przy pisaniu dynamicznych gier jest wyświetlanie przeciwników z lekkim opóźnieniem, dzięki czemu nie trzeba zgadywać gdzie przeciwnik znajdzie się w następnej klatce, gdyby pakiety przyszły po czasie. Efekt jest taki, że zawsze strzela się do pozycji graczy z przeszłości a nie ich obecnego położenia. Serwer otrzymując pakiet informujący o strzale nie bierze pod uwagę stanu teraźniejszego, lecz odtwarza stan z przeszłości - tak jak to widział klient - na podstawie czasu wysłanego w pakiecie. Jeżeli wyłączycie interpolację możecie widzieć inny stan, niż ten odtworzony przez serwer - i nie będziecie trafiać. Warto też wspomnieć o jeszcze jednym skutku tego mechanizmu: nawet nie mając lagów, możecie zostać zabici zaraz po tym jak schowacie się za ścianą - ktoś mógł trafić w odtworzonym stanie w przeszłości.
O tym jak to jest zaimplementowane w CSie można poczytać tutaj:
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
Komentarze (209)
najlepsze
Ponadto rozumiem że korzystałes z io.js ? (AFAIR node.js nadal nie wspiera ES6)
io.js nigdy nie używałem, node od którejś wersji wspiera ES6 z przełącznikiem --harmony a od jakiejś jeszcze nowszej już standardowo. Dokładnie się nie orientuję.
ah wait! nawet gdy pracuję to robię!
Wyłączyłem adblocka na twojej stronie ( ͡° ͜ʖ ͡°)
Komentarz usunięty przez moderatora
Hmmm. Choć z drugiej strony obecnie najlepszą taktyką jest stać w miejscu, kręcić się i strzelać do latających. Bonusy mogłyby zachęcić do ruszenia dupska, o ile nie pojawiałyby się obok gracza.
-na młodym etapie każda cywilizacja jest bardzo agresywna, szybciej jest rozwinąć się cudzym kosztem, czyli podbój i przejęcie technologii oraz surowców (w tym przypadku punktów :) )
- w
Poza tym coś z tłem nie tak.