Projekt: tworzymy własną grę (1) – intro

Ten wpis jest pierwszym z przewodniej linii tego bloga. Zajmę się swoim głównym projektem od samego początku, wyjaśniając krok po kroku co, jak i dlaczego.
Nie gwarantuje, że nie popełnię żadnego błędu, przedstawię idealnie czysty kod, który nie będzie wymagał żadnych poprawek. Jestem amatorem i samoukiem, który stara się programować, najlepiej jak potrafi, ale jestem też otwarty na krytykę i dyskusję. Zakładam, że czytelnik zna podstawy programowania, takie jak zmienne, tablice, pętle, instrukcje warunkowe operatory logiczne czy funkcje. Jak będę wprowadzać bardziej zaawansowane techniki programowania, to zawsze umieszczę, na początku artykułu, linki do darmowych materiałów na ten temat.

Siadając do pisania tego posta, przemyślałem ponownie swój projekt i już na etapie samych założeń zauważyłem jego braki i błędy. To naprawdę niesamowite, jak chęć pokazania swojej pracy innym wpływa na samokrytycyzm. Tak więc mimo pozornego zaawansowania programu będę pisać go na nowo, tylko czerpiąc z niego dotychczasowe pomysły, lub kawałki kodu, które nie będą wymagały zbytniej interwencji. Tak więc do dzieła: pora na ciut teorii i filozofii na rozgrzewkę.


1. Założenia gry: 

Ma to być gra konsolowa opierająca się na dwu wymiarowej planszy podobnej do szachownicy, na której możliwy jest ruch w płaszczyźnie poziomej w kierunkach podstawowych (czyli bez skosów). Gracz będzie poruszać się „wewnątrz” nie mając podglądu na całą planszę. Będę chciał umieścić w grze skrzynie, przeciwników a w dalszym etapie fabułę. Potrzebny będzie też system punktujący oraz zapis punktacji do pliku na koniec gry. To tyle założeń na początek. Przed każdym etapem tworzenia gry, będę umieszczał osobne założenia stawiane dla tego kawałka kodu.

2. Na początek: pętla.

To, że tworzymy prostą grę konsolową, nie zwalnia z zachowania przejrzystości kodu i jego optymalizacji. Tak więc do dzieła:
Główna sekcja gry to jedna, powtarzana aż do końca gry pętla składająca się z konkretnych elementów, i na tej jednej pętli idealnie by było poprzestać. Oczywiście, kusi, aby wszystko „uprościć”, i zagnieździć kilka pętli w sobie: (te same kolory oznaczają tę samą pętlę)

