Projektowanie gry

(Lekcja 1 :: Projektowanie gry)

Wstęp


Witam Cię w kolejnej lekcji kursu [Piszemy platformówkę 2D].

W tej lekcji wciąż nie napiszemy żadnego kodu, jednak zastanowimy się co właściwie chcemy osiągnąć. Przy małych produkcjach np game jam’owych planowanie ogranicza się do prostego szkicu o czym będzie gra, w przypadku większych dojdzie rozplanowanie głównych modułów.

Ta lekcja jedynie dotyka tematu projektowania gier (zachęcam cię do samodzielnego pogłębienia tego tematu), skupimy się wyłącznie na ogólnym rozrysowaniu rozgrywki, odpowiemy sobie także na kilka istotnych (z punktu widzenia kodu) pytań oraz zastanowimy się jak będzie wyglądał schemat klas w naszej grze.

 

Zestaw uniwersalnych pytań


Przy designie każdej gry warto zadać zestaw pytań, które mają na celu ułatwienia programiście/zespołowi pracy nad grą. Dzięki odpowiedziom na te pytania nie musimy wszystkiego wymyślać „na żywo”, uzyskamy także ogólny zarys gry, a przy samym pisaniu będziemy czuli się mniej „zagubieni”.

Odpowiedzi na te pytania powinny być niczym „kamienie węgielne” tzn stałe (constance), ponieważ ich zmiana będzie wiązała się ze zmianą sporej części kodu (czego oczywiście nie chcemy robić). Więc warto zastanowić się nad nimi chwilę, zgodnie z powiedzeniem:

Think twice, code once ~Bill Chapman

Wracając do meritum, pytania na które dobrze jest znać przynajmniej ogólną odpowiedź (kolejność przypadkowa):

  1. O czym jest gra? (klimat gry; ogólny zarys „fabularny” gry, nie chcemy gry o niczym, gra powinna trzymać się jednej ustalonej koncepcji)
  2. Jaki jest cel gry? (co gracz musi zrobić, żeby wygrać, przejść poziom)
  3. Jaka jest podstawowa mechanika i sterowanie?
  4. Czy w grze są jakieś przeszkadzajki/wrogowie? Jeżeli tak to jakie? (ogólny szkic zachowań wrogów, obiektów które mogą skrzywdzić gracza)
  5. Za co nagradzamy gracza?

Zazwyczaj tych pytań zadaje się więcej (np. o znajdźki, ekwipunek, otd.), jednak w naszym przypadku nie miałyby one znaczenia, a to dlatego że dążymy do prostej gry pozbawionej wielu elementów, które mogłyby zaciemnić obraz w tym kursie.

 

Odpowiadamy na pytania


Mimo, że w przypadku tego poradnika będziemy stosowali się do mojej koncepcji, to mimo wszystko zachęcam cię do samodzielnego odpowiedzenia na wyżej postawione pytania, a dopiero później przeczytania moich propozycji.

 

1. O czym jest gra? Zarys świata

Wbrew pozorom to pytanie nie jest czysto estetyczne, tzn nie ma wpływu wyłącznie na wygląd gry, ale także może mieć wpływ na kod, np. jeżeli zdecydujemy się na grę w kosmosie z mikro-planetami, to programista dostaje informację, że grawitacja nie w każdym miejscu będzie taka sama i będzie musiał zaimplementować ją w odpowiedni sposób, jeżeli toczy się na jednej planecie to z góry może ustalić stałą grawitację.

Nasza gra będzie o wojowniku z przyszłości, który na skutek wypadku trafił do świata fantasy. Celem gracza jest pokierowanie bohaterem, w taki sposób aby ten przy użyciu miecza i nowoczesnej technologii dotarł do teleportu, którym będzie mógł powrócić do swoich czasów.

Chciałbym też zwrócić Waszą uwagę na to, że na wczesnym etapie gry nie potrzebne są ładne grafiki, muzyka, itd. Na etapie prototypowania gry zazwyczaj korzysta się z placeholderów, które równie dobrze mogą być kwadratami i prostokątami z opcjonalnym napisem placeholder.

Postać sterowana przez gracza
Postać sterowana przez gracza

 

2. Jaki jest cel gry? Ogólny wygląd rozgrywki

Wiemy, że nasza gra ma być niczym wyróżniającym się platformówką 2D i ta informacja już dużo mówi o typie rozgrywki. Jeżeli się chwilę zastanowimy to każdy etap w dowolnej platformówce składa się z powtarzalnego wzorca i rozgrywka polega na:

  1. przejściu od punktu A do punktu B,
  2. opcjonalnie zebranie dodatkowych elementów jak klucze, które trzeba zebrać aby odblokować przejście do kolejnego poziomu.

