Krzysztof Pianta, Code with me. Zostań game developerem

Dzisiaj w WebKrytyku przyjrzymy się książce Krzysztofa Pianty Code with me. Zostań game developerem.

W przypadku tej książki wypada zacząć od kwestii dość podstawowej: jej logicznego podziału. Książka ma 352 strony i jest podzielona na 8 rozdziałów. Problem w tym, że rozdział pierwszy kończy się… na stronie 280. Praktycznie 80% książki zajmuje jeden rozdział. Pozostałe wydają się doczepione nieco na siłę i mocno ogólnikowe, np. rozdział 6. to jedna strona tekstu. Dysproporcja pomiędzy ilością treści w pierwszym rozdziale a reszcie jest wręcz gigantyczna.

Co zatem zawiera ten pierwszy rozdział? Tak w zasadzie to wszystko i nic – to zbiór niepowiązanych ze sobą przykładów rozwiązań problemów, na które można się natknąć w czasie tworzenia prostych gier przy wykorzystaniu JavaScriptu. O wiele sensowniej byłoby rozbić ten rozdział na kilka mniejszych, w których przykłady były pogrupowane tematycznie. A tak jeden przykład opisuje, jak zaimplementować strzelanie w grze z rzutem z góry, a następny, jak działa grawitacja w platformówce. Na samym końcu natomiast tworzymy z kilku takich przykładów działającą grę (po prostu sklejając kod z przykładów razem). Zresztą to, że te przykłady są tak małe, by można je było po prostu kopiować i wklejać, tworząc w ten sposób nowe projekty (które mogą być komercyjne), jest powtórzone w książce wiele razy – zbyt wiele… Czytając to, odnosiłem wrażenie, że to jakaś kompilacja odpowiedzi ze Stack Overflow. Niby pokazywane były rozwiązania konkretnych problemów, ale tłumaczenie podjętych decyzji programistycznych było niezwykle lapidarne. A mówimy o jedynym rozdziale w książce, w którym było wystarczająco miejsca na dokładne opisy!

W książce jest także relatywnie dużo literówek i błędów językowych. Najbardziej zapadły mi w pamięć funkcja animowana (zamiast anonimowej) oraz branie na tapetę, podczas gdy brać powinno się na tapet. Zdarzył się też ucięty podrozdział (1.6.3), w którym zapowiedziano pokazanie funkcji kopii zapasowych w Notepadzie++, po czym… nastąpiło przejście do kolejnego rozdziału. W niektórych miejscach są odwołania do jakichś wcześniejszych fragmentów (np. o zabezpieczeniach przeglądarki), ale te fragmenty nie istnieją. Ogólnie odnosi się wrażenie, jakby książka była składana na szybko.

Niemniej największą bolączką tej książki jest spora liczba błędów rzeczowych oraz archaiczne wręcz podejście do JS-a. Zacznijmy od tych pierwszych, bo już w podrozdziale 1.1.1 pojawia się błąd, który jest następnie powtarzany w rozdziale 2:

[…] w skład HTML5 wchodzi JavaScript + HTML + CSS […]

s. 8

W rozdziale 2. wręcz padają słowa, że dotąd posługiwaliśmy się jedynie HTML5, ale teraz przyda się znajomość… HTML-a. Tylko że to przecież jest ten sam język! HTML5 to jest HTML i tylko HTML. CSS i JS nigdy nie były częścią HTML5. JS to zlepek standardu ECMAScript oraz poszczególnych Web APIs. CSS z kolei jest rozwijany w modułach. HTML to osobna specyfikacja. No i najważniejsze – HTML5 praktycznie już nie istnieje. HTML5 jako buzzword oznaczający nowe technologie sieciowe wypadł z użycia lata temu.

To niejedyny błąd rzeczowy w tej książce. Z innych zapadło mi w pamięć choćby twierdzenie, że liczby ujemne są ewaluowane do fałszu. Otóż nie, liczby ujemne są rzutowane na true. Jedyną wartością liczbową, która jest rzutowana na false, jest zero. W innym miejscu z kolei łączy się ze sobą składnię spread z rest parameters – a są to dwa zupełnie oddzielne mechanizmy.

