MonoGame – Tworzymy grę Snake od podstaw! | Część 4 – ostatnia

W tej części w końcu zajmiemy się stworzeniem tego co najważniejsze – gracza – czyli węża, oraz planszy po której będzie się poruszał. Gra w końcu stanie się grywalna 😉 Zapraszam do czytania!

Jeszcze chwila wstępu…

Tą część serii zacząłem pisać w poniedziałek. Wyglądała ona zupełnie inaczej niż wersja, którą za chwilę przeczytasz. Dlaczego w ogóle o tym piszę? Dlatego, że dzisiejszy wpis, będzie napisany trochę w innym stylu. Początkowo chciałem napisać go w taki sam sposób jak poprzednie, czyli co robimy z naszym kodem krok po kroku. Jednak finalny tekst napisany w tym stylu, był… trochę zagmatwany. Szczerze powiedziawszy sam zacząłem gubić się w tym co piszę – może to dlatego, że chciałem wszystko wyjaśnić zbyt prosto? Sam nie wiem. W pierwotnej wersji chciałem opisywać nowszy kod gry, z podziałem na klasę Player i GameScene. Jednakże doszedłem do wniosku, że łatwiej będzie to przedstawić w inny, jednak mniej elegancki – ale cały czas działający sposób. Czyli wrzucając wszystko do jednej klasy. Kod może nie będzie przez to doskonały, jednak na potrzeby tej serii(skierowanej głównie do początkujących, takich jak ja) powinien się nadać 😉 Dobrze… Czyli jak dzisiejsza część będzie wyglądać? Przedstawię za chwilę tutaj cały kod klasy GameScene i będę opisywał od czego są poszczególne metody i pola. Myślę, że taki opis będzie bardziej przejrzysty, niż próba wyjaśniania wszystkiego po kolei, jak wcześniej – wszakże w tym kodzie zbyt wiele elementów jest ze sobą powiązanych 😛 No dobrze, skoro wszystko jasne to najpierw…

Dodajemy do projektu nowe tekstury

Przed przystąpieniem do prac, musimy dorzucić do naszego projektu dodatkowe trzy pliki graficzne. Jeden będzie bloczkiem składającym się na węża, drugi będzie tym co wąż będzie zbierał, a ostatnia grafika będzie tłem naszej planszy. Oto grafiki przygotowane przeze mnie:

item

PlayerBody

background

Kolejną rzeczą będzie dodanie do zasobów nowej czcionki(właściwe dwóch), która będzie potrzebna do wyświetlania aktualnego wyniku gry na ekranie. Czcionki dodajemy przy użyciu tego samego narzędzia co tekstury. Wybierając Add-> New Item -> Sprite Font Description

Przykładowy plik z naszą czcionką powinien wyglądać tak:

Należy przy tym zaznaczyć, że najważniejszą rzeczą tutaj jest modyfikacja linijek, w taki sposób:

Jeżeli tego nie zrobimy – z polskich znaków nici 😉 Oczywiście jeżeli chcemy ich używać. W pliku możemy też ustawić takie parametry jak rodzaj czcionki, czy też jej rozmiar.

Kod klasy GameScene

Tak jak pisałem wcześniej, przedstawię teraz cały kod klasy GameScene. Następnie będę opisywał wszystko po kolei 😉 Tak, więc nasz kod wygląda dokładnie tak:

Uff, trochę tego jest. Bierzmy się do roboty! 😉

Pomijając rzeczy oczywiste jak zmienne na samym początku, przejdźmy od razu do listy elementów Rectangle:

Ten element reprezentuje całego naszego węża. Będzie przechowywał jego bloczki, które będziemy wyświetlać i którymi będziemy ruszać.

Tutaj wczytujemy nasze tekstury i czcionkę. Tyle wyjaśnienia chyba wystarczy 😉

Tutaj już jest trochę więcej zabawy. Na początku scena jest czyszczona przy użyciu metody GraphicsDevice.Clear(Color.CornflowerBlue);. Następnie wyświetlane jest tło. Dalej rzecz bardzo ważna: głowa naszego węża(czyli pierwszy element naszej listy), którą kolorujemy na czerwono. Jeszcze dalej wyświetlana jest reszta ciała naszego węża – używamy do tego pętli while. Gdzie zmienna BodyIndex wskazuje na to ile elementów wąż posiada (czyli jak długi jest). Na ekranie wyświetlanych jest tyle elementów na ile wskazuje zmienna BodyIndex. Kolejna linijka wyświetla nasz „item”, którym wąż się żywi. A ostatnia metoda DrawString(), wyświetla aktualny wynik – czyli de facto wartość BodyIndex 😉