My nie chcemy się wybijać i zostaniemy przy tym sprawdzonym rdzeniu rozgrywki, czyli nasza gra będzie posiadała widok typowego side-scrollera, a celem gracza będzie dotarcie do z góry określonego punktu (w późniejszych etapach dodatkowo wymagającego klucza).

 

3. Mechanika / sterowanie

Dobrze jest się zastanowić nad tym aspektem, bo nie zawsze mechanika sprowadza się do prostego poruszania po naciśnięciu standardowych klawiszy (WASD), jeżeli planujemy np grę w kosmosie to warto się zastanowić czy umożliwimy postaci poruszanie się bez sztucznej grawitacji (która znacznie wiele rzeczy upraszcza), bo jeżeli wrzucimy gracza do stacji kosmicznej bez grawitacji i plecaka do EVA to musimy dostarczyć jakiś rodzaj drabinek, dzięki którym będzie można się poruszać.

Wracając do naszej gry:

  • Gracz będzie sterował postacią, która jest w stanie poruszać się na boki, wykonywać maksymalnie podwójny skok, biegać oraz atakować mieczem.
  • Można będzie sterować przy użyciu klawiatury lub pada.

 

4. Przeszkadzajki / wrogowie

Element, który wielokrotnie napędza samą rozgrywkę, czym by była gra RPG bez potworów do zabicia, albo z wrogami tak słabymi że padali by nasze jedno uderzenie mieczem? Bardzo szybko by się znudziła, gracz potrzebuje coraz lepszego ekwipunku i silniejszych przeciwników aby poczuć odczuwalny progress – uczucie że coś osiągnął.

U nas będą istniały 2 typy „przeszkadzajek” mogących skrzywdzić gracza:

  1. bloki-pułapki, które są w stanie osłabić gracza lub nawet go zabić;
  2. wrogie jednostki, które na widok gracza zaprzestają warty i atakują gracza poprzez szarżę w jego stronę.

 

5. System nagród

Ten punkt może wymagać dodatkowego komentarza. Każda gra posiada system, który sprawia że gracz czerpie radość z grania i jest w stanie przechodzić nawet wielokrotnie 1 poziom, czy też jedną czynność (o tym był bardzo dobry odcinek na kanale Extra Credits, w linkach podrzucam serię odcników, którą warto obejrzeć[1]). Najczęściej są to jakieś punkty, które mogą zostać dodane do rankingu i sprawdzić jaki gracz jest najlepszy.

Podobnie zrobimy i my: załóżmy że gracz będzie posiadał 3 życia i za każde zachowane życie nagrodzimy go gwiazdką (czyli mamy znany z gier mobilnych system gwiazdek). Ponownie na nasze potrzeby to zupełnie wystarczy, jednak ponownie będzie dość łatwo to rozbudować o dodatkowe rankingi, inny system rozdawania gwiazdek, itp.

 

Rozplanowanie klas


Dobrze rozplanowany kod powinien być łatwy w rozbudowaniu o dodatkowe elementy bez zbyt edycji poprzednio napisanego kodu (open-close principle [2]).

Poniżej przedstawiam diagram klas, które sobie napiszemy w tym kursie. Jest tego sporo, ale możecie mi wierzyć że dzięki takiemu rozmieszczeniu klas dość łatwo będzie nasz kod rozbudować. Pod diagramem jest krótki opis poszczególnych klas, ich dokładny opis znajdziecie w odpowiednich lekcjach.

Uwaga!

W tym kursie stosuję konwencję angielskiego nazewnictwa klas/metod. Jest to powszechna praktyka i zdecydowanie zachęcam Cię do jej stosowania, nawet w przypadku korzystania z IDE: jeżeli korzystasz z VS lub C::B po polsku, to zmień jego język na angielski (łatwiej jest znalezienia rozwiązania logów błędu po angielsku niż po polsku).

W naszym projekcie dość mocno będziemy się wzorowali z rozwiązań zastosowanych w Unity, a to dlatego że są niezwykle intuicyjne i po prostu dobre (moim zdaniem). Jeżeli korzystałeś wcześniej z Unity to prawdopodobnie większość klas i konwencji będzie dla Ciebie znajoma, jeżeli nie to nic straconego i zaraz sobie większość rzeczy wyjaśnimy. Poniżej widzicie pierwszą iterację schematu UML[3].

Pierwsza iteracja schemtu klas
Pierwsza iteracja schemtu klas

Dodajmy, że schematy UML nie odzwierciedlają tego jak będzie wyglądał i działał kod, lecz jak powinien. Przy pisaniu kodu można zdać się na większą swobodę. W trakcie realizacji kursu niewątpliwie dopiszemy sobie kolejne klasy i elementy.

 