Sam kod JS z kolei wygląda jak napisany dobrych kilka lat temu (a przy okazji dziwnie przypomina mi kod C++). Używanych jest tutaj raptem kilka ficzerów z ES6+ (m.in. składnia spread czy Promise), wszędzie używane są var, a wśród dobrych praktyk wymienia się przypisanie this do self czy otaczanie kodu funkcją anonimową. Obecnie te praktyki można z powodzeniem zastąpić o wiele lepszymi rozwiązaniami: funkcjami strzałkowymi oraz pełnoprawnymi modułami.

Co więcej, jest nawet podrozdział poświęcony różnicom pomiędzy JS a… jQuery. Problem w tym, że ta dyskusja toczyła się dobrych 10 lat temu i obecnie nie ma sensu jej toczyć. Zwłaszcza, że porównanie jQuery do JS-a jest mocno naciągane, bo porównuje się moduł Ajax do starego, dobrego XMLHttpRequest, zamiast do nowszego Fetch API. Swoją drogą Ajax to kolejny błąd rzeczowy w książce, bo to nie jest akronim zapisywany jako AJAX.

Chwilę zatrzymałbym się także na uwadze autora o tym, że nie lubi korzystać z bibliotek, bo w każdej chwili mogą przestać być rozwijane lub staną się nagle płatne. Takie myślenie jest groźne o tyle, o ile prowadzi do syndromu NIH. Zresztą sytuacje, w których darmowe oprogramowanie stawało się nagle płatne, również miały już miejsce. Wówczas wszystko zależy od licencji i dostępności kodu źródłowego, np. wow.js zmieniło licencję z MIT na bardziej restrykcyjną, która de facto w wielu przypadkach wymuszała kupno komercyjnej licencji. Problem rozwiązano prosto: sforkowano bibliotekę.

A skoro już przy forkach jesteśmy, warto też wspomnieć, że książka pokazuje naprawdę złe praktyki w kwestii jakości i utrzymania kodu. Po pierwsze, zamiast wprost zasugerować używanie systemu kontroli wersji (takiego jak git) od samego początku tworzenia gry, proponuje się system ręcznych backupów. Po prostu po każdym dniu pracy miałaby powstawać kolejna kopia gry w katalogu z datą. To jest wręcz książkowy antyprzykład rozwiązania tego problemu, do którego rozwiązania przygotowano właśnie systemy kontroli wersji. Ręczne backupy nie mają absolutnie żadnych plusów względem takiego gita. Po drugie, w książce spójność kodu jest wspomniana raz, w cudzysłowie, jako coś, co „niektórzy lubią”. I faktycznie, autor niespecjalnie przejmuje się spójnością w kodzie… a powinien. Spójność kodu to praktycznie jedyny sensowny sposób na przyjemną pracę w zespole. Nie bez powodu powstały rozwiązania typu ESLint czy Prettier, a zażarte dyskusje nad wyższością 2 spacji nad tabulatorem rozpalają fora od początków Internetu.

I na koniec kilka smaczków z JS-a. Przez całą książkę wykorzystywane jest wyciekanie elementów z [id] do globalnej przestrzeni nazw. Zamiast const gra = document.querySelector( '#gra' ); jest wykorzystywana po prostu globalna zmienna gra. Ten mechanizm to pozostałość po naprawdę archaicznych czasach i nie ma najmniejszego powodu, żeby go obecnie stosować. Zwłaszcza, że żyjemy w erze modularnego JS-a, a w nim zmienne globalne wyglądają po prostu dziwnie. To zamiłowanie do używania starych technik dziwi mnie tym bardziej, gdy obok nich jest przykład użycia AudioContext (które, wbrew temu, co twierdzi książka, nie pozwala na używanie kamery z poziomu strony WWW) – dość nowego i niszowego API.

