Kwiecień 2020. Minęła już euforia uczniów spowodowana tym, że będą uczyć się w domu, minął chaos spowodowany nieogarnianiem przez szkołę narzędzi do nauki zdalnej i każdy zdążył się już do tej sytuacji przyzwyczaić. Wtedy uczniowie po prostu zaczęli tęsknić za sobą i za zapachem prawdziwego budynku, w którym standardowo mogliby siedzieć w ławkach.
Leskie LO zamiast tradycyjnych dni otwartych dla kandydatów utworzyło filmik, na którym uczniowie mówili za czym tęsknią najbardziej ze swojej szkoły. Na końcu filmiku był zaprezentowany budynek szkoły odtworzony przez kreatywnych uczniów w Minecrafcie.
Trzeba przyznać, że Michał i Kuba wykonali kawał dobrej roboty, a budynek prezentował się imponująco. Po wykonaniu zadania i nagraniu ujęć szkoda było tak po prostu zostawić mapę, nad którą w końcu chłopaki spędzili sporo czasu. Jako, że lubię kreatywne gry – dołączyłem do zabawy budowania reszty Leska.
Tylko zabawa nie była już tak fajna, bo po pierwsze nie bardzo wiadomo było jaki jest cel tego budowania, a po drugie praca szła jak krew z nosa, w związku z tym po kilku godzinach mozolnej pracy i zrobieniu dwóch budynków w otoczeniu Leska nikomu już się nie chciało dalej w to brnąć. W związku z tym wpadłem na pomysł.
Zautomatyzować budowę miasta
Tylko jak wygenerować miasto w prostej grze dla nastolatków? Okazało się, że w trybie budowania można używać skryptów – prostych komend, które pozwalały np na wypełnianie jakiejś przestrzeni konkretnym rodzajem bloków. Minecrafta każdy zna, ale dla formalności przypomnę, że świat w grze składa się z sześciennych bloków ułożonych ciasno koło siebie w tej samej orientacji. Można przyjąć założenie, że każdy taki blok ma wymiary 1m/1m. Budowanie mogło więc przyspieszyć wydawanie komend, które wypełniałyby przestrzeń grubości 1 bloku elementami budynku – ścianami, podłogą, dachem… Tylko to rozwiązanie nadal jest czasochłonne a do tego chyba jeszcze bardziej nudne.
Szczęśliwie okazało się, że mając własny serwer, lub włączając mapę lokalnie można tworzyć pliki zawierające w osobnych linijkach serię komend wykonujących się jedna po drugiej, wykonywanych w grze jednym poleceniem. Niestety nie były to pełnoprawne skrypty z funkcjami, parametrami czy obliczeniami matematycznymi, a proste uruchamianie komend jedna po drugiej, więc wizja stawania w danym miejscu i wydawania polecenia typu „build_home 12 15 red blue stone 2” budującego ułamku sekundy czerwono – niebieski domek z kamiennymi fundamentami i wymiarami 12/15 niestety się nie udała. Trzeba było wymyślić coś innego.
Można było za to odpalić skrypt, który miałby już wcześniej przygotowane dane każdego elementu budowli i jedną komendą wygenerowałby całe miasto. Taki skrypt jednak trzeba było przygotować. Zastanówmy się więc co chcemy wygenerować i w jaki sposób przygotować dane wejściowe.
Jakie dane nam będą potrzebne do budowy domków?
Najprostszy budynek odtworzony w Minecrafcie, to seria bloków, które układają się w 6 przylegających do siebie powierzchni tworzących prostopadłościan o wymiarach x bloków na y bloków na z bloków, gdzie x i z to współrzędne poziome, a y – pionowa. Sprawę upraszcza fakt, że mówimy tu o wymiarach wyrażonych liczbami całkowitymi, bo bloki mają tu wymiar 1/1/1. Każdy budynek musi być osadzony w jakimś miejscu, więc potrzebny nam jest kolejny zestaw współrzędnych x,y,z z położeniem tzw. kamienia węgielnego (który co prawda z węgla nie jest, ale opiera się na nim fundament jednego z rogów budynku). Dla jeszcze większego uproszczenia – biorąc pod uwagę specyfikę Minecrafta i topografię centrum Leska, ustalmy, że każdy dom będzie ustawiony równolegle do osi x i z. Mamy więc 6 liczb całkowitych, które określają każdy z domków, co w przypadku budowania wysypiska kontenerowców w zupełności by wystarczyło. My jednak chcemy czegoś co udaje budynek mieszkalny.
Każdy dom oprócz czterech ścian, sufitu i podłogi ma też dach, okna, drzwi i różne kolory fasady. W Lesku bardzo częstą praktyką jest malowanie elewacji parteru na inny kolor niż wyższe piętra budynku. Dachu też są różne rodzaje – bloki mają dach płaski – czyli w przybliżeniu sufit ostatniego piętra jest dachem, kamienice mają z reguły dachy dwuspadowe, a wolnostojące domy często mają dachy czterospadowe. Nie każdy używa czerwonej dachówki, więc kolory mogą być różne. Każdy budynek ma określoną liczbę pięter, a na każdym z nich okna. Ten fakt możemy sobie uprościć uznając, że każde piętro ma w przybliżeniu 3 metry (czyli 3 bloki w grze), więc jako parametr wejściowy zamiast wysokości po prostu możemy podać liczbę pięter. Co do okien, możemy umówić się, że znajdują się jedno pod drugim, więc ich ilość na każdym piętrze i na przeciwległych ścianach może być ta sama. Drzwi sobie pominiemy, bo można je szybko zrobić ręcznie – dużo szybciej niż ogarniać dla nich osobny parametr.
Nasz zestaw wejściowy powinien więc przechowywać następujące dane:
- Współrzędne budynku x, y, z (3 parametry)
- Jego długość i szerokość (2 parametry)
- ilość pięter (1 parametr)
- Materiały: podłoga, elewacja parteru, el. piętra, dach (4 parametry)
- Ilość okien na piętrze w orientacji x i w orientacji z (2 parametry)
- Typ dachu (1 parametr)
mamy więc 13 parametrów wystarczających do wyspecyfikowania każdego z domków. Aby się w nich nie pogubić przyda się dodatkowy parametr z adresem lub nazwą określającą który budynek z tych rzeczywistych właśnie opisaliśmy. Do sporządzenia takich danych wejściowych idealny będzie excel, za pomocą którego stworzymy plik CSV, bo te bez żadnej dodatkowej biblioteki będziemy mogli przetworzyć w naszym skrypcie.
Prosty blok wygenerowany z danych wejściowych
Zbieranie danych
Wracamy na chwilę do świata realnego czasów #zostanwdomu. Tamten rok był z perspektywy czasu dziwny. Wychodzić można było tylko mając dobry powód, w maseczkach, max w 2 osoby i nie zbliżając się do innych. Do tego niepełnoletni mogli wychodzić wyłącznie z pełnoletnim opiekunem. Czy mógł być więc lepszy powód do wyjścia na spacer z niepełnoletnim szwagrem niż fotografowanie budynków w celu zbierania danych wejściowych? Oczywiście wiadomo – google maps i street view, ale co własne materiały to własne materiały. W ten sposób mogliśmy przyporządkować kolory elewacji, ilość okien, liczbę pięter i typ dachu do budynków. To czego nam brakowało, to wymiary powierzchni budynków i ich położenie. Do tego przydały nam się mapy online i funkcjonalność mierzenia odległości. Proste mierzenie i zaokrąglanie do pełnych metrów przy założeniu 1m = 1 blok załatwiło sprawę. Gorzej było ze współrzędnymi położenia.
Ale i na to znalazł się sposób. Toporny i może trochę durny, ale działający. Najpierw trzeba było ustawić mapę online tak, że wszystkie budynki w centrum ułożone były w tej orientacji co ekran, potem zrobić zrzut do pliku graficznego. Trzeba też było w istniejącym już świecie Minecraft ze stojącym budynkiem LO, który zamierzaliśmy rozszerzyć o resztę Leska znaleźć punkt (x,z) = (0,0), który w tym przypadku znajdował się na ścieżce prowadzącej z liceum nad San. Nałożyliśmy mapę tego co już było w grze na prawdziwą mapę Leska w GIMP-ie i zestawiając odległość w rzeczywistości, z odległością w grze ustaliliśmy ile pikseli w naszej skali to 1 blok. Mierząc więc prostym zaznaczaniem ile pikseli od punktu 0,0 znajduje się prawy dolny (w naszej orientacji) punkt budynku i dzieląc go przez nasz współczynnik px/blok otrzymywaliśmy potrzebne współrzędne. Ponieważ samo centrum jest względnie płaskie – współrzędną wysokości y pozostawiliśmy stałą – zgodną z wysokością wygenerowanego na mapie płaskiego terenu.
Ulice
zorientowaliśmy się, że z samymi budynkami będzie pusto, więc pasuje wygenerować ulice. Powstała więc potrzeba utworzenia drugiego pliku ze zdefiniowanym położeniem ulic. W naszym klockowym świecie zadanie było o tyle proste, że wystarczyło wygenerować płaską powierzchnię w czarnym kolorze, ale niestety o ile budynki ustawione w jednym kierunku jakoś przeszły, o tyle ulice musiały iść lekko pod skosem aby wpasować się w krajobraz miasta. Więc i tutaj trzeba było kombinować. Najprościej było założyć, że ulice patrząc od góry składają się z połączonych z sobą równoległoboków. Tutaj przyszłościowo zaimplementowalibyśmy stopniowy spadek w dół, więc współrzędna y też byłaby brana pod uwagę. Do tego potrzebne nam będą współrzędne początku i współrzędne końca, a także szerokość ulicy i materiał z którego będzie zrobiona ulica.
Algorytm będzie tworzył zestawy bloków 1x1x szerokość ulicy zaczynając od współrzędnych początku i stopniowo przesuwał się aż do osiągnięcia końcowej współrzędnej – proporcjonalnie przesuwając pozycję pierwszego bloku w rzędzie. Ponieważ pasy tworzące drogę będą zawsze równoległe do którejś z poziomych współrzędnych – dodamy sobie jeszcze jeden parametr określający która to będzie współrzędna – x czy z.
Podsumowując dane wejściowe ulicy:
- współrzędne x,y,z początkowe (3 parametry)
- współrzędne x,y,z końcowe (3 parametry)
- materiał (1 parametr)
- orientacja x czy z (1 parametr)
- szerokość ulicy (1 parametr)
Tutaj też najprościej będzie utworzyć plik csv dodając w nim jeszcze jedną kolumnę opisującą o którą ulicę w tym przypadku nam chodzi. Współrzędne początkowe i końcowe oczywiście pobierane były tym samym prostym sposobem co koordynaty budynków.
Prosty skrypt napisany w PHP zamieniał dane wejściowe na zestaw funkcji fill, które odpalane w grze za pomocą jednej komendy wygenerowały miasto zdefiniowane w wejściu. Oczywiście i tutaj były lekkie problemy, ponieważ generowanie za pomocą skryptów działa tylko w obrębie, który Minecraft aktualnie przetwarza, więc trzeba było uruchamiać skrypt w kilku miejscach planszy.
Czerwone kropki na rysunku oznaczają punkty początkowy i końcowy ulicy, jasnozielone – punkty startowe poszczególnych linii, a ciemnozielone – pozostałe elementy ulicy.
Ekipa wykończeniowa
Do dzisiaj pamiętam jak na teamspeaku Kuba niedowierzał i cieszył się widząc resztę miasta, która wyrosła dookoła szkoły, którą z taką starannością budował. Oczywiście Liceum, w którym chłopaki starali się oddać każdy istotny detal z dokładnością do jednego bloku wyraźnie odstawało swoją jakością od prostopadłościennych budek ułożonych wg układu centrum Leska. Zmotywowani nowymi wyzwaniami i wielkością przedsięwzięcia Michał z Kubą zabrali się za dekorowanie i adaptację surowych budynków i trzeba przyznać, że w tym względzie mają ogromny talent. Wkrótce mogliśmy pospacerować po naszej planszy czując się jakbyśmy się poruszali w realnym świecie.
Co było dalej?
Dalej były stopniowo znoszone obostrzenia, zbieranie ocen na koniec roku, realne spotkania z ludźmi, wakacje i cały szereg rzeczy, który był dużo ciekawszy od stawiania bloków w prostej grze. Taka z resztą naturalna kolej rzeczy, że i najfajniejsze rzeczy jeśli nie mają wyraźnego celu – muszą się w końcu znudzić. Po tych czasach pozostała nam fajna pamiątka w postaci pliku mapy ze światem, który razem stworzyliśmy.
A czy przy generowaniu świata można było coś zrobić lepiej? Oczywiście – można było pobrać mapę wysokościową terenu i wygenerować jego ukształtowanie – wtedy można byłoby wygenerować odleglejsze rejony z domami na górce; można było oznaczyć pozycje drzew, poprawić algorytm domów, żeby mogły być pod różnym kątem, znaleźć katalog z danymi GPS budynków i potraktować go jako dane wejściowe i wreszcie – zapisać mapę Leska prosto do pliku z mapą, zamiast kombinować ze skryptami. Myślę jednak, że raczej nie będzie już ani motywacji ani powodu, żeby do tego wracać, a w międzyczasie pojawiły się inne ciekawe wyzwania.
Na sam koniec zestawienie Leskich lokacji z google maps z tymi z naszej mapy, po kolei: ratusz, liceum, plac z fontanną, synagoga i przychodnia i DK 84 przechodząca przez centrum Leska.