Transkrypcja
Przypomnienie i errata
Dzisiaj kontynuujemy bardzo ciekawy temat! Nasza rozmowa skupia się na algorytmach i podejściu data driven w kontekście gier komputerowych.
Na początek wróćmy do tematu z poprzedniego odcinka i kilku kwestii, które wymagały doprecyzowania.
Odszukałem materiały, o których rozmawialiśmy. Artykuł o pętli gry nie nazywa się „Loco Team”, lecz „Fix Your Time Step”. Jest dostępny na stronie Gamer on Gamescom i doskonale wyjaśnia, jak zaprojektować pętlę gry, aby zmieścić w niej wszystko i zarządzać częstotliwościami odświeżania poszczególnych elementów.
Jeśli chodzi o post mortem Age of Empires, okazało się, że nie zaglądałem tam od pięciu lat, a może i dłużej! Materiały, których szukaliśmy, są teraz dostępne na Game Developer COM. Linki do tego artykułu oraz do artykułu „Fix Your Time Step” znajdą się w opisie bieżącego i pierwszego odcinka na YouTube.
Polecane książki i materiały
Sięgnę do „taboretu” – dotychczasowa więź wyczarowała mi książkę. Tak, książka istnieje! To jest właśnie ta, od której zaczynaliśmy w pierwszym odcinku i od której będziemy kontynuować. Oczywiście „Game Engine Concepts” również pojawi się w linku pod YouTube’em, ponieważ tego nie pokażemy. To są trzy opasłe tomy poświęcone szeroko rozumianej sztucznej inteligencji. Polecam. Materiał jest rozłożony w czasie, od dawniejszych rzeczy, jak ta książeczka. Zawiera bardzo fajne rzeczy, opisane są algorytmy, które będziemy omawiać. Książka pochodzi z 2005 roku, ale to nie szkodzi, bo zawarte w niej rzeczy są ponadczasowe, tak jak sedno samych algorytmów. To jest coś, co będzie działać zawsze – można to tylko ulepszać. Podejrzewam, że coś tam się polepszyło, być może wyszukiwanie ścieżek, cele, czy zarządzanie celami u agentów. Może to się jakoś odświeżyło, ale sama podstawowa zasada jest taka, jaka jest. To tak jak z sortowaniem bąbelkowym – jest również ponadczasowe.
Ostatni link, który się pojawi, dotyczy „Game Programming Patterns”. Przeglądając stare materiały i linki z dawniejszych czasów, takie jak post mortem, natknąłem się na tę pozycję. To książeczka, którą warto przeczytać. Jest dostępna za darmo w internecie. Autor sprzedaje wersję PDF i drukowaną, ale udostępnił również wersję do czytania online, więc można ją sobie tam na stronie przeczytać. Znajdują się tam fajne, proste wzorce projektowe do zastosowania w grach, między innymi opisana jest pętla gry, nieśmiertelny singleton, a także uogólnienie singletona. Jest ok. Dodatkowo jest tam wiele ciekawych i bardzo specyficznych rzeczy pasujących do gier, jak na przykład bilbordy, czyli algorytmy efektywnego wyświetlania grafiki stanowiącej tło, która nawet może być ruchoma. Nie wiem, czy to z tej książki, ale kto wie, czy nie algorytm generowania cząsteczek również by się tam znalazł. Ale to sobie można tam wejść i poczytać.
Diablo, warcaby i generowane nazwy
W ramach erraty do poprzedniego odcinka, chciałbym przypomnieć nazwy potworów z Diablo, ponieważ je odszukałem. Szanowni państwo, te potwory naprawdę były fajne, a ponoć miały nazwy generowane! Podstawowe, które pojawiają się na wielu stronkach, to: pomiot wymiocin, zguba kręgosłupa, wyzwanie, ból, smród kości i siewca zgnilizny. Bardziej hardkorowe to: spijacz wymiocin szlamu oraz unikatowy: pełzająca część twarzy i pomiot pęcherza. Jest jeden taki, którego prawie dałbym sobie paznokcia obciąć, że sam widziałem, ale nie mogłem go wygooglać. Tak mi się kojarzy, że był taki: „żygać trzewik”. Gdzieś kiedyś go chyba tłukłem nawet. Czy do tych potworów już było, że człowiek stracił rachubę? Nie sposób nie wspomnieć, ale akurat może to jakoś niespecjalnie się łączy z ich… no, chyba że zostały wygenerowane przeze mnie te nazwy teraz, bo chyba przez te wczesne to wiadomo.
A i jeszcze jedna mała uwaga: podaliśmy tam liczbę, że we wszystkich możliwych ustawieniach warcabów jest . Program, o którym wspomnieliśmy, Chinook, robi przegląd zupełny na tym zbiorze i jeśli zapuści się automat na takiej partii warcabów, to zawsze musi zremisować. Bo jeżeli żaden gracz w warcaby nie popełnia błędu, to gra musi się skończyć remisem. Zostało to udowodnione w ramach pracy nad tym programem.
To tyle w kwestii przypomnienia. Możemy chyba zaczynać część właściwą.
Algorytmy i podejście Data Driven
Część właściwa będzie o algorytmach. Zanim zaczniemy, wypadałoby wspomnieć o bardzo ważnej rzeczy, o której trzeba pamiętać. Pojawia się ona w post mortem przeróżnych tytułów i w artykułach jako takich: podejście data driven to rzecz obowiązkowa w tego typu zagadnieniach jak gry. Już pierwsze rozdziały z tych klasycznych perełek programowania gier o tym mówią. Goście od Age of Empires doszli do wniosku, że bardzo dobrze zadziałało to, że zrobili maszynerię sterowaną danymi. Zaraz, jak dojdziemy do odpowiednich algorytmów i zaczniemy je rozkładać z grubsza na czynniki pierwsze, okaże się, że dobrze jest mieć te dane, na których one operują, gdzieś tam osobno od tego kodu.
W grach występuje tak duża ilość wszelkiego rodzaju sygnałów od systemów NPC-ów, sztucznej inteligencji zarządzających poszczególnymi komponentami. Chyba ciężko sobie wyobrazić, żeby to wszystko było stricte proceduralne. Tutaj taka ideologia, którą czasami chcemy wprowadzać w naszych biznesowych aplikacjach, żeby troszkę zamodelować ten świat. Właśnie, trochę na pozór takiego rzeczywistego świata, gdzie rzeczy też niejako działają troszkę na sposób eventów, no bo jednak komunikacja czy poszczególne sygnały to jest właśnie wysyłanie jakiś wiadomości, czy to dosłownych wiadomości, czy właśnie takich sygnałów, które są interpretowane jako wiadomości. Więc gry, jako takie jeszcze bliższe odwzorowanie tego świata rzeczywistego – bliższe niż nasze tutaj biznesowe króliki – na pewno zdecydowanie. No to na pewno właśnie takim podejściem bazującym na eventach, bo fajnie się to wpisuje.
Eventy to oczywiście jak najbardziej jedna, dość istotna rzecz przydatna do sterowania, ale wszystkie dane, jakie leżą oprócz tych eventów w okolicy, to jest właśnie to coś, co też powinno być podciągnięte od tego głównego kodu. Nie możemy mieć nic za krótko dodanego. Wszystko musi być sterowane i eventami, i danymi. Jeszcze dopowiem, bo bardzo szczegółowo będziemy wchodzić w post mortem Age of Empires. Może nam się uda dość szczegółowo, ale tam jest też taki wniosek, że kiedy już mieli przepisany silnik na ten właściwy i mieli oddzielone, odseparowane komponenty od głównego silnika i całej reszty, mogli sobie wszystkie obiekty gry, jakie tam tworzyli, parametry dodawać tak, jak chcieli. I naprawdę mieli tych parametrów całe mnóstwo. Co zresztą widać w samej grze, bo widać, że tam powinno być dużo liczb pod spodem, dużo się dzieje i fajnie się dzieje. To właśnie to pozwoliło im w późniejszych już etapach produkcji, mając już kodzik ustabilizowany, ten główny silnikowy, tak zmieniać parametry poszczególnych jednostek, żeby na przykład pracować nad balansem przez cały czas. Tak, czyli to jest właśnie to podejście data driven. Jego szczególnym przypadkiem jest event driven, można powiedzieć, które pozwala sterować po prostu częścią zachowań, być może monitorować.
Final Fantasy XV, Wiedźmin 3 i testowanie gier
Do potwierdzenia tej tezy jest też jeden z rozdziałów w Game Engine Architecture od twórców Final Fantasy XV, którzy zdaje się podejście zapożyczyli też z Wiedźmina 3, co było przedstawione na jakiejś konferencji. Nareszcie coś rodzimego! Otóż twórcy Final Fantasy XV, jako że robili grę de facto, można powiedzieć, open world, to sporo się tam działo – i sztuczna inteligencja NPC-ów, i zachowanie odnośnie wyszukiwania ścieżek po mapie, poruszania się bohaterów, czy wszelkich interakcji. Tych danych było na tyle dużo, że oni sobie zrobili osobną aplikację, zwykłą „webówkę” opartą na NodeJS i JavaScripcie, która agreguje działania. Oni to nazywają „loggerem”, w sensie, że logują poszczególne eventy z gry i poszczególne rzeczy, które się w tej grze dzieją. De facto są to takie eventy, które zawierają informacje na temat danej akcji czy danej sytuacji, która się dzieje. Agreguje on te wszystkie eventy właśnie w tej aplikacji webowej. Tam jest formalnie baza danych, to można powiedzieć normalna, standardowa, webowa apka, jaką znamy ze świata naszego bliższego biznesowi. W tej aplikacji aktywują te wszystkie dane, czy to na mapie, robiąc jakieś heat mapy, patrząc, czy nie ma jakiś problemów z meshami na danej mapie, na zasadzie, czy możemy dobrze się poruszać, nie ma jakiś dziur, tego typu rzeczy, czy jakieś problemy z dziwnym zachowaniem sztucznej inteligencji. I wszystkie te rzeczy w tej aplikacji reagują, bo zobaczyli, że tak naprawdę to jest jedyny sposób, żeby taki nawał informacji jakoś przetworzyć i to wszystko trochę zrozumieć.
To wszystko też jest bardzo fajnie spartyjonowane, bo można sobie to przeglądać z dokładnością co do maszyny, na której gra dana osoba, bo to jest korygowane per maszyna, per dana osoba, która testuje. Możemy to sobie przeglądać dla danej sesji gry. Czy na przykład tester testuje jakiś kawałek funkcjonalności i żeby reprodukować jakiś błąd albo pokazać programiście, że coś tam nie działa, to można w tej aplikacji sobie podejrzeć wszystkie eventy. No bo inaczej to ciężko tak naprawdę zdebugować ten cały stan, który pod spodem doprowadza do tego. A więc taki data driven na zasadzie właśnie, czyli event sourced. Po prostu mają event sourcing. Pytanie, na ile oni tam odbudowują stan tej gry, ale na pewno, jak później buga próbują reprodukować, to mogą sobie odbudowywać. W sensie mogą sobie przynajmniej odgrywać część stanu na pewno tak, bo często są. Część to pewnie taki stan, którym można zasilić swoją własną sesję gry i zredukować ten kawałek. Szkoda. Miałbym taki klocek, to tak bym zrobił. Dokładnie.
Ale sam fakt agregacji takiej ilości danych oczywiście ma plusy. Możemy też zapuścić później jakieś algorytmy uczenia maszynowego, żeby automatycznie wykryć w tak dużej ilości danych jakiś pattern czy jakieś inne prawidłowości. To też pokazuje, że testowanie gier to nie jest tylko i wyłącznie takie reviews. Tu siedzę, spadłem i tam klikam i patrzę, co się dzieje, a później musimy mieć bardzo duży wgląd w tak dużą ilość informacji, żeby te wszystkie rzeczy, które i proces, i proces tego tuning to. W tym procesie na pewno pomaga i jest też miejsce, jak widać, dla zwykłych, ludowych aplikacji, nawet w gamedevie.
Ewolucja testowania w grach
No właśnie, ktoś chciał się przenieść, to można tam takie aplikacje też tworzyć. Pewnie, pewnie można, i nawet, jak mówisz, są przydatne. Ale tak właśnie nawiązuję do tego testowania. Takie testowanie gry to już jest, powiedziałbym, po pierwsze, nie może za bardzo odbiegać od zwykłych takich pryncypiów testowania. Czyli jak coś testujemy, to w powtarzalny sposób. I tutaj z kolei znowu przypominają się post mortem, tylko że w Wiedźminie ponownie, gdzie goście mówili, że po prostu, mówiąc brzydko, mieli dość swojej własnej gry. No tak, musieli ustawiać się w którymś tam momencie scenariusza, gdzieś tam po iluś questach, już mieli. Też sobie maszynerię doprowadzili do takiego stanu, że można było w dowolnym punkcie właściwie się wstrzelić. No więc też jakimś zalatuje event sourcingiem czy czymś podobnym. Ustawili sobie ten scenariusz. No i od tego punktu jedziemy i sprawdzamy, czy wszystko się zgadza. Więc ci testerzy w gamedevie to wcale nie mają łatwo, bo jak na przykład trzeba dwieście razy przejść ten sam fragment scenariusza, to pewnie później się nie chce już na swoją grę patrzeć. Oczywiście, że tak. To nie jest tak, że gra w gry, jeszcze mu płacą, i tylko granie w ten sam kawałek. „Jaką masz pracę?” – „Gram w…”. To jest cały czas.
No tak, to jest takie testowanie na poziomie już faktycznego produktu i kontaktu z tym produktem. Ale jak widać, testowanie to jest też analiza i interpretacja dużych zbiorów danych. I też są fajne łóżka. Jest między innymi też w temacie Game Engine Architecture opisane testowanie. Już bitowe, ale też oparte właśnie na. Na pewno rzeczy, które się dzieją w silniku gry. Czy możemy w tych testach, tak jak autor fajnie opisał to taki swój framework troszkę do rozpoznawania pewnych obiektów w grze i testowania np. pewnych elementów sztucznej inteligencji na zasadzie, czy dana postać widzi daną postać, czy dana postać bywa np. rozpoznawane postacie, czy poprawnie atakują się, widząc siebie nawzajem, czy tracą swoją widoczność, jeśli wchodzą za jakąś przeszkodę i np. zapominają. To są takie rzeczy, które w kilku linijkach dosłownie da się w takim teście zwykłym, można powiedzieć, unikatowym zastopować. Oczywiście, mając pod spodem odpowiedni zestaw narzędzi i też przygotowane trochę takie bazowe testy, ale nie trzeba tego tak mozolnie klikać na zasadzie, że ja sobie chodzę tym ludzikiem i patrzę, czy czy tam wszystko działa, a to rzeczywiście też jest trochę drobnych elementów.
Rozwój narzędzi i podejść
To chyba musiało upłynąć parę lat, żeby ludzie też w branży gamedev do tego dojrzeli. Bo pamiętam, jak wykłócałem się kiedyś z kolegami na forum polskim, na tym warsztatowym, takim, gdzie tam się pojawiały wątki takie, że już nawet tłumaczone przez gości, gdzie jeden przedstawił się jako członek ekipy Wiedźmina, jeden. To tam wiadomo, wszyscy na twarz prawie padli, jak taką znakomitość mają na forum. Ale ci tacy ziomkowie, którzy właśnie mieli doświadczenie jakieś i tak widać było, że sensownie odpowiadali na te rzeczy. Oni jeszcze zagajenie o testy automatyczne – to w ogóle dla nich była czarna magia. W ogóle twierdzili, że tego się w grach nie da zrobić i że wszystko trzeba testować z palca. Jak im tam próbowałem podpowiadać: „A może byśmy pomyśleli, żeby jakiś setup gdzieś tam, coś tam, nie, tutaj odciąć zależności?”, to chyba nie potrafili sobie zwizualizować, że tej warstwy wizualnej nie można mieć.
Tutaj musiało troszkę czasu rzeczywiście upłynąć, bo testowanie w grach to jest trochę inna klasa problemów. I krótszy czas pracy. Też zakładam, że trochę inne podejście do testowania w tego typu produktach, jak gry, jest trochę inne niż w aplikacjach biznesowych, bo jednak aplikacje biznesowe są utrzymywane i celowane raczej na dłuższy okres życia. Możemy jednak te testy trochę lepiej przewidzieć i wpisać w cykl tego produktu, bo wiemy, że mamy zamiar czy plany ten produkt utrzymywać przez dłuższy czas. Gra, przynajmniej te 10–15 lat temu, kiedy jeszcze królowały gry usługi, czyli coś, co jest sprzedawane jako usługa i działa cały czas, tylko jako jednorazowe produkty, które sprzedajemy, jeśli działają, to jest super i zapominamy. To testowanie też jest troszkę po macoszemu traktowane, no bo to nie jest coś, co będziemy utrzymywać przez dłuższy czas. Jeśli działa mniej więcej okej, to wystarczy to manualnie przetestować i nie potrzebujemy… No, chyba że się sypie i reklamacje, to jest oczywiście inna sprawa. Tak, ale…
Ale generalnie, troszkę jestem w stanie zrozumieć tę niechęć do inwestycji w testy w takim rozumieniu jednorazowej sprzedaży produktu. To jest jedna rzecz. Druga, podejrzewam, że tutaj kwestia troszkę też zasobów i trochę narzędzi zrobiła swoje. No bo jeszcze 15–20 lat temu takie testy, na przykład w grach, wiadomo, warstwa wizualna, ale nie jestem pewien, czy wtedy już na przykład narzędzia do graficznego porównywania dwóch różnych plików z możliwością diffowania były tak popularne. Teraz nawet w biznesowych aplikacjach na pewno było tego mniej. Tak, teraz biznesowe aplikacje mają też odpowiednie testowe frameworki do definiowania screenów. Czyli mamy zwykłą apkę HTML-ową. Możemy sobie w łatwy sposób definiować screena na zasadzie graficznej reprezentacji i pokazania różnic. Taki najbardziej chyba najprostszy test, który przychodzi nam do głowy, myśląc o testowaniu takim stricte wizualnym. Teraz to się czasami stosuje jako taki element, nie testowy. To jest inna warstwa testów. Wiadomo, że najwięcej powinno pewnie być tych takich najprostszych, najszybszych JUnit-owych, a tych takich bardziej skomplikowanych, pewnie powinno być mniej, ale możemy sobie te skomplikowane też wyobrazić. Tylko kwestia jest znalezienia równowagi pomiędzy backendem a frontendem. Oczywiście, niemożliwe. I przede wszystkim rozdzieleniu tych dwóch zagadnień, bo coś tak mi mówi, że to właśnie produkcje, których się nie dało by testować… pewnie ci koledzy mieli rację, że nie dałoby się w ich tam, gdzie oni hodowali. Jasne, że tam by się nie dało łatwo testów zrobić, jeżeli tam wszystko było poplątane. A pewnie było, jak było, bo jakby nie było, to może by jednak coś próbowali.
I w grach Team Utility raczej nie jest zbyt powszechne. Tak też słyszałem właśnie od nich między innymi, że, ale to może w ogóle, żeby u nas było. I Unity też nie są. Ale z tego, że świat poszedł do przodu, i ten, i tu, i ówdzie się pewne podejścia sprawdziły, to pewnie i gamedev powoli zaczął nadążać za tym i okazało się, że jak się rozdzieli odpowiedzialności, to jednak daje się coś takiego zrobić. Tym bardziej, że te noże właśnie, chyba narzędzia też. Odbiegamy od tematu głównego, ale ale ten jeszcze chyba trzeba dopowiedzieć, że narzędzia jako takie też poszły do przodu. I teraz te silniki, edytory, które na tych silnikach pracują, tak jak Unity czy Unreal, to są tak zaawansowane klocki, że one same w sobie są już grubymi produktami Javy, a one nam dostarczają właśnie tej warstwy wizualnej, którą następnie możemy o skryptowe. Już, to już, to już są dwa rozdzielone koncepty tutaj. Więc teraz można sobie wyobrazić jeszcze, że wszyscy już zrozumieli, co to jest, np. serwer gry. To już widzą rozdzielenie pomiędzy klientem a serwerem. Nie, że klient jest. Ma swoją logikę, ale. No ale musi zwizualizować ten świat, który na serwerze nie istnieje w warstwie wizualnej. No i mamy taki serwer właśnie, który już jest po prostu sterowany i danymi, i przede wszystkim eventologią. No tak, to już jest mocno, to już jest przetartestować. Tak, jak już się ma taki mindset, to to już podejrzewam, samo się robi. Wszystko to jest Microsoft. No, także mamy sobie ten data driven jako taki wniosek z post mortem Gamedeva z roku 1998, żeby nie było tak, że wiekowa rzecz. Ile to? 25 lat? Już ćwierć wieku Ciebie już taki wniosek wysnuł, to i mamy dzisiejsze przykłady, gdzie sami możemy taki wniosek wysnuć, i mamy książki, które tłuką to do głów od lat 20 z kawałkiem bez śrub. Data driven. FS to już w końcu może coś by się nauczył. No już może by się nauczyli. To się daje testować. No i taki właśnie chciałem. Tylko wstęp do tego. Data driven. Nie za dobra, to dobra, i klepiemy kodzik, tylko niech to będzie kodzik z głową. Oczywiście. Oczywiście do tego.
Maszyny stanu i silniki skryptów
Wracając do meritum, skończyliśmy na maszynie stanu, która była taka. Zaczęliśmy od takiego wariantu najprostszego, gdzie da się go narysować na kartce w kratkę. Linię też się da, nawet na czysto, ale na czysto się da. Oby tylko nie rysować tego, jak scenarzyści Rings of Power pisali scenariusz na serwetce w restauracji – tego nie polecam, ale rozumiem aluzję. Wstawka kulturalna przy okazji. Więc możemy sobie tę maszynę stanu albo zrobić taką prostszą, albo zrobić trudniejszą. Możemy zrobić różne wariacje tam w środku. Możemy sobie zrobić hierarchiczne maszyny stanu. Za chwilę nam się tutaj pojawi, jak zdążymy oczywiście. Kolejny algorytm, który jest bardzo blisko, czy może nie tyle algorytm, co koncepcja. Bo algorytm maszyny stanu to jest to właściwie, co sama maszyna opisuje. Ale koncepcja maszyny, ten wzorzec, to jest to, co nas interesuje. Tutaj hierarchiczne sterowanie, hierarchiczne za pomocą hierarchicznych goli, celów. To jest taka trochę wariacja tej maszyny stanu, ale w ciekawy sposób można to zrobić. I jak się do tej maszyny stanu dołożyło różne, nie wiem, jeszcze tam takie upiększenia, to można było np. wykorzystać ją do tego, żeby nam tymi NPC-ami sterowały, w sensie zaopatrzyć NPC-ów w odpowiednie maszyny stanu, które im po prostu pozwalały jakieś proste scenariusze robić.
Idąc tym duchem scenariuszy, nie sposób oprzeć się koncepcji silnika skryptów. To byłaby taka druga klasa algorytmów, które pojawiają się w literaturze i pojawiają się w produktach. Stąd też są w literaturze. Cóż to by mogło być takim silnikiem skryptu w grze? To byłby właśnie mechanizm, który pozwala zakodować coś wewnątrz programu samego w sobie, gdzie program, w sensie kod gry, jest jakoś tam przygotowany do tego, żeby wczytać taki skrypt, jest przygotowany, żeby zrozumieć to w sensie. Dostarcza mu jakiegoś wejścia i wyjścia, no bo musi zrozumieć to, co w tym skrypcie siedzi. Skrypt realizuje całą logikę biznesową w tym momencie. I to też jest przykład sterowania danymi, bo ten skrypt to są nasze dane, to są nasze dane, które możemy podmieniać, możemy jakoś modyfikować bez konieczności przebudowy właśnie całego… A nie zapominajmy, że taka gra to się potrafi budować… To jest tak hop siup, że żeby to wszystko zbudować i to takie procesy budowania czegoś takiego.
Znowu przytoczę post mortem Age of Empires 1. Nie jest to duża grupa jak na dzisiejsze standardy. To jest taki tyci, tyci, taki prosty RTS. Jak taka jedna płyta CD, to jeszcze z muzyką, coś tam, muzyka i z animacją, i z animacją. I jeszcze PDF-a nam wcisnęli. No to tam sama gra zajmowała tam ze sto kilkadziesiąt mega, z tego co pamiętam, także to nie jest duży produkt. A tam już do porównania współczesne Call of Duty — 200 kilobajtów. No to to już jest zezwierzęcenie i degrengolada. Ale że tam musi być sztucznej inteligencji, panie. Ze 30 GB, 30 GB. Reszta to grafika 17K. A skalę fani pokazują ze stu kilku megabajtów do gigabajtów. Muzyka akurat jest trochę skrajnością, ale generalnie obecnie powyżej 100 GB to większość produkcji ma. Wiedźmin 3, jak wyszedł w 2015 roku, był na trzech płytkach DVD. Miał chyba z 17 giga tak na dysku. Pierwsze rozpakowanie, po rozpakowaniu jeszcze 20. A teraz ostatni mod ultra, jak mi wyszedł, to mi chyba pokazało w Google 50 kilka notatek. Grafika 4K i HD tekstur. To sporo ta grafika najwięcej. Tylko i wyłącznie jeleń. Bo bo nawet ten Horizon to też potrafi chyba ważyć ze 110 giga, ale jakoś tak, nie wiem, może to u mnie nie wygląda. Muszę dociągnąć patcha chyba. No bo mój dziadek już nie ruszy, a mnie coś tam, coś tam trochę wygląda, ale jakoś coś mi ten cieniowanie skóry się nie podoba. Jakość jest taka, jak w Maku przed tym modem. Może muszą zbudować? No może tak.
Skrypty w grach i rozszerzalność
W każdym razie mamy te skrypty w Zonie i trzymamy je gdzieś w jakimś tam źródle tych skryptów, nie gdzieś tam w danych i mamy silnik, który potrafi zrozumieć te skrypty. No i oczywiście musimy mieć jakieś reguły, które pozwolą tym skryptom podłączyć się do silnika, ale jak już to zrobimy, to możemy sobie skrypty, dialogi, możemy zachowanie NPC-ów, skrypty i co tam jeszcze. Interakcje między tymi jakimiś tam obiektami, agentami czy takimi inteligentniejszymi obiektami gry możemy sobie zarządzać całymi scenami, czyli możemy sobie tworzyć scenki, na przykład na podstawie skryptów. Mamy obiekty, to zresztą widać nawet w nieśmiertelnym Macu naszym. Żeby daleko nie szukać, to widać, że niektóre cutscenki, które są po prostu renderowane, przechodzą sobie płynnie w gameplay. Nie i ten i odwrotny gameplay. Może nie tyle płynnie, bo jest tam jakieś takie zaciemnienie ekranu, takie cięcie, ale widać, że to samo ustawienie postaci albo zbliżone, które, czy w ogóle cała sceneria, która była właśnie w scence, z której wychodzimy, nagle i ją mamy w akcji, bo mieliśmy zapowiedź walki i ten i ci sami przeciwnicy są w tych samych miejscach. To co w tej scence. Nie? Albo wchodzimy do lokacji i taka scenka, Może ona nas się renderuje jakoś tak w locie, czasem nie, chociaż Makoto no wyglądał jak takie pre renderowane, z tego co kojarzę, a to chyba było na silniku gry, ale w locie to w trakcie gry. No to nawet tutaj pewnie obcowanie by wchodziło w grę tego zakazu, no bo to to muszą być jednak reżyseria tych scenek i wiadomo, kamera i wszystko jest skopiowane na pewno, ale wciąż na nośniku.
No, ale nawet takie prozaiczne rzeczy, jak jakieś pojedyncze triggery, jakieś spawny czegoś gdzieś tam, takie obiekty, które muszą zareagować, jak się w ich obecności pojawiamy, też spokojnie mogą być od skryptów dane, nie? No i taka najważniejszych rzeczy – dialogi po prostu z NPC-ami. Co widać w minutach. „Viva la dead”! Bardzo dobitnie pokazane, jak to można schrzanić.
Logika w skryptach i DLC
To super poruszyłeś ten element obiektów i właśnie te skryptowe i tych interakcji, bo to jest w ogóle jeden z takich patentów, który się często stosuje, że część z tej logiki zachowań nie implementuje się tego właśnie bezpośrednio w silniku gry, tylko właśnie w tych skryptach. I te skrypty dołącza się czy jako pewne zachowania, czy jako pewne parametry. Takie sterowanie po prostu obiekty. Bo co jest ważne? Fizykę się implementuje, ona jest w silniku, tak samo jak grafika. W ogóle fizyka jest nierozerwalnie połączona z grafiką i zadaniem, więc obsługa fizyki musi być. I notabene książka z naszego taboru – od czego się zaczyna? Od wprowadzenia do fizyki w grach. Polecam pierwszy rozdział, więc ta fizyka jest zaimplementowana. Wejście na wsparcie dla skryptu logiki jest zaimplementowane. I teraz wchodzi nasz skrypt i sobie skryptuje. I to jest właśnie to jest właśnie koncept, który jest nie bez powodu stosowany, bo to bardzo łatwo umożliwia tworzenie DLC.
A teraz wyobraź sobie dowolną grę, gdzie zawsze mamy jakieś dodatki sprzedawane osobno, jakieś skórki, postacie, jakieś obiekty. I te rzeczy. W większości przypadków one już przychodzą ze swoją własną logiką, ze swoim własnym zachowaniem. One nie są zaimplementowane w tej grze. To bardzo często było między innymi w Simsach stosowane, które miały tysiące różnych dodatków na temat przeróżnych rzeczy, które można w Simsach robić. No to nie gram w te. To jest takie okropne. I wiadomo. No ja też nie, ale skoro to wiadomo i dzięki temu, że też są skryptowe zachowania i ta logika, która przychodzi z tymi nowymi rozszerzeniami, z tymi rzeczami, które możesz w tych nowych dodatkach dostać. Dzięki temu nie musimy. Po pierwsze. Po pierwsze masz większą swobodę w tworzeniu nowych rzeczy, bo możesz po prostu wszystkie rzeczy, które stworzysz, zachowania i wszystkie elementy, które się starasz jako całość. Nie tak samo, jak to mają być na czas, tylko jako taki malutki moduł i on będą działać na pewno z wersją podstawową, bo nie potrzebujemy tego, nawet tego języka czy w ogóle jakiś tam filarek z tej gry zmieniać zgodność API, bo to wszystko jest zgodne z tym, tym i w to obiekty mogą po prostu protestować spójne swoje dostępne zachowania. I ten podstawowy, podstawowy jakiś tam NPC wie, jak korzystać z tego nowego obiektu, który się pojawił. Więc to jest nieodłączny element takiej rozszerzalności i modułowości. I właśnie te gry to jest coś, czego ciężko mi znaleźć jakieś analogie w biznesowych systemach, mimo że my też dokładamy cały czas nowe kawałki, tylko… No, ale nie mordujemy tak tego, my i tak tego nie budujemy, bo może nie po prostu przebudowujemy, bo zawsze mamy dostęp do całości tego, tego naszego systemu, więc prościej jest wziąć i całość zmienić, niż bardziej to robić mocno, żeby się znalazły jakieś dziwactwa. Gdzie nie gdzie to byłoby takie. Ale to są takie pojedyncze zastosowania, dość specyficzne i pewnie w mniejszym zakresie.
Tak trochę może bardziej przypominać takie systemy plugin, kiedy mamy takie jądro systemu OSGi i Eclipse właśnie na tym mocno się opierał, czyli podstawowy widać nawet Eclipse. Tu szukam analogii. To wszystko, wszystko jest w gamedevie. Więc jest jakaś klasa systemów, która z podobnych rzeczy nie korzysta. No ale wiadomo, to nie jest do tego stopnia jak świat gier, kiedy jest mocno otwarty na rozszerzenia, czyli open-close. Kolejna analogia, a nawet pożyteczna, to już taka naprawdę w grach zastosowana bardzo mocno. W ogóle gry, jak spojrzeć na nie, na całą tę branżę i w ogóle na to, co ta branża wypluwa z siebie, to gry to są, jak dla mnie, absolutny majstersztyk architektury przede wszystkim i algorytmów, tego, co można zrobić i tego, co można zrobić. Możliwości, które oddolnie tak i z jego rozbudową, i z utrzymaniem w ogóle. Całkiem inna klasa jest zagadnień niż taka półka na grudzień. Smutna prawda. No to się teraz cieszę do tych gier.
Skrypty w grach — od Lua do własnych języków
Dlatego staramy się szukać tych analogii, żeby chociaż troszkę jakąś namiastkę tego gamedevu gdzieś tam znaleźć. Można by wrzucić do takiego „gruda”, takiego dinozaura, co tam w grach skacze. To też by było fajne, jak to pozwoli. Nie będzie widział, nie będzie widział. Nie pytamy się.
Co do skryptów — powiemy sobie, co tylko nam się w tym trudzie nada. Będziemy zarządzać obiektami dostępnych skryptów silników. Silników skryptów jest kilka. Nieśmiertelna Lua gdzieś tam z dawien dawna i pewnie jakieś świeższe. Czytałem czy słyszałem, że w Pythonie ludzie robią, także cokolwiek. Własne implementacje prostych, własne implementacje, ale to też jest dość proste. Mądre głowy zalecają po prostu wziąć już gotową implementację, bo już jest przetestowana i przede wszystkim rozbudowana. Pisać swój własny język… Teraz, jeśli mamy do napisania grę i siadamy i piszemy język skryptowy, kompilator swoich skryptów, interpreter… Dokładnie, to jest taki wzorzec właśnie w tych patentach, w tej książeczce o patternach. Brzydko to się nazywa tam. I to jest właśnie taka maszyna wirtualna. Czyli żeby zrobić grę, piszesz wędkę, taką wędkę piszesz i masz swój byt gotowy myśleć. Na pewno to jest fajne i tak sobie siedzą ludzie, wymyślają swoje, jakie tam kolejne bajty będą znały. Liczby teraz tak długo powstają, ale tylko teraz.
No właśnie do tego zmierzam, żeby tego wszystkiego nie pisać. Jednak można wziąć gotowe rzeczy. Może niekoniecznie maszynkę Javy tam wciskać, ale chociaż, jak się zmieści… Jeśli ktoś by chciał, to czemu nie. Nie wiem, czy gdzieś nie było wspominane, że silniki już też mają najczęściej jakieś wbudowane sportowe. No właśnie rozwiązania. Dosyć często się słyszy, mimo wszystko, o Lua. To jest taka stara rzecz, standard. Przeważnie w książkach takich PDF-owych to się przewija jako standardowa rzecz. Generalnie chodzi o to, żeby mieć sprawny silnik do skryptowania. Czyli musi być maszynka, która rozumie to, co napiszemy. Musimy mieć składnię dobrą do tego, żeby zrealizować nasze rzeczy. Jak mamy się dobierać do obiektów z gry, to musimy móc tym czymś sięgnąć do API, jakie nam silnik wystawia. No i silnik oczywiście musi być uprzejmy, żeby nam to API wystawić, bo później to wszystko puścić. Puścić wodze fantazji, a reszta to co taniej można skryptować.
Algorytmy sterowania ruchem i podział przestrzeni
Dobra, żeby nie przeciągać, to może następna klasa algorytmów — to byłyby to algorytmy związane ze sterowaniem ruchem obiektów. Większość chyba problemów dotyczy rzeczy, z którymi jednak w grach mocno się spotykamy, czego nie ma zupełnie w aplikacjach biznesowych. No, nie poruszamy obiektami za bardzo, a myszka już sama się rusza, już nawet tego nie można zaimplementować. Co za czasy!
Na szczęście można zrobić sobie grę, gdzie mamy ławice rybek i rekina. No i trzeba tamten algorytm ławicy zrobić. A to już wspominaliśmy pobieżnie, że taki algorytm bardzo prosto się robi – w teorii oczywiście, żeby go sobie zaimplementować, trzeba trochę jednak pewnie przysiąść. Ale zasada jest prosta: mamy coś, co się porusza gdzieś tam. To jest ten nasz lider. No i mamy przyczepione do tego lidera obiekty, które muszą znać pozycję lidera, swoją. Muszą się orientować z grubsza w świecie, żeby nie wpadać na przeszkody, ale ich zadaniem jest trzymać się od lidera w pewnej odległości i w pewnej odległości od swoich sąsiadów. No i jak się takie coś zrobi, to już mamy zarządzanie i takimi rybkami, i latającymi szpakami, i czym tam jeszcze — tłumem zombiaków. Zombiaki też się tym nie przejmują. Wpadanie na siebie… do nas, to wtedy możemy tego ifa pominąć. Jest prościej, ale są ify na powodzie. Włóczenie nogami w dobrą stronę, to to musi być dobrze. No tak, jeśli będzie w różne strony, to jak to wygląda? To nie wygląda wtedy. No ale np. jak się popatrzy na latające ptaki na niebie, to one właśnie realizują taki algorytm w grze w rzeczywistość. W rzeczywistości w grze też są zaprogramowane, ale w rzeczywistości to tak właśnie. Poczytałem takie artykuły sobie. Jak miałem okazję. To trochę jak widziałem takie latające stada, to totem to faktycznie zachowują się tak jak w grze, czyli po prostu latają tak, jakby jeden prowadził. Chociaż czasem to wygląda jak trochę takie gotowanie się, bo to, co było z tyłu, dostaje się nagle przodem chmury i ona nam leci w jakąś stronę. Wiem, że AI mają słabsze, jednak niższe niż w tych grach. Czyli żyją w tych symulacjach w żywych. To ja już mówiłem, czy dowód? Dowód? Bo ptaki latają jak w grze. No jeszcze pewnie zawieszają. Jakby nie patrzeć, to wszystko jest super.
No ale żeby np. tak ładnie ogarnąć taką całą chmarę różnych stworów, to jeszcze jak one latają i są taką ławicą, stadem czy tłumem… Coś to ta klasa problemów, nie? Czy jakimś zbiorem cząsteczek, chociażby nie? No to jeszcze względnie jest spoko. Gorzej, jak one mają. Wchodzi ze sobą w interakcje. Bo wyobraźmy sobie teraz, że mamy 1000 ich w tej w tej ławicy. I teraz niech, niech każdy z nich ma jakiś tam zasięg widzenia bądź ataku i niech to nie będzie już podążanie za liderem, tylko niech to będzie starcie wojsk po prostu na mapie, gdzie wiadomo, że nasza formacja musi się zachowywać jak formacja, czyli nasza formacja podąża za tym wzorcem sterowania grupą, ale naprzeciwko siebie ma grupę wroga, z którą musi wejść w interakcję. I teraz mamy tych jednostek powiedzmy 1000 — 500 na 500. Taka bitwa, takie małe bitwy już teraz nie, bo np. 1000 na 1000 byłaby fajniejsza. I teraz, czy każda z tych jednostek? Ten tysiąc po jednej stronie ma sprawdzić z tysiącem swoich kolegów po drugiej stronie, czy przypadkiem ich nie atakuje? To byśmy mieli milion sprawdzeń na każde z tych sprawdzeń. Wyczuwam. Dziel i rządź.
I ten dziel. To właśnie podział przestrzeni jest nowością, także tutaj. Do ogarniania takiego chaosu dobrze przydają się algorytmy dzielące przestrzenie. Jakieś tam drzewa w 3D — będą to ósemki owe. Na 2D — drzewa czwórki, czy nawet może jakieś ich wariacje, bo niekoniecznie zawsze trzeba na symetryczne fragmenty dzielić tę przestrzeń. Można ją jakoś podzielić inaczej. Ważne, żeby tylko ją podzielić tak logicznie, żeby wyeliminować konieczność sprawdzania czegoś, co np. nie ma szansy wchodzić w interakcję z czymś innym. Czyli jeżeli mamy naszego łucznika, który tam daleko, widzimy już tych, tych zbrojnych wrażych, którzy na nas całą tą kupą, mości panowie, ciągną, ale nasz łucznik jeszcze nie może ich ostrzelać, to nie sprawdzamy tego, nie? Jeśli jest poza zasięgiem, no to może widzi to. Czyli mamy po prostu sprawdzenie naszego cylindra otaczającego nasz oddział z cylindrem otaczającym tamten oddział. I dopóki one się nie znajdą w zasięgu naszych ataków, to w ogóle nie rozpatrujemy ataków. Czyli dziel i rządź po prostu. Czyli klasyczny podział, podział przestrzeni problemu. Tak. Podział problemu na fizyczne przestrzeni tu i teraz. Może jeszcze to fizyczne, ale przestrzeń też koncepcyjnej. Tak, dokładnie.
Nawigacja po siatkach i grafach
I tak, jak oni tam chodzą, to pewnie po siatkach chodzą. W niektórych grach to tak ciężko spotkać z gwiazdką i gwiazdka to jest, że chodzą albo jak chodzą, to gwiazdkę rysują, też się zdarza. No, ale żeby mogli chodzić, to to też musimy im zapewnić jakieś środowisko do tego chodzenia, czyli musimy im tę przestrzeń zdefiniować, bo jakim damy po prostu taką przestrzeń… Ale nawet w Tron, bo chciałem zrobić nawiązanie, ale tam już ten, jako Jeff Bridges grał, to. Chyba tak. Jeśli tak, to on tam zaczyna w drugiej części, że stworzono grid, czyli siatkę. No to już wiadomo, że poszedł na łatwiznę, jak nie zrobi linii ciągłej przestrzeni, tylko zrobimy przestrzeń dyskretną na siatce. Więc jak mamy siatkę, to możemy po tej siatce chodzić. Ale siatka — czym jest grafem?
Przejdziemy zaraz do algorytmów grafów. Pewnie więc za niedługo, ale taki NPC, który by sobie dreptał po mapie. Tenże właśnie zagadka. Nawet ten, ten, który tu akurat piec, bo jak go wysłałem po jedzonko, to po to wysłał mnie. No ale on też idzie, czy to jest NPC, czy nie NPC, to on idzie po prostu po tych punktach nawigacyjnych. Czyli musi mieć ten algorytm. Oczywiście wyszukiwania ścieżki to jest coś niezbędnego, co musi być. I tutaj algorytmy jakieś grafowe wchodzą do tego, A* na przykład, jakieś tam optymalizacje, ale musi istnieć też ten graf. Czyli to musimy mu dać to, po czym on idzie. Ta siatka, która odwzorowuje ten teren, który jest już faktyczną teksturą, ale postacie nie chodzą, która jest na nim nałożona. Postacie jakby nie chodzą stricte w sensie, nie patrzą na te tekstury, a tylko na tę siatkę intuicyjną.
Co więcej, obecnie w grach bardzo często, już można powiedzieć, już właściwie zawsze, albo grę już też to mocno różnicują. Tych siatek nawigacyjnych jest bardzo więcej niż jedno tematyczne, bo są tematyczne, ale są też często w grach, np. mamy wielkich bossów. Wyobraźmy sobie Dark Souls, nawet Wiedźmina 3 też mieliśmy przecież wspomnianego bossa. To są zwykle postacie dużych gabarytów, duża i dużej postury. I oczywistym jest, że mała postać gdzieś może sobie chodzić, ale ten większy nasz boss czy w ogóle jakiś Behemot to już nie wszędzie wejdzie, nie wejdzie do jakichś… jakiś Diablo czy jakiś Diablo. Nie będzie już pod każdy, pod każdy krzaczek i wszędzie, więc w zależności od postaci, w zależności od sytuacji musi istnieć inna siatka fikcyjna, żeby dało się sensownie tymi postaciami po ich naturalnym, nawet lepiej, chodzić. Miejsca. Tak, tak, tak. Czyli one. Ten algorytm wpuszczania ich na poszczególne węzły musi uwzględniać to, czy może wejść nie tak, ale nawet lepiej, bo bo np. weźmy atak naszego zwierzaka, żeby daleko nie szukać, czy coś podobnego. Nie, coś, co unieruchamia albo blokuje po prostu kawałek terenu, to to sprawia, że postać jakaś tam inna. Kiedy jest ten kawałek terenu zmieniony? Nie wiem. Powiedzmy rzucamy jakieś zaklęcie zmieniające otoczenie w lawę, już tam nie można wejść. Można, ale np. jest blokada, bo bohater inteligentnie uniknie tego. Więc żeby gracz go nie spychał byle jak, to trzeba tą siatkę zmodyfikować tak dynamicznie, żeby…
To wtedy ta siatka po prostu musi wiedzieć o tym, że ten obszar jest teraz niedostępny. Być może to być może jedną z metod. To jest po prostu manipulacja kosztami na takiej siatce. To się na nas jakoś czasowo przerzucamy. Taki czasowy efekt. Mamy scheduler, ten Spring, wiadomo. I ta waga. Waga na milion. No i wiadomo, że nie opłaca się tam iść, bo ta droga jest bardziej kosztowna. A jak efekt wygasa przy kolejnych datach, to po prostu ten koszt się zmniejsza i siatka znowu się odblokowuje. I przy okazji możemy sobie debatować tam powiedzmy grafikę, zmieniając tekstury w sensie ani mówiąc te to całe przejście, nie? No czyli proste rzeczy zaczną wkrótce dalej tego nie ma. No tak. No i po tych siatkach, jak będą łazić, nic po nie. To nie chcielibyśmy. Tak, więc cieszcie się, jaka ta siatka jest. Jaką ma rozdzielczość? Gratulacje tych swoich oczek. No, chyba że o to chodzi. Jakiś robot. No, chyba że robot. No, może robot, może nie, ale jednak nie. Robot chodził tylko jakieś 5 dni, co 5 dni i pół nocy. W ogóle upiornie fajnymi takimi łukami się liczba kół porusza.
Drzewa decyzyjne i cele hierarchiczne
No tak, charakterystyka ruchu może być różna, co to musimy np. wygładzać? Nie ten ruch. Czyli tutaj wchodzą algorytmy wygładzania jakiś tam ścieżek. Tak. Czyli np. na początek wchodzi nam A* w połączeniu z siatką nawigacyjną, być może jakąś tematyczną. A na koniec wykładamy to algorytmem, który robi to wszystko takie bardziej naturalne, bardziej naturalne, tak jak chodzą ludzie albo upiory. Tak jak się uporał, tak jak w poprzednim odcinku wspominaliśmy o tym omijaniu dorożki, bo w ostatnich latach te postacie nie omijają ich. Także 90° w prawo i w lewo i takim kwadratem tylko omijamy. To płynie tak jak ludzie i większość ludzi omija przeszkody. No bo inaczej z tą kwadratową będzie, będzie mocno widoczna. No, no dobra, czyli mielibyśmy sterowanie ruchem obiektów jako taką klasę algorytmów fajnych i też do zabawy.
Tutaj zgrabnie możemy przejść już do tych algorytmów grafowych, bo ośrodki nawigacyjne właśnie to są jakieś tam grafy. No tak. Czy ogólnie, jeżeli mamy nawet taka najprostsza rzecz jak taka mapa właśnie złożona z jakichkolwiek kafli, to to już jest właśnie siatka nawigacyjna. Mimo, że nawet nie ma kosztów przejść pomiędzy poszczególnymi węzłami, może po prostu być nałożona na ona może sama tworzyć topologię terenu też, czyli po prostu tam gdzie jest węzeł. W tej siatce jest obszar map, po którym można przejść. W Simsach to mamy np.. Nie wiem, jak to jest pod spodem, bo nie zerkałem w kodzik. Ale tam jest także, że po prostu na poszczególnych kaflach można stawiać, a na innych nie można stawiać, więc one mogą być w ogóle wyjęte z tej siatki. Tym bardziej, że taka mapa – „hello” – ona nie przewiduje tego, że się np. terra formuje jakiś tam kawałek skały i nagle można wejść na niego i już tam. W sensie, że on zmienia swoje. Po utworzeniu mapy on zmienia swoje przeznaczenie. Raczej nie. Ja jeszcze przynajmniej w swojej historii Simsów nie spotkałem się z takimi mapami, gdzie można by było coś tam zmieniać. Jak już trzeba gdzieś się przenieść z takiego obszaru zamkniętego, to jest portal, ale to już jest wyjście i wejście zrobione po prostu sztucznie. Natomiast cała cała siatka może być za hard-kodowana i też dobrze.
No i na takiej siatce też możemy sobie wyszukiwać, wyszukiwać ścieżki. Właśnie przeglądając chodząc po grafie wiesz, że tutaj jest siatka nawigacji na dyski graf. Trochę dwuwymiarowych i ciężko jednak bez tych wag by było coś tam. Tak mi się wydaje. Po przeliczać. Pewnie odpowie tak, bo to jest zawsze jakieś tam modyfikatory czy inne współczynniki wchodzą w grę. Potem ten ruch nie jest taki płaski zupełnie i. No ale ta siatka nawigacyjnej w formie grafu też nie musi być super taka jednorodna. Więc jeżeli mamy jakąś tam topologię tego terenu to możemy sobie na to nanieść tą siatkę i wtedy nam powstaje graf, gdzie już faktyczne przejścia pomiędzy punktami nawigacyjnymi są określone odpowiednimi kosztami i wtedy wiemy, czy możemy przeniknąć przez tę barierę, czy nie możemy sobie jakąś tam barierę postawić. Możemy ją zdjąć, zmniejszając ten koszt. Czy nasza postać dokonuje teleportu, czyli przeskakujemy w ogóle więzami naszego grafu? No to to też.
To specyficzne zastosowanie, czyli nie idziemy po krawędzi grafu, a możemy się przenieść w dowolne miejsce w tym grafie. Ale to chyba też dobrze by było, jakby była określone, określona możliwość przejścia. Pewnie tak jak się należy na poziomie grafu, to by to jednak byłaby krawędź po prostu narysowana. W sensie jakbyśmy ten graf zwizualizować, to byłaby jakaś krawędź, która ma przypisany jakiś tam koszt? Pewnie. Poruszenia się po takiej sytuacji też można sobie wyobrazić. Przetransportowanie się do jakiegoś wydzielonego fragmentu wcześniej niepołączonego grafu. Bo mogę się przez teleportować na jakiś np. heks, który jest taką malutką wysepką. Czyli normalnie tam nie przejdę, ale mogę się tam teleportować. Czyli to będzie. No dobra, tak, tak. To w Simsach było faktycznie to może możliwe. To jednak meble były takie. Czy można było taką enklawę zrobić, gdzie nie w ogóle nie dojdziesz? Jest tylko teleport. Wejściowy i wyjściowy, np. nie. Albo dwukierunkowy. No, no to chyba jednak zrobili. Zresztą dziwnego nie zrobili, fajnego.
No. Tylko że tak. Te algorytmy grafowe to nie tylko do poruszania po terenie, ale też do jakiegoś poszukiwania rozwiązań problemu. I np. mamy taki graf w postaci drzewa. W szachach czy w warcabach? W sensie możemy stworzyć, możemy przeszukiwać go, jak już mamy. W zależności od tego, jaki problem rozwiązujemy, albo bardziej nam pasuje przeszukiwać ten graf wszerz, albo w głąb. Przeszukiwanie wszerz to jest sprawdzanie najpierw sąsiadów tego węzła, w którym siedzimy względem tego samego rodzica, a w głąb to jest pójście po ścieżce, aż dotrzemy do węzła liścia, do liścia, do węzła terminalnego. I jeżeli chcemy dalej ich sprawdzać, np. jeszcze nie jesteśmy kontent. No to wracamy gdzieś tam i badamy sens odczytania, czy wracamy o jeden poziom i badamy wszystkie potomka. I od tego momentu znaleźliśmy już zakończenie. Więc wracamy raz w górę i badamy jakąś ścieżkę znowuż, aż do znalezienia tego w głąb. I tak możemy sobie takie drzewo. Ono będzie wyglądało wizualizowanie jak taki piorun, fajny, nie? Bo bo uderzenie pioruna. Takie przeszukiwanie w głąb, hahaha. Bo tak, bo po co? To jest fajna analogia. Rzeczywiście dopiero szuka tej drogi, pierwszej drogi, a tego jeszcze brakuje. To wraca. Jesteśmy sru, wraca. No nie, nie widziałem takiego pioruna. Czyli piorun przeszukuje w głąb, w głąb, ewentualnie z kulistym. No tak, to już jest inny pasażer, nie przeszukuje, nie przeszukuje.
Także zależnie, co chcemy rozwiązać, do takich czy innych tam zagadnień bardziej nam się przyda przeszukiwanie wszerz, a do innych w głąb. Np. jak szukamy pierwszych ruchów dla partii szachów, to pewnie wszerz będziemy przeszukiwać, jakoś to mniej. Jest dużo jeszcze możliwości, ale w którymś momencie, jak już mamy dobry scenariusz, to już szukamy w głąb. Do czego nam to doprowadzi? Tak, bo to może już doprowadzić do finalnego rozwiązania. Jeśli już tych ruchów jest na tyle mało, że możemy znaleźć taką kombinację ruchów, że… Na razie to ewentualnie możemy połączyć przeszukiwanie w głąb z wyszukiwaniem wszerz gdzieś do jakiegoś, szukać w głąb do jakiegoś poziomu. I tak się właśnie w szachach robiło, że szukamy w głąb do któregoś tam ruchu wprzód w drzewie, ale szukamy wszerz wszystkich możliwości takiego ruchu, w sensie wszystkich możliwości wykonania kilku ruchów do przodu i badamy zysk z tego i wybieramy na tej podstawie strategię. To takie podejście proste, można by powiedzieć, do rozwiązywania problemu szachowego. Co tam jeszcze, bo przecież. A no i cykl opóźnień.
Cykle i drzewa decyzyjne
Te grafy to są takie nic, poniżej mogą mieć cykle. Tak, to znany problem w Windowsie. Windows znajdzie cykl u siebie, to się zawiesza i robi update na niebiesko się kończy. To jest cykl w grafie, w grafie Windowsa. To jest cykl i ten może się zastanowić i takich cykli uniknąć. Można uniknąć, rozpisując graf jako drzewo. Ale jak się nie da, to co? Ja bym tam trochę zliczał te cykle i po prostu tak, ile razy po którymś razie już się zniechęcił. No to może nas spotkać najprostsze wyjście. Jeśli możemy, możemy to zaliczyć. Tak naprawdę, jeśli już drugi raz jesteśmy w tym samym, tym samym zbiorze wierzchołków i mamy ten cykl, no to już możemy tego nie szukać więcej. No chyba, że mamy akurat taki problem, że musimy ileś tam razy ponowić. To wszystko tak naprawdę jest pożądane i wtedy go wykonujemy i ta ścieżka, która to potem zapewnia. Oczywiście to, to jest nasze rozwiązanie, nie. Także te grafy to też fajne są klocki. Można. Można je tam ładnie przeszukiwać.
I właśnie tu wspomnieliśmy o drzewach. Z takiego grafu można zrobić drzewo decyzyjne, które ma po prostu różne zagadnienia na różnych poziomach. Czyli zaczynamy od jakiegoś roota i całe to drzewo opisuje rozwiązanie jakiegoś tam złożonego problemu. Czyli mamy, wychodzimy od jakiegoś korzenia tego drzewa. I zadajemy kolejno pytania: co? Co można zrobić? Nie. Jeżeli coś można zrobić, idziemy tą ścieżką, którą wybraliśmy, to na kolejnym poziomie mamy takie dość szczegółowe, dające pytanie, co dalej? Albo np. co tam. Ciężko zaraz wydłubać jakiś przykład, ale to trzeba by było wymyśleć jakieś tam zadanie. Dobra, nie będę, bo zejdzie za długo, ale w każdym razie takie złożone zadanie, które ma kilka kryteriów do wyboru, to wtedy można sobie rozpisać to jako drzewo decyzyjne i po prostu kilka jakiś tam badać różne, różne ścieżki i sprawdzać, która tam daje najlepsze rozwiązanie, czyli maksymalizować jakąś tam funkcję po prostu, przechodząc ten graf.
Hierarchiczne cele: strategia i taktyka
Tak i teraz hierarchiczne cele. Co to, to by było takiego? Ja tam wspomniałem wcześniej, że to była taka trochę wariacja na temat maszyny stanu hierarchicznej, bo maszyna stanu hierarchiczna to jest algorytm zagnieżdżony w algorytmie i tak może być wiele razy. To tutaj, jak byśmy mieli jakiegoś agenta, który realizuje jakiś cel. To możemy sobie wyobrazić ten algorytm jako w ogóle tą klasę algorytmów, jako i jako taką kompozycję celów z mniejszych celów do pewnego poziomu.
Taki trochę podział: strategia i taktyka. Strategia będąca takim nadrzędnym podejściem do rozwiązania pewnego problemu. A taktyka jako zestaw kroków, zestaw pewnych rozwiązań, które mają do tego strategicznego celu przybliżyć, dokładnie, czyli albo w ogóle go umożliwić. Metodyk naszej rzecz umożliwić, jakkolwiek? Tak, tak, tak. Czyli mamy naszego NPC-a, który powiedzmy jest jakimś Polakiem i chciałby ubić smoka i trzeba mu umożliwić ubicie tego smoka w tym naszym świecie. Trzeba mu dać po prostu możliwość pomyślenia o tym, że on by mógł tego smoka ubić i jak miałby to zrobić. I np. jego celem mogłoby być zdobycie najpierw broni, żeby tego smoka móc ukatrupić. Może trochę zdobycie doświadczenia, dowiedzenie się o tym, jak tego smoka. Kurde, zmierzam znowu w stronę Wiedźmina, okej? Znów taka ogólna analogia. A nie wyszło jak wyszło. No bo Wiedźmin to musi się przygotować, więc musi wiedzieć, jakiego potwora ma. Tak, więc musi najpierw pozyskać wiedzę na temat tego potwora pod lasem. Ale możesz analogia z drzewem zrobić. Tam, tam nie ma Wiedźminów. No, nie mogę. No to masz i to bóstwo, które burzę.
No dobra, no to musisz znaleźć miecz. Czy twórcę oburza to, że jest grupa wojaków, która chce ostrzelać zamek przeciwnika? Muszą najpierw zrąbać 13 sosen, zrobić 4 bufety, ustawić go gdzieś tam i dopiero strzelać. Nie no. Także najpierw muszą skumać, gdzie te sosny się rąbie, więc idą do majstra i dowiadują się, że w lesie. Czyli mają ten swój ogólny cel, którym jest zaatakowanie zamku wroga. Więc najpierw musiał sobie go zdekomponować na mniejsze, pozyskał środki do zaatakowania tego zamku i tu wpadają na pomysł. Zbudujemy burzę. Więc żeby to zbudować, to też ten cel dzielą sobie na mniejsze i idzie do lasu pozyskać drewno. Masz ściśle w oddziale na przykład. No to cieśla już z tego drewna wie, jak zbudować tę machinę, ale muszą mu wojacy przynieść tego drewna, więc cały oddział realizuje ten cel: zdobądź materiał, a następnie zbuduj. Następnie, jak już mają zbudowane, to jadą z nim na stosowne wzgórze czy gdzieś tam, żeby się zdeprecjonować i wycelować w ten nasz zamek.
To chyba nawet bardziej by tu pasowała ta warstwa strategiczna. To byłoby to nasze AI, które jest tym naszym graczem sztucznym. I ono sobie planuje pewne, pewne cele, czy np. musi zaatakować nas jako gracza ludzkiego. Więc żeby nas zaatakować, to co musi zrobić? Musi zbudować żołnierza tego busa. I to jest ta część strategiczna. To trzeba zrobić. A taktyczna to właśnie to. To wojsko musi podjechać pod nasze mury i tam nasze mury uderzać. Tu jest ta część. Ale jednocześnie to się wszystko jakby przeplata, bo ta część strategiczna zarządza tym takim wysoko poziomowym jakby celem, co robić w danym momencie, czy w ogóle budować to wojsko, bo może go nie trzeba budować, a zasoby przeznaczyć na jakieś tam ulepszenia, ale to już zdemaskowalibyśmy go, że on oszukuje.
No tak, ale no gdyby nie oszukiwał, gdyby nie oszukiwał, to mógłby tak. Zakładając, że to, to, to to ma poruszać się w ramach, w granicach prawa, prawa przypisanego teraz przez tą grę. Zakładam, że większość takich rzeczy jak też weźmiemy ten rodzaj Grand Strategy, czyli wszystkie Total War i tego typu rzeczy, tamto mamy jeszcze na wyższy poziom podniesione, bo w takich Total Warach to ta warstwa strategiczna jest mocno rozbudowana, bo na tej mapie, gdzie budujemy sobie jakieś ulepszenia w miastach, budujemy wojsko, a część taktyczna jest na zupełnie osobnej mapie, gdzie przenosi się starcie. Bo tak, to już jest nasze starcie, to jest część stricte taktyczna, gdzie mamy oddziały, mamy formacje, mamy ukształtowanie terenu i tam już są zupełnie inne reguły, no bo to już walka sensu stricte, a mapa strategiczna, gdzie też trzeba podejmować jakieś decyzje, bo może chcemy zająć tą prowincję, może chcemy jednak coś ulepszyć, może chcemy zaatakować, może sobie cichutko, licząc, że nikt nas nie zauważy i po prostu przetrwamy do końca tej tury i więcej nas nie interesuje? Nie wydaje mi się.
To są takie dwa osobne poziomy i to hierarchiczne. Ogólne algorytmy takiej klasy właśnie od planowania takich dalekosiężnych celów właśnie służą do tego, żeby jednak ta nasza sztuczna inteligencja jednocześnie planowała z wyprzedzeniem większym niż ta jedna tura, bo w tej konkretnej turze może się dużo dziać, np. mogą mnie wszyscy atakować, ale ja też jako sztuczna inteligencja, nie oszukując, mam też jakiś plan, taki globalny, na zasadzie, że chcę być potęgą wojskową. Więc mimo tego, że teraz mnie nikt nie atakuje, to i tak robię to wojsko, bo ja chcę. Doradca kulturalny jest tam stłamszony, dokładnie, elitarny, dokładnie, dokładnie tak samo jak ci doradcy cywilizacji działają. Gdzie coś też cywilizacji. Sztuczna inteligencja grająca z nami musi mieć dokładnie te same, te same przemyślenia, bo musisz zastanowić się, czy rozwijać. Czy atakować, czy budować, czy to coś więcej. Więc po to są. To są rzeczy, które są na zupełnie innych poziomach i też nie mogą się tak super mocno zakłócać. No bo inaczej będziemy mieli strategię, która co turę się zmienia i w jednej turze idziemy w wojsko, ale w drugiej turze idziemy w naukę i logika zanika, który tam sobie nie wysyła B, bo to nie nastąpiło. Żeby to przyniosło jakiś efekt, musi to być takie wrażenie trochę, że to jest troszkę długofalowe, przemyślane, niż po prostu co turę losowo wybieram, że robię wojsko, robię, robię, idę w naukę. Więc to jest tak, tak. I to właśnie te cele strategiczne. Fajnie to zilustrować, że tutaj taktyka się przeplata ze strategią i na tym najwyższym poziomie jest strategia, ale jeszcze dodatkowo te cele są z określonymi priorytetami. Tak napisałem np. ten akurat wariant. Ta strategia po prostu ma cele ustawione na takie priorytety, bo celów zawsze jest masa, brak celów jest zawsze masa, ale mało tego, bo te priorytety mogą się zmieniać w czasie, bo np. rozbudowujemy się i powiedzmy i ciśniemy na koniec mapy, gdzie jest baza wroga, żeby go tam stłamsić. Ale nagle nas kolega atakuje tu z prawej strony i się nagle priorytety zmieniają. Bo jak gracz ludzki coś takiego zaobserwuje, to już traci.
Priorytety celów i triggery
Powiedzmy, no dobra, nie tyczy się to Koreańczyków, bo oni to widzą wszystko wszędzie w StarCrafcie, nie. Ale taki przeciętny gracz, zwykły szary Europejczyk, to zwykle straci animusz. Ten jego atak gdzieś tam, jak go tutaj atakują, więc sam przekieruje siły, żeby się obronić tutaj. Tym bardziej, że może nie być wyjścia i może w ogóle trzeba wracać z tego ataku, żeby tymi resztkami, które zostały, obronić są bazy, bo zaniedbaliśmy tutaj obronę i nie mamy czym, albo już już straciliśmy. Nie tak samo i może mieć po prostu podmianę priorytetów, jak zobaczy, że podczas realizacji tego celu jakiś inny cel wskoczył. I jak to programowo zorganizować? Przy takim hierarchicznym rozłożeniu celów, np. za pomocą stosu, nie. Czyli po prostu mieć te cele zdefiniowane, poukładane w określone hierarchie. Ale w sytuacji, kiedy realizujemy coś i nagle coś innego jest wyżej wypromowane, to po prostu to wskakuje, przysłania nam ten cel. Tak jak w życiu, przesłania nam cel bieżący, jakiś nowy cel, który na stos nam wskoczył na samą górę. Zapominamy o starym celu i jedziemy dalej. Jedziemy, aż się wypali z tym i nagle ten pęd się skończy, albo coś się stało gorszego. I wracamy do poprzedniego. I nagle tamten się okazuje dobry, nie? No to tak samo.
W szczególności właśnie te cele mogą też fajnie być sterowane skryptami, bo często np. w takich grach, jak Total War, kiedy mamy scenariusze, które są oparte na faktycznych wydarzeniach, jest tak, że np. wybija jakiś rok, gdzie wiemy, że np. tam prowincja zaatakowała jakąś inną prowincję, to te cele są jednak tym trikiem skryptowym włączane na zasadzie, że hej! PS: poprawność historyczna, poprawność historii. Czasami są takie same, które są historycznie poprawne, bo chcemy, żeby takie było. Żeby odwzorować pewne zachowanie, chcemy wymusić, żeby dana prowincja czy państwo akurat w tym roku zaatakowała nas, bo tak było naprawdę, więc chcemy to też wymusić. Więc to musi też być. Musi to być takie mocno elastyczne i łatwo konfigurowalne. Coś tu jeszcze. Miałem taki przykład, ale chyba mi wyparował w międzyczasie. Dobra, jak mi się przypomni, to to jeszcze powiem, bo o tym odkładaniu na stos było. No nic, no dobra, to na razie tyle tych hierarchicznych celów.
Może teraz byśmy o tych triggerach jeszcze wspomnieli, bo to jest też taka fajna rzecz, która w klubach nie występuje. Tam się za dużo nie triggeruje, no chyba że czasami wszystko. Nic tych klubach nie występuje. Nie wiem, jak my to robimy i jak to pogodzić. Triggery na takiej mapie czy gdzieś tam w przestrzeni gry, bo przecież to nie musi być mapa w rozumieniu mapy, to logiczne. Jakieś tam mapy terenu LGD są w bazie danych są, a faktycznie jednak set kieruje się trigger. Dobra, to nie takie głupie te bazy. To np. w platformówkach na porządku dziennym, czy w ogóle gdziekolwiek w jakiejkolwiek praktycznie grze, mamy obiekty, które jakoś tam reagują na to, kiedy się ktoś do nich lub coś zbliży. Na przykład jakieś materiały wybuchowe lubią wybuchać w tym momencie, nie. Czerwone beczki, czerwone beczki zwykle robią wybuchy i tylko najpierw pęcznieją, więc mamy trochę czasu na klepnięcie. Nie zawsze stoją wszędzie. Nie, żeby tam ktoś kiedyś posprzątał, bo grożą wybuchem. No wiesz, zawsze stoją. Jakbyś posprzątał w newralgicznych punktach, nie posprzątał, bo jak się zbliżysz, to wybuchnie. No po prostu tego nie posprząta. Idzie nasz bohater albo jakiś NPC, ktokolwiek, nie i po prostu zignoruje takiego, wybucha. Aha, albo coś innego wyskoczy, jakaś manetka czy coś tam. Nie, Mario i wyskakujące monetki czy w ogóle niezliczone ilości platformówek. To są właśnie rzeczy, które są obsługiwane takim mechanizmem.
Jasne, poprawiłem podział wypowiedzi, grupując je w bardziej logiczne i spójne akapity, zgodnie z kontekstem rozmowy.
Wyzwalacze w grach
Trigger, czyli po naszemu wyzwalacz, to obiekt w przestrzeni, który jest monitorowany. Jeżeli coś znajdzie się w jego pobliżu – coś pożądanego, na co jest wyczulony, np. nasza postać – to zachodzi między nimi interakcja i coś się dzieje, jakiś efekt się odpala. Mechanizm ten wydaje się banalnie prosty, bo trzeba tylko monitorować tablicę takich obiektów i bliskość gracza.
Silnik to załatwi, ponieważ w centralki gry mamy po prostu sprawdzanie takich bytów. Sprawdzamy, czy coś nam się pojawiło pasującego do wzorca w okolicach któregoś z naszych obszarów. Jak tak, to ślemy odpowiednie zdarzenie na szynę, że tak to się właśnie stało i ktoś tam coś z tym zrobi. Albo bezpośrednio obsługujemy to zdarzenie, niech tam sobie coś wyemituje.
Budowa autonomicznego agenta
Tym sposobem, gdybyśmy poskładali sobie teraz te dużo ciekawych klocków w całość, mogłoby się okazać, że z takich składowych, albo z algorytmów, albo z klas algorytmów, jesteśmy w stanie zbudować np. autonomicznego agenta.
Taki agent jest skryptowy, „czuje” ten świat, bo ma jakieś czujniki – potrafi się podpiąć do nich, potrafi zareagować na zdarzenia, które tam występują, czy wręcz monitorować parametry świata lub obiektów świata. Po prostu je sobie przegląda, ma jakiś plan działania, jakąś strategię. Pewnie skrypt może mieć zestaw swoich celów, także hierarchiczny. Może to być podzestaw celów całego AI.
Jeżeli to jest np. generał, który podbija kolejne prowincje i to on jest tym agentem, to będzie realizował aktualnie najbardziej priorytetowy cel tej strategii militarnej, powiedzmy. Może się poruszać autonomicznie. Jeżeli będzie np. takim NPC ścigającym nas w jakiejś strzelance, to nawet musi. No bo jak będzie stał jak kołek, to go pobijemy, albo po prostu nie zrealizuje swojego celu, więc musi mieć jakieś możliwości poruszania.
Błędy w AI gier – przykłady
A czasami to często taka kategoria bugów, że np. jakiś NPC przeciwnik odblokuje się i wtedy łatwiej jest go ubić. Często są to takie proste bugi, co wynika z takiego. To są po prostu bugi, które troszkę rujnują pewną immersję, bo jednak ta postać ma wykazywać pewne zachowanie czy jakąś wielkość. Ten agent nagle stoi jak kołek pomiędzy beczką a stołem. Nie może się wydostać, a my ją tam z łuku cały czas, czy z innego Fireballa, uderzamy. No i cieszymy się, że pokonaliśmy tak łatwo.
Przykład z Heroes of Might and Magic
Taka mapa wolno przechodzi. Jak się grało demonami, to można było sobie jednym takim małym stworkiem guzikiem rozwalić bohatera przeciwnika. Wystarczyło podejść pod odpowiednim kątem i on tam stał. A ten go już miał w zasięgu i wystarczyło go po prostu postawić i nic więcej. On strzelał do niego. Tamten się nie zorientował, oczywiście, że coś tam się dzieje. Raz na jakiś czas tylko tam się otrząsnął, bo mu się zmieniały parametry, więc tam jednak coś odczuwał trochę, czy tam rzucił jakimś grubym tekstem, jak to mieli w zwyczaju, ale potrafił zginąć.
No i ten levelował nieskończoność, bo przychodził następny na jego miejsce. Albo podchodziło się znowu pod nowego, bo po prostu dystans ataku tego ziomka był większy niż widoczność. Stąd taki błąd. Ale można było jednym takim działkiem rozwalić taką mapę. Nie interesowało tego NPC, że życie mu ubywa bez powodu i tak. Może go interesowało, bo tam raz na jakiś czas coś skomentował, ale nie miał żadnego zachowania, żeby zareagować. Nie miał zachowania, które nawet losowo poruszyłoby się w stronę. To by umożliwiło czasem wykrycie i podejście w zasięg widzenia tego przeciwnika. Tak po prostu.
Przykład z Wiedźmina i Skyrim
Te klasy bugów są świetnie pokazane w „Viva La Dirt League”. Tam to, co oni pokazują, jak to NPC robią wszystko dla królestwa czy tam dla swojego króla i nie widzą, że tam jest kolega zagrożony pod bokiem. Czego, np. niestety, muszę tu przytoczyć przykład z Wiedźmina. W trójce nie zrobisz, w dwójce to nie wiem, ale w jedynce dało spokojnie strażnika tamtego załatwić gdzieś obok i nikt inny się nie zorientował, bo jeszcze NPC nie reagowały na to. Ale w trójce, jak już kogoś uświadczysz w mieście, to wszyscy widzą to, wszyscy widzą, a już odpukać – zrobić to tuż przy strażniku, to wtedy strażnik wskakuje w takim levelu, że nie idzie go załatwić.
No tak, to jest właśnie kwestia tego, jak dużo takich interakcji ze światem jesteśmy w stanie zakodować. Bo właśnie zabijanie strażników w miastach w grach to jest popularna rzecz, oczywiście. Kradzieże wszelkiej maści, tak jak również przez słynny bug w Skyrim, gdzie zakładaliśmy sprzedawcy wiadro na głowę i mogliśmy wszystko kraść w jego pokoju, bo wiadro na głowie tego sprzedawcy czy postaci blokowało już pole widzenia. Czyli ten obszar zmiażdżył zderzakiem? Po co mają wiadra na głowie? Można było spokojnie wszystko okradać.
Ale tutaj ten mechanizm obserwacji kradzieży był wbudowany, bo normalnie jak tego wiadra nie było, to było kopiować, ale nikt nie pomyślał, że wiadro na głowie jednak blokuje. To oczywiście może dlatego nie przeszkadzało im aż do Was, że blokować to jeszcze może blokować, ale żaden pies się nie zdziwił, że nagle ma wiadro na głowie. Nie było takiego „ifa” tam. Nie było, no nie przewidzieli. Kto rozsądny zakłada NPC-owi wiadro na głowę?
Ewolucja AI w grach
Skomplikowane vs. proste rozwiązania
Do tego możemy takiemu agentowi dołożyć skrypt, jakąś ontologię, niech tam sobie słucha na takie czy inne zdarzenia, czyli połączy to wszystko, ma jakieś cele hierarchiczne nawet. No i mamy agenta sprytnego, który pomyka, nie można mu wiadra na głowę założyć – od razu się zorientuje, że coś jest nie halo. Potrafi swoimi celami manipulować tak, żeby osiągnąć ten główny cel. Potrafi się dopasować do sytuacji, bo potrafi sobie przełączyć cel, jak mu się zmieniają jakieś tam rzeczy. Potrafi inteligentnie ominąć przeszkodę po jakiejś wygładzonej ścieżce. Potrafi, oczywiście, idzie po siatce nawigacyjnej, no bo inaczej nie umie. Albo leci po siatce nawigacyjnej, bo do tej pory to siatki w 2D tak zmontowali. Ale przecież są gry, gdzie się porusza w przestrzeni 3D. No tak, ale to już wyższy hardkor – kolejny wymiar.
Nawet tu widzimy, jak dużo elementów algorytmicznych, bo powiedzmy sztuczna inteligencja, bo tu są jakieś, powiedzmy, algorytmy wspomagające szeroko rozumianą sztuczną inteligencję. Ale jak dużo musimy zaimplementować, żeby zrobić takiego prostego, wydawałoby się, NPC w grze, która jeszcze pogada i pogada, gdzie większość graczy traktuje to jako zupełnie oczywistą rzecz: „To przecież jest NPC. Pokaż mi swoje towary”. Tutaj sobie stoi coś tam i tak naprawdę. A przecież współczesne gry, otwarte światy, takich NPC-ów to mają dziesiątki, setki.
Ale napiszesz go raz? Już go masz. Tak, tak. Jeśli mówimy o takich podstawowych mechanizmach, one oczywiście są, są przekładane. Ale jeśli do tego dodamy jakieś linie dialogowe, które nie są powtarzalne, to to już jest sprawa danych. Skrypty, teologia, zadbanie i w ogóle cała parametryzacja. To już nawet gość od tego pierwszego mówił, że tak naprawdę ta cała warstwa danych im się przekształciła w taką pokaźną bazę danych. Po prostu tam, gdzie poszczególne typy jednostek były opisywane tabelami w tej bazie. No, przyjmijmy po prostu tak, przyjmijmy, że były opisywane tabelarycznie. I to były duże zestawy danych. Taka jednostka nie. Czyli to nie było to, że był jeden bajt na zdrowie, jak worek kapci, że miał zdrowia 255 czy 256. Nie pamiętam, ile tam było. Zdrowia, tak i koniec. No kilka parametrów. Nie, nie, co tam.
Na pewno naprawdę musi być dużo parametrów, bo oczywiście oprócz statystyk jednostki, która czyta postaci tego naszego aktora, agenta, to musi się znaleźć w tych parametrach. Ale oprócz tego są parametry opisujące po prostu zachowanie. Działanie tych algorytmów, które tam użyjemy. To musi być jakoś umieszczone albo przy jednostce, czy też na zewnątrz. Mamy ogólnie mix różnych parametrów wszystkich tych algorytmów per poszczególne postacie czy obiekty. To nie jest tak, że wszystko jest od jednego, od jednej instancji i wszystkie postacie zachowują się tak samo, bo przecież część może nas zaatakować szybciej, część w ogóle. Część ma takie nastawienie, porusza się też inaczej, więc to wszystko. Część będzie beczką, która tylko wybuchnie.
Architektura gier i dziedziczenie modeli
Dokładnie, ale prawdopodobnie bardzo wiele z modeli mimo wszystko dziedziczy. To też był taki casus. Już nie pamiętam, w jakiej to było grze, ale zdaje się, że właśnie element, jak to było, że samochód przemianowany z jakiegoś obiektu, który był zupełnie innym obiektem, ale jakoś to tak fajnie w ich modelu pasowało, że bardzo łatwo można było zmieniać dorożka—samochód, który wcześniej był tym stołem. Wystarczyło podmienić tylko tam model, teksturę, parę parametrów i można było jeździć na takim czymś z dokładnością do wybuchu. No właśnie nie pamiętam, bo jak zacznie wypadać, to wybuchnie, to wiadomo.
I to też pokazuje, że właśnie to nawiązuje do tego, co powiedzieliśmy na początku dzisiaj, że architektonicznie i algorytmicznie taka gra to jest mistrzostwo świata. Jeżeli ona musi tyle zagadnień ze sobą połączyć, współczesne gry to zdecydowanie muszą te zagadnienia ze sobą połączyć, więc to muszą być odpowiednio odseparowane fragmenty kodu, które ładnie ze sobą rozmawiają, nie jakieś komponenty, które i często to jest jednak treść, ale są to komponenty, które mają dobrze określoną odpowiedzialność, swoją własną.
I jak się spojrzy np. na trzewia takiej sceny w Unity np. zrobionej, to tam już jest, tam to widać jak na dłoni na przykładzie samego modelu obiektu gry. Gdzie możemy sobie dodawać jakieś kształty do scenek, chcemy jakąś logikę za tym idącą podłączyć, to wtedy dołączamy odpowiednie klocki do tego naszego „gameobjectu”. Te, które zawierają to czy tamto, to będą placuszki algorytmiczne w postaci kodu. Tak, tak, tak, albo już gotowego np. Nie tak. Gotowe komponenty odpowiadające za poszczególne fragmenty animacji, albo za całe za jakieś tam w ogóle poruszania się po ścieżkach. Te wszystkie elementy tam są i też nasze tak zwane skrypty, które piszemy tam akurat w C#, to one właśnie są też takimi fragmentami logiki dokładane. Ale już jako dodatek do gotowego obiektu, czyli ten obiekt gry, z którego budujemy np. naszego agenta, to jest po prostu pudełko, do którego wrzucamy poszczególne placuszki i w zależności od tego, jaki ich etap połączymy, ile ich nawrzucamy, to on ma takie, a nie inne właściwości, bo to wszystko w jakiś sposób jest w stanie podpiąć się w miarę potrzeb swoich własnych do pętli gry i do tego mechanizmu „update”. No i skomplikowana rzecz. Skomplikowane rzeczy, innymi słowy.
Losowość w grach – optymalizacja i realizm
Ale właśnie czasami to nie musi być takie skomplikowane. Nie musi, bo tutaj omówiliśmy bardzo dużo mniej lub bardziej skomplikowanych rzeczy i podejść. Ale czasami tak naprawdę wystarczy zwykła losowość, bo jakbyśmy chcieli np. zamodelować celność takiego bota, który strzela do nas z karabinu, to nie musimy wprowadzać przecież jakiegoś skomplikowanego algorytmu, jakiejś tam sieci neuronowej, którą szkolimy do strzelania. Wystarczy też jakoś specjalnie tam wpływać na lot pocisku jakimś podmuchem wiatru gdzieś tam, ale wystarczy rzut oka. Trajektoria w miarę prosta, delikatnie opadająca parabola.
Ale wystarczy w takich przypadkach, gdzie chcemy odwzorować jakieś takie cechy troszkę fizyczne, bo wystarczy wylosować troszkę rzeczy i to będzie nasza załóżmy celność. Tylko że to wtedy nie jest takie losowanie zwykłe z takiego rozkładu jednorodnego. No bo celność to raczej nie jest cecha, która jest od zera do stu. Tylko raczej każdy ma tam jakąś naszą średnią i możemy tylko sobie czasem lepiej, czasem gorzej strzelić. Czyli jakiś rozkład normalny. Rozkład normalny dla większości takich cech atrybutów właśnie w grze, czy to wzrost naszych NPC-ów, wysokość drzewek, drzew, to dotyczy się też natury, oczywiście. A i algorytmy generowania np. świata tak dokładniej, i one one wszystkie generują to zmienną z rozkładu normalnego. To wynika z właściwości świata i z tego, że z takiej prostej. To jest Centralne Twierdzenie Graniczne. Aż sobie kurka aż sprawdziłem, ale poszłaś. Nie mówię tego z głowy, ale żeby to wyjaśnić.
Generalnie twierdzenie graniczne mówi nam o tym, że suma liczb z różnych rozkładów, średnich liczb z różnych rozkładów, różnych parametrów, zawsze ma przypominać w rozkładzie kształt takiego właśnie dzwonu. To nie jest taki rozkład jednostajny, tylko jednorodny, tylko takiego właśnie dzwonu, czy np. wzrostu ludzi. Mamy większość ludzi, którzy są średniego wzrostu, mamy część ludzi niższego wzrostu, najczęściej mniejszą znacznie niż to, niż to średnia, większego. To taka naturalna rzecz. Widzimy ją wszędzie w przyrodzie, no i w grach. Jeśli mamy takie mechanizmy, które też na tym bazują, jak generowanie świata, tak jak mówiłeś, czy jak celność naszego czaru, naszego strzału, tego typu rzeczy, to do tego stosujemy właśnie rozkład normalny Gaussa. No bo to najbardziej jest taki naturalny rozkład i wtedy te wyniki są najbardziej zbliżone do tego, co obserwujemy w świecie, w świecie rzeczywistym.
Błądzenie losowe i generowanie świata
Natomiast w grach ekonomicznych np. bardzo często mamy sytuację, kiedy musimy wylosować niejako czy wygenerować ceny surowców. Tak jak np. w biciem jakiś wykopują giełdowy i co tam rośnie, skacze. Jak w grze jesteśmy, to w polonizacji wkurza, bo tak tam Korona ciągle nakłada podatki. Strasznie się wahają te ceny na niekorzyść. Tak, no czasami mam oczywiście efekty na to wpływające, ale w większości przypadków, załóżmy, latamy sobie po planetach czy to w „Mass Effect”, czy za chwilę „Starfield” wyjdzie i może będą jakieś ceny surowców czy coś takiego. I wchodząc sobie na daną planetę, widzimy jakiś taki ładny wykres, który wygląda tak naturalnie. Tutaj coś nam rośnie, tu maleje i tak sobie ładnie, ładnie chodzi, i do tego zwykle stosuje się tak zwane błądzenie losowe, czyli algorytmy, które mając pewien punkt wejścia.
Czasami też ograniczenia, bo możemy nie chcieć, to błądzenie losowe w kosmos wystrzeliło w górę czy w dół. Można? Chyba że kosmicznej. Chyba że to jest cena Bitcoina, to może wtedy pójść do góry. Nie samolotem, ale spaść. Dawidzie, a tak 51 to jest prawdziwe błądzenie w Bitcoinie.
I wracając do tego naszego zasobu, możemy sobie w łatwy sposób wygenerować cenę takiego surowca. Możemy sobie to ograniczyć pewnymi granicami. Przy odpowiednich parametrach możemy też nadać cel temu błądzeniu losowemu. Czyli możemy dążyło do jakiegoś. Dokładnie, dokładnie, bo możemy mieć jakiś. W naszym scenariuszu naszej gry w pewien okres, załóżmy po 100 turach, cena jakiegoś kosmicznego minerału wyskoczy bardzo dużo, bo tak przewiduje scenariusz. Więc mamy sobie błądzenie losowe z odpowiednim parametrem, który skacze zupełnie losowo, ale jest zbieżne do tego naszego parametru, który mamy zadanego. Jakaś taka można powiedzieć granica bez gracza to jest super naturalne. On widzi tą cenę i rzeczywiście wygląda naturalnie, ale my wiemy, że ona sobie tam zbiega do tego punktu, który wyznaczyliśmy w przyszłości, bo takie mamy założenia.
Więc to jest bardzo fajny, naturalny mechanizm, a co lepsze błądzenie losowe też polega na generatorze liczb losowych. Oczywiście to wszystko jest prawdą i np. mając taką grę kosmiczną, gdzie mamy setki planet, jeszcze raz statki, gdzie będzie 1000 planet z tych surowców, pewnie tam wszystkiego będzie dużo, ale tylko 100 ma być zamieszkałych, twierdzą twórcy. A reszta co? Nie nadaje się. A reszta nie wiadomo. Może będzie życie, może nie będzie, nie wiadomo.
Przechowywanie danych w grach
A ten „Starfield” to będzie co? Jakieś MMO? Nie, to będzie. To będzie taki stołowy, samodzielny, taki kosmiczny Fallout, troszeczkę musztry. Bo to, co twórca miał tysiąc planet do zwiedzenia, tak twierdzą twórcy. Tak, kiedy to pierwszego września, już pragnę tego. Tego, którego jutro, pojutrze, pojutrze. Dokładnie, jak to premiera w wersji automatu. Tak otwarta już, pełna premiera zdaje się szóstego, więc może w następnym odcinku już też zebrało się. Kiedy nagrywamy, to już słuchacze to już pewnie gracze i albo my już gramy.
W każdym bądź razie, zakładając nasze tysiąc planet, może już nie trafi do przodu naszej grze. Mamy tysiąc planet, albo 10 000. Nieważne. I teraz załóżmy, że mamy X surowców. No i mamy oczywiście ten nasz piękny wykres tego naszego surowca. I nie chcemy chyba za bardzo trzymać i pamiętać tego wykresu dla wszystkich planet, dla każdej jednostki czasowej. No bo to może być troszkę przy pewnej skali problematyczne, więc wystarczy zapamiętać ziarno generatora losowego i możemy w bardzo łatwy sposób dla każdej planety, w dowolnym czasie, dla dowolnej tury, wyliczyć wartość w danym punkcie.
To jest takie super fajne. Super fajna optymalizacja do wygenerowania takiego ciągu losowego, pseudo losowego, który wydaje się mega naturalny, a jednocześnie może być zaprezentowany dosłownie jedną liczbą, „seedem” tego waszego random generatora. I jeszcze mi teraz znowuż przypomniałeś właśnie takich klas algorytmów pseudo losowych do generowania w ogóle całych światów. Jeżeli są generowane proceduralnie, to nic z tego. Z tego to troszkę się wiąże. Czyli ten „seed”, który jest jednak dzbanem i on nadaje cały świat porządnie naprawić. Może być nieskończoność. Dokładnie.
To tak. Czy to nie piękne? Czyli losowość. Czasami już tak, czasami losowość jest mniej czy bardziej zamierzona, ale jest potężnym narzędziem, tak i bardzo często się wspiera. Takie algorytmy symulujące tą naszą taką inteligencję. Czyli to, że gra sobie tam liczy coś fajnie albo generuje ten świat inteligentnie, a tak naprawdę to wszystko jednak jest losowo z jakimś tylko, powiedzmy, takim wstępnym zaczynem. Czyli taka. Takie oszukaństwo. Ale sprytne.
Tylko to też wymusza takie ograniczenia, jak na przykład zakaz podmiany tego generatora na jakiś lepszy. W kolejnym np. w lidze się oczywiście wszystko rozsypie. Tak, tak, tak. Trzeba pamiętać, że wszystkie rzeczy użyte do generowania muszą być niezmienne. Jeśli zmienimy generator i tekstury, to oczywiście, że na niedzielę wyjdzie nam coś innego. To idą krzaki właśnie.
Mapy wpływu w grach strategicznych
No dobra, to co? Przejechaliśmy po algorytmach. Jeszcze został taki jeden fajny, taki, który nawet kiedyś badaliśmy gdzieś w jakiejś hutniczej produkcji. Tak bardzo naszym podejściu to własnym. Mapy wpływu to też bardzo fajny mechanizm, bo też można podobnie jak w takich tematycznych siatkach nawigacyjnych po prostu narzucić na świat gry, bo to obojętnie, do czego to podepniemy. Czy jeżeli to będzie mapa naszego świata, to mamy mapę wpływu czegoś na jakieś cechy, na obiekty znajdujące się na tej mapie. Jak to będzie w platformówkę? Równie dobrze może to działać na tych platformach i na obiektach na nich położonych.
Ważne jest to, że obiekt, który roztacza wpływ jakiejś swojej właściwości, przy czym może to być dowolna właściwość, jaka nas interesuje. Roztacza go zgodnie z jakąś funkcją w tej przestrzeni i z własnościami tej przestrzeni właściwie, czyli uogólniając do przestrzeni Riemanna. Przestrzeń może być dowolnie parametryczna i rozkład tego parametru jest po prostu stosowną funkcją. I tutaj stosować, komentować. No to to szybko się robi. Gdzie funkcja w funkcji.
No ale efekt tego może być fajny, bo może się okazać, że np. jednostka boi się jakiegoś miejsca na mapie. I to tak się boi, że będzie to wyglądało naturalnie, że jak w tym Indianie w dłonie kartę, że się bali tego symbolu na skale, to nagle się okazuje, że pewien obszar mapy jest niedostępny. Ale to dlatego, że tu właśnie rozpoczęliśmy jakiś wpływ czegoś, co oddziałuje na nasze NPC i one po prostu unikają tego. Albo w drugą stronę, jest coś, co przyciąga te nasze NPC do jakiegoś miejsca, bo tam się znalazło źródło wpływu, jakiejś cechy, która generuje ten wektor akurat do niej i wtedy jakaś interakcja zachodzi taka.
I można to bardzo fajnie sobie modelować. Można też z danego punktu wyemitować ileś tam różnych wpływów, bo eksperymentowali z takim strategicznym wpływem. Na poziomie jakimś tam kulturalnym, naukowym i innych tam parametrów. Akurat inspirowane cywilizacją, ale leciutko. No ale tego. Prosta rzecz, a cieszy. No i znajduje zastosowanie właśnie w grach strategicznych. Przeważnie tak, bo zwykle to stopniowanie może to zwizualizować. Jak widzimy granice kulturalne gdzieś tam w cywilizacji, czy tego typu rzeczy, to fajnie to wygląda i rzeczywiście też nakłada jakąś taką dodatkową warstwę na rozgrywkę, więc zwłaszcza strategiczną.
To pewnie chyba głównie tak, bo to gdzie jest ta mapa, to nie jest ani na mapie się tak się aplikuje.
Dobra, to co z wybranych algorytmów? To by było na tyle.
Algorytmy wybrane,
Algorytmy wybrane. Szybko opowiedziane lub nie szybko.
No nie szybko, bo to nasz najdłuższy odcinek. A to nie koniec.
Nie koniec. Ale chyba dzisiaj koniec.
Na dzisiaj koniec. Nie koniec ogólnie, ale na dzisiaj koniec.
Musimy skończyć, bo tam później wejdziemy w naprawdę ciekawe rzeczy i będzie się działo.
W odróżnieniu od tych dwóch odcinków…
Także… to do następnego odcinka, Wojtas.
Do następnego, Michał. Dzięki Super, super tematy.
Dzięki.