Z kolei przy opisywaniu obsługi klawiatury informacje o wciśniętych klawiszach są zapisywane do tablicy. Problem w tym, że tablica ta jest traktowana jak tablica asocjacyjna – a takich w JS-ie nie ma. Fakt, że to działa (czyli da się dodać do tablicy nieliczbowy klucz), wynika wyłącznie z tego, że tablice w JS-ie są równocześnie obiektami. A te mogą mieć własne własności. Niemniej tak się nie pisze kodu JS. Jeśli chcemy wykorzystywać nieliczbowe klucze, powinniśmy użyć albo Map/Set, albo zwykłego obiektu.

Jednak chyba największe zastrzeżenie mam do stosowania pętli renderującej opartej na setInterval i odpalanej co ok. 16 ms (1000 / 60). Od lat jest to zła praktyka i o wiele lepszym rozwiązaniem jest stworzone do takich celów requestAnimationFrame. Ono co prawda jest wspomniane, ale dopiero po wszystkich przykładach i nie do końca jest wyjaśnione, dlaczego powinno się go używać. Zwłaszcza, że informacja o tym, że requestAnimationFrame odpala się w 60 FPS-ach nie jest prawdziwa. Nie ma pewności, że ta funkcja odpali się co 16 ms, bo odpala się tylko wtedy, gdy przeglądarka jest w stanie wyrenderować nowy obraz. A to sprawia, że przy mocnym sprzęcie i wolnym głównym wątku faktycznie uzyskamy 60 FPS-ów, ale w innych wypadkach ramek może być 10, 14, 3 na sekundę… Dlatego tak niezwykle istotna jest technika delta time.

Ta książka na stronie Heliona jest reklamowana m.in. tak:

Nie zaglądaj tu, nie warto! Stracisz tylko czas, na sto procent nie dowiesz się niczego ciekawego, znudzisz się i będziesz rozczarowany, bo… z pewnością nie chcesz dołączyć do prawdziwej elity programistów, zdobyć poszukiwanych na rynku umiejętności, nauczyć się czegoś naprawdę ekscytującego ani uzyskać wpływu na jedną z najdynamiczniej rozwijających się gałęzi przemysłu komputerowego, prawda?

Nie mogę się z tym opisem zgodzić. Umiejętności, jakich uczy ta książka, były dobre w roku 2008, dzisiaj pisanie aplikacji i gier w JS-ie wygląda zupełnie inaczej. Jedyne, co można by z tej książki wyciągnąć, to sposoby rozwiązań konkretnych problemów gamedevowych (jak implementacja grawitacji). Tylko że, jak już wspominałem, rozwiązania te są opisane bardzo pobieżnie, w myśl powtarzanej zasady, że mają być gotowymi klockami do budowy gier przez kopiuj-wklej.

Coś przy powstawaniu tej książki mocno zawiodło, bo panuje tutaj chaos nie tylko w samej strukturze treści (nieproporcjonalne rozdziały, ucięte podrozdziały, liczne literówki i błędy językowe), ale i w jej warstwie merytorycznej, zawierającej sporo bardzo podstawowych błędów rzeczowych (liczby ujemne rzutowane na false, traktowanie HTML5 jako czegoś innego niż HTML, korzystanie z tablic jak z tablic asocjacyjnych…). Naprawdę odnoszę wrażenie, że zabrakło czasu na doszlifowanie wszystkiego i ostateczne przeczytanie całości.

To najlepsza książka o przeglądarkowym gamedevie… 2008 roku. Ale obecnie, niestety, mamy rok 2020.

3 myśli w temacie “Krzysztof Pianta, Code with me. Zostań game developerem”

  1. Korzystasz z visual studio code? Jeśli tak to możesz mi napisać dlaczego gdy wpisuje log i pojawia się console.log() to nie podpowiada mi zmienny w tych okrągłych nawiasach console.log()? Ale gdy wpisze console.log() ręcznie, to podpowiadanie zmiennych działa.

      1. Wpisuje skrót log pojawia się console.log() w środku tych nawiasów jest kursor i wpisuje nazw zmiennej i nie podpowiada jej. Dopiero jak kliknę i przestawię kursor w inne miejsce kodu i powrócę nim do tych nawiasów, wtedy podpowiadanie składni działa.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.