Po długiej przerwie przyszedł czas na kolejną aktualizację; Wprowadziłem do kodu mnóstwo zmian, a także zmusiłem węża do samodzielnego poruszania się; Poniżej wypunktowane zmiany;
1. Podział kodu
Kod został podzielony na 10
modułów, dzięki czemu całość stała się bardziej czytelna, a same moduły są krótsze; Opis poszczególnych modułów:
moduł |
opis |
SnakeASCII.lpr |
główny moduł projektu, tworzy, wywołuje i niszczy klasę gry |
Game.pp |
zawiera główną klasę gry |
Controller.pp |
zawiera klasę do obsługi klawiatury |
Painter.pp |
zawiera klasę, służącą do malowania interfejsu (barierek planszy, kępek trawy, węża i złotka) |
Converter.pp |
zawiera klasę, posiadającą statyczne funkcje konwertujące, przydatne w kilku klasach |
Timer.pp |
zawiera klasę stopera, potrzebną do obsługi klawiatury oraz automatycznego poruszania się węża (w późniejszym czasie także do liczenia czasu rozgrywki) |
Level.pp |
zawiera klasę pojedynczej planszy, z możliwością ładowania jej danych z plików konfiguracyjnych |
Snake.pp |
zawiera klasę węża (w późniejszym czasie przyda się także do rysowania węża w tutorialach) |
Gold.pp |
zawiera klasę z informacjami o złotku, losowanym na planszy |
Common.pp |
zawiera zbiór wspólnych stałych i typów, wykorzystywanych w całym projekcie |
2. Ruchy węża
Skorzystałem z sugestii @dzek69'a i oprogramowałem węża tak, że możliwe jest przechodzenie przez swoje ciało; Ale to przechodzenie nie jest takie dowolne - zbyt łatwo by było;
Wąż może przejść jedynie w miejscu, w którym było złotko; Taki moduł węża jest tak samo jak wcześniej, rysowany innym-jaśniejszym kolorem; Kolejnym ograniczeniem jest możliwość przejścia przez moduł węża, który nie znajduje się na jego zakręcie - w takim przypadku, nawet jeśli w tym module znajdowało się złotko - zostanie wykryta kolizja i wąż "umrze";
Jeszcze jednym ograniczeniem jest to, że przez moduł ze złotkiem można przejść tylko raz; Jeśli przez bramkę przejdzie ogon węża - jest ona zamykana; To nie jest celowe, tylko logiczne - bramka zostaje utworzona w miejscu, gdzie znajdował się poprzedni moduł, nie ogon; A skoro ogon przechodzi przez bramkę, to właśnie ogon posiada informację o złotku; Ogon przechodzi przez bramkę, więc i informacja o złapanym w tym miejscu złotku przechodzi do kolejnego modułu, a że ogon jest ostatni, to ta informacja zostaje tracona;
3. Klawiszologia
Niestety obsługa klawiszy strzałek jako znaków AnsiChar okazała się bardzo problematyczna, więc klawisze sterowania zostały zmienione; Poniżej lista obsługiwanych klawiszy z ich przeznaczeniem:
klawisz podstawowy |
nazwa wewnętrzna |
opis |
W |
INPUT_KEY_UP |
skręcenie węża w górę |
S |
INPUT_KEY_DOWN |
skręcenie węża w dół |
A |
INPUT_KEY_LEFT |
skręcenie węża w lewo |
D |
INPUT_KEY_RIGHT |
skręcenie węża w prawo |
spacja |
INPUT_KEY_TURBO |
maksymalne przyspieszenie węża |
klawisz specjalny |
nazwa wewnętrzna |
opis |
---------------- |
---------------- |
---------------- |
P |
INPUT_KEY_PAUSE |
zatrzymanie gry |
R |
INPUT_KEY_RESET |
zresetowanie gry |
Q |
INPUT_KEY_QUIT |
wyjście z gry |
Znaczenia klawiszy specjalnych chyba nie muszę tłumaczyć; W każdym razie póki co, zresetować grę można jeśli nie jest włączona pauza; A jeśli zabijecie węża, aby całkowicie wyjść z gry trzeba najpierw ją zresetować; |
|
|
Standardowo wąż wykonuje ruch co kilka klatek, sprawdzając stan tajmera; Ilość klatek oczekiwania na ruch węża zapisana jest w pliku planszy; W przypadku najmniejszej prędkości węża, ruch wykonywany jest co 6
klatek, w przypadku największej prędkości - co 2
klatki; Każda klatka gry trwa ~40ms
, więc dla najwolniejszej prędkości ruch wykonywany jest co ~240ms
, a przy największej - co ~80ms
; Istnieje pięć stopni prędkości - ich opis w tabelce:
typ ruchu |
nazwa wewnętrzna |
ilość klatek |
ilość milisekund |
najwolniejszy |
lossLow |
6 |
240ms |
średni |
lossMedium |
5 |
200ms |
szybki |
lossHigh |
4 |
160ms |
ekstremalny |
lossExtreme |
3 |
120ms |
niemożliwy |
lossImposible |
2 |
80ms |
Jak widać w tabelce, możliwe jest skorzystanie z trybu turbo; Charakteryzuje się on tym, że ruch węża wykonywany jest zawsze w każdej klatce, czyli co ~40ms ; Tryb ten włącza się wciskając i trzymają albo klawisz spacji, albo klawisz ruchu; Aby go wyłączyć, trzeba puścić trzymany klawisz; Tryb ten możliwy jest tylko w przypadku ruchu w jednym kierunku - przy wciśnięciu klawisza skrętu węża jest on automatycznie wyłączany; |
|
|
|
Obsługa ruchów węża napisana jest tak, że w jednej klatce wąż może skręcić tylko raz, nawet jak pomiędzy ruchami węża wciśnięto kilka razy klawisze sterowania (nie dotyczy trybu turbo); Klasa gry współpracuje z klasą kontrolera, który zapamiętuje tylko jeden wciśnięty klawisz, a kolejne znajdujące się w buforze inputu kasuje; Jeśli wciśnięto klawisz skrętu węża, gra oczekuje na moment, aż tajmer odliczy tyle cykli, ile klatek trzeba czekać na ruch (to zapisane jest w pliku planszy); Po obsłudze skrętu węża, zapamiętany klawisz jest kasowany; W przypadku klawisza trybu turbo (czyli spacji lub przytrzymanego W
, S
, A
lub D
) lub klawisza specjalnego, jest on obsługiwany w każdej klatce;
4. Znaki żywego węża
Poprzednio malowanie węża było uboższe, dlatego że nie obsługiwał on przechodzenia przez moduły ze złapanym złotkiem, a samo złotko było malowane na każdym module, łącznie z zakrętami, pomijając moduły głowy i ogonka; Teraz malowanie węża jest dużo bardziej skomplikowane;
Głowa węża zawsze posiada ten sam znak - znak prostokąta; Zakrętny węża posiadają te same znaki, co wcześniej; Natomiast zmienione zostało malowanie "karku" węża oraz ogona;
Jeśli moduł karku znajduje się w otwartej bramce (czyli głowa węża przeszła przez moduł ze złapanym złotkiem), rysowany jest zawsze krzyżyk, który łączy ciało węża; W przypadku ogona, sprawa jest jeszcze bardziej skomplikowana; Jeśli moduł ogona nie znajduje się w miejscu otwartej braki - rysowany jest aktualny znak, czyli albo podwójna prosta linia, albo znak zakrętu; Jeśli natomiast ogon znajduje się w miejcu otwartej bramki - rysowany jest znak T
, odwrócony w odpowiednią stronę, który także ładnie zamyka strukturę jego ciała; Zaobserwować to można lepiej przy najniższej prędkości węża;
5. Kolory żywego węża
Tutaj nowość - wprowadzone zostały schematy kolorów dla węża; Określone są w nich kolory modułów ciała węża, bez kolorów głowy oraz ogona, które zawsze są takie same - żółte; Każdy moduł ciała węża może być malowany na dwa kolory - ciemniejszy dla podstawowego modułu, w którym nie znajduje się złotko ani bramka, oraz jaśniejszy, w którym znajduje się złapane złotko lub otwarta bramka; Obsługiwane jest 6
schematów kolorów;
schemat |
stałe koloru |
typ modułu |
zielony |
Green |
podstawowy moduł ciała |
LightGreen |
moduł ze złapanym złotkiem lub otwartą bramką |
|
zielono-niebieski |
Cyan |
podstawowy moduł ciała |
LightCyan |
moduł ze złapanym złotkiem lub otwartą bramką |
|
niebieski |
Blue |
podstawowy moduł ciała |
LightBlue |
moduł ze złapanym złotkiem lub otwartą bramką |
|
fioletowy |
Magenta |
podstawowy moduł ciała |
LightMagenta |
moduł ze złapanym złotkiem lub otwartą bramką |
|
czerwony |
Red |
podstawowy moduł ciała |
LightRed |
moduł ze złapanym złotkiem lub otwartą bramką |
|
złoty |
Brown |
podstawowy moduł ciała |
Yellow |
moduł ze złapanym złotkiem lub otwartą bramką |
|
Schematów jest tyle, dlatego że tyle jest par kolorów - podstawowy + Light* (wyjątkiem jest złoty); Może ich być więcej - trzeba tylko powiększyć tablicę ze stałymi kolorów; |
|
|
Schematy te zostaną wykorzystane w późniejszym czasie, już po stworzeniu trybu "podróży", czyli przechodzenia plansza po planszy; Docelowo ma być 6
różnych "światów", po 10
plansz w każdej; Dla każdego świata, wąż będzie posiadał inny schemat kolorów;
Poniżej zrzuty wszystkich obsługiwanych na dzień dzisiejszy schematów kolorów węża:
Schemat kolorów węża także zapisany jest w pliku konfiguracyjnym danej planszy;
Samo malowanie węża jest bardzo szybkie, dzięki odmalowywaniu zawsze tylko i wyłącznie 4
modułów, bez względu na długość węża: modułów głowy, karku, ogona i modułu, który ogon opuścił; Barierki planszy oraz kępki trawy nigdy nie są odmalowywane, więc co klatka, to malowanie czterech modułów;
6. Śmierć węża
Kolejnym ficzerem jest malowanie animowanej śmierci węża; Charakteryzuje się ona tym, że w sposób animowany, kolejne moduły węża kolorowane są na szaro lub biało, a odcień szarego zależy od typu modułu - ciemniejszy dla podstawoweych modułów ciała, a jaśniejszy dla modułów ze złapanym złotkiem lub dla bramki;
Poniżej na zrzucie namalowany martwy wąż:
Szybkość malowania animowanej śmierci węża zależy od jego długości; Im dłuższy wąż, tym mniej wynosi czas odstępu pomiędzy malowaniem modułów;
7. Kolory planszy
W odróżnieniu od poprzedniej wersji, barierki planszy oraz kępki trawy mogą być malowane na wszystkie dostępne w konsoli standardowe kolory; Nie dotyczy to koloru czarnego, który zarezerwowany jest dla tła planszy;
Barierki planszy zawsze malowane są na jeden kolor, zapisany w pliku planszy; Nie ma możliwości namalowania kawałka barierki na inny kolor; Natomiast w przypadku kępek trawy, każda z nich może być malowana na inny kolor, ale - jedna kępka, jeden kolor;
Zarówno barierki planszy, jak i kępki trawy malowane są w sposób pseudo-animowany, malując wiersze z góry do dołu; W miarę fajny efekt wyszedł;
8. Pliki planszy
W dalszym ciągu korzystam z plików Ini do przechowywania informacji o danej planszy; Część informacji zawartych w pliku nie jest jeszcze obsługiwanych, jak nazwa planszy czy poziom jej trudności, i może zostać w późniejszym czasie albo wykluczona, albo zamieniona na inną;
Pliki te zostały zmodyfikowane - dorobiłem więcej sekcji oraz kluczy, stąd więcej rzeczy można ustawić; Zawartość przykładowego pliku planszy (ta plansza jest także w załącznikach oraz końcowym zrzucie ekranu) tutaj;
Dozwolone wartości dla poszczególnych kluczy wymienione są w pliku Readme.txt
, zawartym w katalogu levels
; Część katalogów jest póki co pusta, ale będą także używane w późniejszym czasie;
Zrzut planszy, którą opisuje powyższy plik konfiguracyjny:
9. podsumowanie
Gra zajmuje niewiele w pamięci i niewiele mocy procesora potrzebuje; Powinna działać szybko na nieobciążonym kompuerze; Jeśli komputer zostanie obciążony, zachowanie płynności gry będzie dokładnie takie same, jak obciążenie gry na Pegasusie - całość spowolni; I takiego efektu się spodziewałem, dlatego że chciałem tę grę napisać tak, jak dawno temu pisano gry na Pegasusa; Żadnych wątków, synchronizacji logiki i widoku itd., stąd też timer liczący klatki gry i oszczędność malowania konsoli; Wyszło jak dla mnie idealnie;
To chyba wszystko, co nowego udało się wprowadzić; Zgodnie ze sugestią @vpiotra, dodałem informacje o licencji; Wybrałem licencję GNU Lesser GPL 3 na wypadek, jakby ktoś chciał na tym zarobić :]
Co do problemu z kodowaniem znaków, o którym pisał @Krolik - Lazarus nie wspiera możliwości kompilowania jednego kodu na różne platformy; Podany w załącznikach plik wykonywalny będzie poprawnie wyświetlał znaki jedynie na Windowsach, gdzie ustalone ma kodowanie CP_UTF8
; Jeśli kod ma działać na uniksach - musi być pod uniksem skompilowany; Dodana jest obsługa menedżera łańcuchów dla uniksów, więc znaki powinny być poprawnie wyświetlane; Ja niestety nie mam możliwości skompilować kodu na którymś z Linuksów czy Mac OS X, więc to zadanie pozostawiam linuksiarzom;
To tyle - życzę miłego analizowania kodu (połowa jego objętości to komentarze) oraz grania - bo już można; W załączniku dodaję źródła gry oraz skompresowany UPXem plik wykonywalny w trybie release, skompilowany na WinXP, więc tylko dla windowsowców;
A co do GitHuba - chyba jeszcze nie czas, bo mało funkcjonalna jest ta "gra" :]