Gra platformowa 2D w Unity Od Zera do Game-Developera [OZDGD]
W ramach serii Od Zera do Game-Developera, w której to chcę zachęcić innych do zainteresowania się nie tylko grami, ale też sferą ich produkcji, powstały już następujące teksty:
- Wszyscy mówili: nie idź do gamedevu. Nie posłuchałem i dziś mogę się pochwalić pierwszą grą
- Programujesz w Unity? Ten kreacyjny wzorzec projektowy musisz znać
- Jaki komputer do Unity? Zobacz porównanie trzech zupełnie odmiennych sprzętów
Niniejszy tekst jest kontynuacją serii, w ramach to której tworzę pełnoprawną, ale prostą platformową grę 2D:
- Jak stworzyć grę w Unity? Stawiam repozytorium, Gita i projekt w Unity
- Poradnik Unity. Co musicie wiedzieć o tym silniku, żeby stworzyć własną grę?
- Platformowa gra 2D w Unity. Tworzę GDD i rozpisuję mechaniki
- Architektura kodu w grze platformowej. Opisuję najważniejsze podstawy [OZDGD #4]
- Platformówka 2D w Unity. Tworzę środowisko oraz postać gracza [OZDGD #5]
- Platformówka 2D w Unity. Tworzę główną postać i ją programuję [OZDGD #6]
- Przeciwnicy i system walki w platformowej grze 2D w Unity [OZDGD #7]
Unity Tile, czyli błogosławieństwo dla level designerów
W gamedevie istnieje wiele pozycji, a ich rozwarstwienie jest tym większe, im większy jest projekt i samo studio. W jednoosobowym projekcie musicie jednak być w stanie przywdziać skórę każdego specjalisty i stać się również projektantami poziomów (level designers), którzy to odpowiadają za zaprojektowanie “plansz” (tego, po czym porusza się gracz), aby zapewnić mu możliwie najwięcej zabawy na danym poziomie. Osoby te dostają więc podstawy z innych działów, a więc konkretne narzędzia, które pozwalają im zaprojektować dany poziom od podstaw.
W naszym przypadku wykorzystamy wbudowane w Unity narzędzie “Tilemap“, które to znacząco ułatwia proces tworzenia poziomu. Nie chciałbym tutaj przedstawiać całej dokumentacji, która jest związana z tym dodatkiem do silnika Unity, więc po więcej informacji musicie udać się na oficjalną stronę producenta, ale w gruncie rzeczy musicie wiedzieć przede wszystkim tyle, że to właśnie narzędzie pozwala nam w prosty sposób projektować poziomy w 2D.
Jako że do projektu podchodzę w możliwie najmniej “graficzny” sposób, wykorzystuję do niego asset, który zapewnia mi najważniejsze elementy, bo nie tylko kafelki środowiska, ale też animowane postaci 2D (w tym głównego bohatera) oraz kilka elementów otoczenia w zawsze spisującym się świetnie (i mało obciążającym układ graficzny) stylu pixel-art. Takie też zestawy grafik polecam wam szukać, jeśli nie chcecie sami ich tworzyć, bo w paczkach tego typu znajdziecie wszystko, czego potrzebujecie, aby stworzyć swój pierwszy poziom. Przykładowo w tej podlinkowanej znajduje się paczka “tileset” z wszystkimi interesującymi mnie kafelkami o wielkości 16×16 pikseli, którą można wykorzystać do stworzenia własnej Tile Pallete, czyli w pewnym sensie “palety barw”, którą będziecie “malować” swoje poziomy.
Aby to zrobić, na scenę należy dodać odpowiedni obiekt 2D, jak na zrzucie ekranu poniżej:
Tak oto zyskaliście możliwość tworzenia poziomu 2D, bo teraz klikając na poszczególny kafelek w Tile Pallete, będziecie mogli nanosić wskazane kafelki na każdą jedną komórkę “grida”, którego właśnie stworzyliście, o tak:
Pamiętajcie, że to co aktualnie stworzycie, będzie fizycznym elementem w świecie gry dopiero wtedy, kiedy tak zaprojektujecie swój projekt, więc możecie teraz albo postawić na proste tło w formie grafiki, albo stworzyć wypełnienie poziomu innymi kafelkami, które z czasem logicznie odseparujecie od pozostałych. W tym poziomie akurat chciałbym nadać tłu dżunglowy charakter, więc zdecydowałem się na ten pierwszy wybór, odpowiednio umieszczając grafikę na scenie i dostosowując komponent transform. Magiczne 0.1 w pozycji Z nie wzięło się znikąd – gwarantuje to, że w pozycji sceny tło będzie za, a nie przed pozostałymi kafelkami.
Teraz nadszedł czas na nadanie poszczególnym kafelkom logicznego charakteru. Na tę chwilę są one jedynie obiektem w grze, do którego trudno się specjalnie odwoływać w skryptach, ale możemy to zmienić, dzieląc odpowiednio poszczególne kafelki w GameObject o nazwie Grid. Na tę chwilę mamy jedynie podstawowy podział, dlatego uznajmy, że w naszej grze musimy wiedzieć, co dokładnie jest ziemią i platformami, a co ścianami oraz sufitem. W tym celu powielam podstawowy GO Tilemap kombinacją Ctrl+D i zmieniam nazwę nowego i oryginalnego GO na to, co mają zawierać, czyli kolejno GroundAndPlatform (w gamedevie przyjęło się wykorzystywać angielskie określenia) oraz WallsAndCeiling i z pomocą gumki usuwam poszczególne kafelki, które nie pasują do tego nazewnictwa.
Teraz z kolei nadszedł czas na nadanie tym kafelkom fizycznych właściwości, które przydadzą się w naszej grze. W obu przypadkach ich komponenty zawierają tylko Transform, Tilemap oraz Tilemap Renderer, a więc nic, co pozwoli nam m.in. weryfikować, na czym aktualnie znajduje się postać gracza. W tym celu musimy zapewnić obu GO z kafelkami komponent Composite Collider 2D, który stworzy automatycznie Rigidbody 2D, aby finalnie dodać do nich Tilemap Collider 2D.
Te trzy komponenty współpracują ściśle ze sobą, aby zapewnić każdemu kafelkowi fizyczne właściwości, bez których te byłyby jedynie tłem. Żeby jednak ustawić je odpowiednio, musimy w Rigidbody 2D ustawić typ statyczny (static), a w Tilemap Collider 2D wskazać, aby komponent wykorzystywał kompozyt (Composite):
Ostatnim etapem stawiania środowiska gry jest ustawienie obu zestawom kafelków odpowiednich warstw, co przyda nam się w przyszłości. W ogólnym rozrachunku zabawa warstwami i logiką nie jest aż tak dobrą praktyką, bo może doprowadzać do problemów, ale jest to domena głównie dużych projektów. Dlatego my musimy teraz wejść w kreator warstw:
Dodać dwie warstwy w dowolny indeks i następnie ustawić odpowiednie warstwy do odpowiednich zestawów kafelków, aby wziąć się wreszcie za postać gracza.
Tworzymy podstawę dla postaci bohatera
Nadszedł więc czas na stworzenie postaci od podstaw. Jak na razie, zajmiemy się jedynie jej wizualizacją, a pozostałe elementy dołożymy na następnych etapach. W tym celu musimy stworzyć na scenie pusty GameObject i stosownie go nazwać.
Teraz należy zadbać o to, aby jakoś wyglądał, więc w tym celu dodajemy do niego nowy komponent – Sprite Renderer, który odpowiada (w skrócie) za wyświetlenie grafiki. Grafiki, którą stworzyliśmy sami, lub którą znajdziemy w pobranej lub kupionej paczce.
W tej chwili nasza postać jest jednak jedynie grafiką, a nie rzeczywistym elementem w grze. Aby to zmienić, musimy dodać do niego dwa kolejne komponenty – Rigidbody 2D oraz Capsule Collider 2D. Podczas gdy pierwszy zapewni możliwość oddziaływania fizyki na obiekt, drugi umożliwi nam sprawdzanie czy postać gracza, aby nie koliduje z jakimś elementem na scenie.
Powyżej możecie zwrócić szczególną uwagę na samo umieszczenie kapsułowego collidera. Zamiast pozwolić mu objąć całą postać, zmniejszyłem go nieco po to, aby nieco ułatwić graczowi wyzwanie, co w tego typu grach jest dosyć częstą praktyką, bo te kilka pikseli jest na tyle małym marginesem, że gracz może mieć wrażenie, że dany przeciwnik go nie tknął… choć mijałoby się to z prawdą. Pamiętajcie – komputery nie kłamią, ale w okłamywaniu naszej percepcji (tutaj percepcji graczy) mogą nam pomóc, a jeśli już przy kłamaniu jesteśmy, na tym etapie możecie już zapewnić komponentowi fizyczny materiał z tarciem ustawionym na 0,1 i parametrem odbijania na 0. Przyda się to w przyszłości, kiedy będziemy pracować nad umiejętnością ślizgania się postaci po ścianach.
Jest to też świetny moment, żeby rozbudować naszego gracza o trzy dodatkowe obiekty w roli “weryfikatorów”. Mowa o pustych GO, które będą odpowiedzialne na dalszym etapie za sprawdzanie, czy nasza postać znajduje się przy ścianie, na platformie i czy jakiś przeciwnik jest w jej zasięgu ataku. W tym celu musicie stworzyć trzy puste GO, nazwać je odpowiednio i ustawić poprzez parametr Position w ich komponentach Transform tak, aby były na dolnej części postaci (weryfikator dla platform), na jej prawym boku (weryfikator dla ścian) oraz jeszcze dalej z prawej (zasięg ataku).
Taki oto obiekt gracza warto teraz przerobić na Prefab, czyli specjalny rodzaj pliku, który pozwoli wam zapisać wszystko to, co robiliście w danym obiekcie i np. przenosić go między scenami, a w razie problemu przywrócić jego stan (o ile nie nadpiszecie ustawień opcją Override w Prefabie). W tym celu kliknijcie GO Player i trzymając ciągle lewy przycisk myszy, przeciągnijcie go do folderu Player/Objects.
Prefab zostanie utworzony automatycznie, pozwalając wam gładko przejść do następnego etapu – sprawienia, że postać będzie mogła się poruszać i reagować na otoczenie. To już jednak zabawa na kolejny poradnikowy wpis w serii Od Zera do Game-Developera, w ramach to której powstaje ta właśnie gra platformowa. W aktualnym stanie postać powinna zatrzymać się na kafelkach, kiedy włączycie grę, ale jeśli tak się nie dzieje, upewnijcie się, czy w GO z kafelkami komponent Tilemap Renderer ma ustawione Order in Layer na 1, a nie 0. Jeśli już to skończycie, zapiszcie wszystkie zmiany, zróbcie commita i pusha, żeby wysłać je na zdalne repozytorium.