(strat – (menu główne – ( konstruowanie świata – ( początek obszaru -( wyświetlanie informacji -(podejmowanie decyzji) – reakcja zwrotna) – albo -> ( przejście do: początek obszaru –  ( albo -> ( koniec gry i zamykanie świata ) – menu główne) – albo ->  ( konstruowanie świata (   – albo -> wyjście z gry).

Gdy zostanie to rozpisane, nagle zaczyna wyglądać strasznie skomplikowanie. Do tego ma jedną bardzo ważną wadę, którą powoduje coś tak złowróżbnego, jak:

3. Optymalizacja.

A w zasadzie jej brak. Musimy sobie uświadomić, dlaczego zagnieżdżanie się w sobie pętli nie zawsze jest optymalne (poza faktem, że jest to po prostu mało czytelne).

Gdy uruchamiasz program i pracujesz tylko w funkcji main, jedyne co musisz pamiętać, to regułę, że każda zmienna stworzona po { znika, gdy pojawia się zamykający ją } wygląda to mniej więcej tak:

  • {int a          // dostępne: a
  •   { int b       // dostępne a,b
  •     { int c     // dostępne a,b,c
  •     }             // dostępne a,b
  •   }               // dostępne a
Każdy z tych poziomów tworzy nowe dane, które zajmują kolejny kawałek pamięci, a jeśli nasza środkowa pętla ma pracować przez 99% czasu, to czemu przechowywać też te dane, które są potrzebne tylko na samym początku programu? Oczywiście, w tym malutkim projekcie nie ma to, dla komputera znaczenia, ale nie zwalnia to z pisania kodu tak, jak by miał być uruchamiany na kalkulatorze. Zawsze staramy się odciążać pamięć, zamykając i czyszcząc wszystko to, co nie będzie używane przez dłuższy czas (co stanie się szczególnie istotne, gdy pojawią się wskaźniki). Ponadto, jak za chwilę pokażę kierowanie się tymi zasadami, wpłynie też na większą przejrzystość kodu. Pomyślmy chwilkę, jak zmieścić całą grę tylko w jednej, głównej i jednej pomocniczej pętli? Moja propozycja jest dość prosta:

 4. Komenda IF

    To będzie największy sprzymierzeniec, program będzie się pytać przy każdym przebiegu pętli o wszystko, co istotne i jeśli będzie potrzebne, to dopiero wtedy uruchomi konkretną funkcję. Działanie programu rozpocznie się tworzeniem tego, co stałe przez cały czas gry, czyli świat i gracza, natomiast całą resztę w miarę możliwości będzie wywoływane tylko w razie potrzeby. „IF” należy traktować , jako ciągłe pytanie się o wszystko: Czy chcesz grać? [ if (!czy gramy) return status_gry; ] jaki chcesz poziom trudności [ if (jaki poziom == trudny) poziom trudności  = 2; ] czy postać się poruszyła, czy zaatakowała i tak dalej…
Zaczynam od stworzenia pętli pomocniczej, która nam bardzo ułatwi wszystko i przekornie będzie wbrew swojej nazwie największą pętlą, obejmującą całą grę. Niech wygląda to tak:
 
  • Początek pętli pomocniczej.
  • -> (wyświetl menu główne) – //pobierz dane i graj lub wyjdź
  • stwórz świat gry na podstawie wyboru z menu
  • stwórz gracza na podstawie wyboru z menu
  • {tu będzie nasza gra}
  • koniec rozgrywki: -> tabela wyników, zapis wyniku do pliku.
  • czyszczenie gracza i kasowanie danych z pamięci
  • czyszczenie świata i kasowanie danych z pamięci
  • czy: grasz dalej? -> powrót do początku pętli  lub wyjście z programu.

Menu znajdzie się w osobnej funkcji, natomiast dane z pętli pomocniczej będzie traktowane tak ostrożnie, jak by to były dane globalne. Świat gry utworzony na tym etapie jest tylko szkieletem, który będzie się wypełniał w trakcie gry, tak samo, jak i dane umieszczone pod pojęciem „gracz”. Na koniec dzisiejszego wpisu dodam jeszcze:

 5. Szkielet pętli głównej.

 Główna pętla musi zawierać odwołanie do każdego z elementów gry, a jednocześnie powinna być jak najprostsza. Oczywiście to tu będzie się korzystać z ulubionego „if-a” prawie, w co drugiej linijce. Wypunktuje teraz kolejne kroki jakie trzeba zrobić za każdym razem:
 
  • sprawdź czy coś się wydarzyło na początek pętli, jeśli tak to wykonaj
  • wyświetl aktualny stan gry i opis świata.
  • poczekaj na decyzje gracza i w zależności od niej :
  • sprawdź czy gracz chce wyjść z gry (wyjdź z pętli głównej z informacją o przerwaniu)
  • * wykonaj interakcję/atak/ruch i uaktualnij świat i gracza *
  • sprawdź czy gracz żyje – (jeśli nie, wyjdź z pętli głównej z informacją o śmierci)
  • sprawdź czy gracz nadal ma czas (jeśli nie, jak wyżej z informacją o końcu czasu)
  • sprawdź czy gracz wygrał (jeśli tak, jak wyżej z informacją o wygranej)
  • wróć do początku pętli
 
 Punkt  *wykonaj interakcję/atak/ruch i uaktualnij świat i gracza * jest punktem złożonym z warunkowego wywoływania odpowiednich funkcji i można go śmiało podzielić głębiej:
 
  • jeśli gracz atakuje – wywołaj atak
  • jeśli gracz wchodzi w interakcję z otoczeniem – wywołaj interakcję
  • jeżeli gracz się przemieszcza – wywołaj ruch gracza
  • jeśli gracz nie robi nic  – przejdź dalej.
 
Celowo nie używam określeń „wywołaj funkcję” lub „wywołaj metodę”, bo to, jak będzie to technicznie realizowane, nie ma na ten moment najmniejszego znaczenia. Jednocześnie program zakłada, że gracz może w jednym cyklu wykonać tylko jedną z powyższych czynności.
 

To wszystko na dziś. W następnej części powstaną pierwsze linijki kodu. Na początek napiszę trochę o tablicach i wskaźnikach oraz stworzę szkielet naszego świata, starając się zmieścić w minimalnej ilości kodu, i sprostać założeniom.

7. Narzędzia:

Program będę pisać i sprawdzać w Visual Studio 2017. Kod powinien być kompatybilny z wszystkimi aktualnymi IDE i kompilatorami, takimi jak np. CodeBlocks, natomiast na pewno nie będzie działać w DevC++ i innych zabytkach podobnej klasy, szczególnie, że zamierzam korzystać z dobrodziejstw C++11 a możliwe, że pojawią się o jakieś elementy z C++17.

8. W następnym odcinku:

Przed przeczytaniem następnego  odcinka zapoznaj się z podstawami tablic i wskaźników do tablic. Pomocne niedługo może być, także posługiwanie się systemem Git. Linki do tutoriali znajdziesz we wcześniejszych postach.

Następny odcinek

 
Prośba na koniec: *nieaktualna*
 

Jeszcze kilka chwil, na starym blogu kotnakolana.blogspot.com będzie dostępna ankieta z pytaniem, w jakim języku prowadzić projekt: po polsku, czy po angielsku( nazwy zmiennych, funkcji, klas, obiektów).
Dawajcie też znać, jeśli uznacie, że jakiś kawałek kodu można napisać lepiej! To bardzo istotne, ponieważ jak każdy chyba już wie. że:

God killa a Kitten
Miały być koty – to są 😉
 

2 myśli na temat “Projekt: tworzymy własną grę (1) – intro

    Dodaj komentarz