Ogólne objaśnienie działania klas


Tak naprawdę wszystko łączy klasa GameManager, która decyduje o tym co ma zrobić poszczególna klasa. To tutaj jest zlecane są wszystkie polecenia (zmiana poziomu, danie czasu obiektom na wykonanie swojego ruchu itd), a następnie przekazywane dalej.

Postacie (gracza i wrogów) reprezentuje abstrakcyjna klasa Character, która posiada swoją pozycję i rotację (Transform), moduł odpowiadający za fizykę (Rigidbody) oraz za kolizje z innymi obiektami (Collider). Jedyną różnicą pomiędzy Player, a Enemy jest to że gracz sam decyduje o swoich ruchach, w przypadku wrogów odpowiada za to sztuczna inteligencja.

Za fizykę (kolizję, poruszanie) odpowiada BasicPhysicsEngine, który dzięki rzutowaniu do klasy abstrakcyjnej PhysicsEngine jest łatwo wymienialny, tzn łatwo jest go podmienić na coś lepszego.

Dalej mamy mniej znaczące klasy, których omówienie zostawimy sobie na poszczególne lekcje (szkoda powtarzać kilka razy dokładnie to samo).

Może Ci się wydawać, że tych klas jest za dużo i mogłoby ich być dużo mniej. Fakt, można napisać grę z dużo mniejszą ilością klas, jednakże jeżeli zależy (a zależy) nam na elastyczności i łatwemu rozbudowaniu/ulepszeniu gry to ten model w pewnych miejscach jest wciąż za mało elastyczny i można go ulepszyć. Ale żeby nie mieszać Ci dodatkowymi klasami pozostańmy przy tym modelu (i tak czeka Cię sporo analizowania).

 

Przydatne materiały


  1. [1] [Filmy od Extra Credits] – polecam obejrzeć przynajmniej te filmy z serii „Making You First Game”;
  2. [2] [Open-Close Principle];
  3. [3] [Krótki wstęp do UML];
  4. [O pisaniu gier] [Jak NIE pisać pierwszej gry] – zestaw artykułów o pisaniu gier mojego autorstwa;
  5. [Jak stworzyć dobrą grę komputerową] – artykuł od Adama Sawickiego;
  6. [Projekt gry: typowe pułapki] – pułapki w projektowaniu gier;
  7. [Repozytorium]

W kolejnej lekcji zajmiemy się przygotowaniem projektu, napiszemy pierwsze linie kodu oraz sprawimy, że gracz będzie w stanie się poruszać.