Metoda Update(), przyjmuje w argumencie czas jaki upłynął od rozpoczęcia gry – jest to bardzo ważne, w celu kontrolowania szybkości poruszania się naszego węża. Kod zawarty w if’ie wykonuje się tylko raz, po rozpoczęciu rozgrywki. Są to operacje skalowania wyświetlanych elementów i przygotowania wszystkiego do gry. Na samym początku czyszczona jest lista z elementami przechowującymi dane o elementach węża. Dalsze metody wykonują się przez cały czas trwania gry. Są to kolejno: Metoda odpowiadająca za obsługę klawiatury, przemieszczanie węża, sprawdzanie kolizji, oraz tego czy gracz znajduje się w granicach okienka.

Ta metoda, tak jak pisałem wcześniej wykonuje się jedynie raz po rozpoczęciu gry. Tutaj skalowane są elementy takie jak Tło, rozmiar pojedynczego bloku węża – oraz „item’ku”.

Ta metoda odpowiada za dodawanie do węża nowego elementu. Jeżeli gra dopiero wystartowała, to dodawany jest jego pierwszy element – czyli głowa. Jeżeli gra już trwa to kolejne elementy są dodawane w odpowiednich miejscach na końcu węża, w zależności od tego w którą stronę ten aktualnie się porusza. Przy tym zwiększany jest BodyIndex, który wskazuje na ilość elementów, które się na niego składają. Na samym końcu generowany jest nowy „item’ek” do jedzenia – albowiem ta metoda wywoływana jest zawsze po zderzeniu się właśnie z nim.

Metoda trochę nieelegancka. Obsługuje klawiaturę, kiedy wciśniemy strzałkę w prawo zmienna IsMoveToRight = true i nasz wąż będzie się właśnie w tą stronę poruszał. Przy czym poruszając się w prawo, nie może od razu skręcić w lewo (jak widać w warunkach).

Ta metoda aktualizuje położenia całego węża podczas gry. Jako argument przyjmuje zmienną typu GameTime. Jej kod jest wykonywany tylko w okresie czasu, który jest podzielny przez 75. Dzięki temu nasz wąż nie będzie poruszał się zbyt szybko. Pierwsza część kodu odpowiada za poruszanie końcem węża – każdy element przyjmuje pozycję tego, który przed chwilą był przed nim. Na końcu ruszamy głową. Każdy blok jest przesuwany dokładnie o taką wartość, jaką zajmuje pojedynczy bloczek węża.

Metoda ta, sprawdza czy głowa węża nie „wyszła” poza nasze okienko. Jeżeli tak się stanie – wywoływana jest metoda GameOver();

Kolejna sprawdza, czy głowa węża nie zderzyła się z żadnym innym jego elementem. Jeżeli zderzy się – wywoływana jest również metoda GameOver(). Z kolei jeżeli wąż zderzy się z item’kiem, czyli go zje;) to wywołujemy metodę AddBodyBlock() – która wydłuży naszego węża, oraz wywoła metodę generującą nowy item’ek.

A to właśnie ta metoda… Właściwie tylko zmienia położenia naszego item’ka, który następnie jest wyświetlany. Położenie jest losowane, z tym że przedział tego losowania ogranicza rozmiar okna.

Metoda kończąca grę… ustawia wszystkiemu wartości początkowe, odblokowuje możliwość zmieniania rozmiaru okna (które należy zablokować, po rozpoczęciu gry). Przypisuje właściwości znajdującej się w innej klasie, nasz wynik, który następnie wyświetlimy w scenie kończącej grę. Po czym ustawia BodyIndex = 0, oraz zmienia wartość IsStart na true; Na końcu wyświtlana jest scena GameOver. Jej kod wygląda tak:

Generalnie rzecz biorąc, jest bardzo podobny do tego z MenuScene. Jedyną różnicą jest brak przycisków, a zamiast tego obecność czcionek. Ta scena pozwala zacząć grę od nowa, lub ją zamknąć.

Na koniec cały działający projekt do pobrania: download.

Mam nadzieję, że w miarę dobrze wszystko wytłumaczyłem. Bardzo cieszę się, że w końcu dodałem ten wpis – pomimo kilku problemów 😉 Jeżeli macie jakieś wątpliwości/uwagi – a myślę, że będą na pewno bo kod doskanały nie jest – śmiało piszcie w komentarzach.