Code ON!


  • Maciej Gwiżdż

    Super, kiedy kolejna część, bo nie mogę się doczekać? Polecam obejrzeć ten film https://www.youtube.com/watch?v=RGX4lskX8gA 🙂

    • Nie przepadam za materiałami od tvgry (wyjątkiem są materiały w wykonaniu Hed’a).
      Z tego co mi się wydaje to pisałem przy pierwszej części tej serii, że jeżeli pojawi się chociaż 1 na miesiąc to będzie dobrze 😉 Postaram się, żeby była jak najszybciej, ale niczego nie obiecuję (najwcześniej za tydzień).

  • Adam Pajkert

    Ciekawe… zrobiłeś dobrą organizację projektu a mimo wszystko twoja organizacja leży ( przynajmniej tak to widać od naszej strony 😀 ). Oczywiście z niecierpliwością czekam na kolejną część i nie pisz już że nie masz czasu, bo marnujesz go na odpisywanie mi xD Pozdrawiam 😀

    • Organizacja nie leży, ten art czekał gotowy na opublikowanie MinMaxa, z resztą wspomniałem, że będzie powstawał dość powoli: „Co do częstotliwości nowych wpisów, to absolutnie niczego nie obiecuję i pozostańmy przy wersji, że przynajmniej raz na miesiąc się coś pojawi”. Jak widać wszystko się zgadza. Przy okazji gratuluję wygranej na warsztat.gd
      Pozdrawiam

      • Adam Pajkert

        Taka wygrana to nie wygrana… Byłem jedyny który wysłał program, który był do dwóch kategorii… Trzecia to był Reverse Engineering. Inni poddali się w walce z programem, albo im nie pykło. Zresztą mój program też nie działał w 100% jak miał mieć wg. założeń i był pisany „na szybko”. Tak mnie boli ten programik, niby napisany w 7 godzin, ale tyle rzeczy da się tam usprawnić… ARGH. Perfekcjonizm mnie dobija. Jeśli potrzebowałbyś z czymś pomocy lub mógłbym jakoś przyśpieszyć tworzenie nowych wpisów, to ja bardzo chętnie 😉 Pozdrawiam.

  • morsisko

    Zapowiada się super. 🙂
    Chciałem tylko dodać (choć nie wiem, być może było to zamierzone) że na końcu tekstu jest pewien dziwny fragment, tzn:

    „[…]|linie kodu oraz sprawimy, że sprawimy że gracz będzie w stanie się poruszać.”, a jeszcze dokładniej mówiąc, „sprawimy, że sprawimy”.

  • darvd29

    Byłoby super gdyby kolejne części kursu pojawiały się jak najszybciej, mam strasznie duże ambicje, a Twój blog czyta się wspaniale 😀

    • Dzięki za miłe słowa, ale muszę Cię zmartwić bo gdyby zaimplemetnować tylko obecne klasy (bez dodawania kolejnych, a dojdą nowe bo po prostu muszą) to zajmie to materiału do omówienia jest mniej więcej na ~ 14 lekcji, co przy dobrym tempie 2 lekcji na miesiąc daje 7 miesięcy, a i tak myślę że dojdzie do okolic roku. Zależy to od tego jak dużo czasu będą zajmowały mi studia.

  • Wszedłem na ten blog, by poczytać poradniki SFML, a tu taka niespodzianka. Aż zrobię coś, czego nigdy nie robię – zasubskrybuję kanał RSS. :>

  • diablol

    Czekam na nastepną część, a tak w ogóle masz ten diagram klas uzupełniony bo chciałbym zobaczyć jak to będzie wyglądało?

    • Nie mam go uzupełnionego, o ile to się nie zmieni (i nie pójdę do pracy) to najpewniej kolejne części pojawią się na wakacjach. Obecnie siedzę przy dwóch większych projektach i jak widać nie mam kiedy dodawać czegokolwiek na bloga

      • diablol

        Ok, mam jeszcze pytanie :D. W klasie TextureManager będzie jakiś Vector czy tablica która przechowywać będzie tekstury i funkcje ustawiające je, a SpriteRenderer będzie wyświetlał Sprite’y ale w jakiej klasie będą one przechowywane?

        • W TextureManager będzie hashmapa przechowująca tekstury, w razie potrzeby będzie przekazywała do nich wskaźniki, wczytywała nowe tekstury / usuwała z pamięci niepotrzebne.

          SpriteRenderer będzie zajmował się renderowaniem spriteów, tzn będzie posiadał listę / hashmape obiektów które można rysować i będzie ustawiał je wg warstw

          • diablol

            Dzięki za odpowiedź.

          • diablol

            Tak dla testu zacząłem pisać snake przed większą grą na podstawie podobnego do tego rozplanowania klas, ale nie wiem po co klasa SimpleGraphicsEngine dziedziczy po SpriteRenderer będzie w niej po za składnikami dziedziczonym? Zrobiłem tak jak ty klasę TextureManager(http://wklej.org/id/2397017/ nie wiem czy ona działa bo nie kompilowałem) i w niej przechowuję tekstury z gry, ale teraz nie rozumiem jak stworzyć to listę w SpriteRenderer(http://wklej.org/id/2397028/). Będe miał jeszcze klasę Snake i tam chciałbym mieć klasę Collider i metodę, która będzie wykrywała kolizję ze ścianą i własnym sobą(dobrze tak robię?) to gdy ona zwróci true to mam wyzerować to listę, ale jak przecież ona będzie przechowywała zarówno bloki z Snake jak inne Spritey’y takie jak: przyciski menu, tło i głowa snake i w jaki sposób się do niej odwołam z klasy Snake? Masz może jakiś pomysł?

          • Nie pisałbym tak prostej gry jak snake w oparciu o tak rozbudowany system: „Less is More”

          • diablol

            Jak będę pisał inną większą grę to jak tego użyć?

          • Z założenia SpriteRenderer to miał być tylko interfejs na silnik renderujący, polecam zajrzeć na repo: https://github.com/PiGames/Bomberman gdzie robimy coś prostszego i mającego pewnie więcej sensu.

            Do większych gier raczej poleciłbym ci jakiś silnik do gier, a takie rozbijanie bym zrobił gdybyś chciał pisać własny framework do gier

  • Łukasz Kaptur

    jak raz chciałem kogoś choć trochę wesprzeć i wyłączyłem adblocka a tu nie ma reklam 🙁

    • Przykro mi, ale nawet nie planuję ich dodać 😉 Jeżeli chcesz mnie wesprzeć to zachęcam do polecania bloga osobom (np znajomym), które mogą się zainteresować poruszanymi przeze mnie tematami