Celem tego samouczka jest nauczenie programowania gier 2D i języka C za pomocą przykładów. Autor programował gry w połowie lat 80. i był projektantem gier w MicroProse przez rok w latach 90. Chociaż wiele z tego nie ma związku z programowaniem dzisiejszych dużych gier 3D, w przypadku małych gier casualowych będzie to przydatne wprowadzenie.
Implementacja Snake'a
Gry takie jak wąż, w których obiekty poruszają się nad polem 2D, mogą reprezentować obiekty gry w siatce 2D lub jako tablica obiektów o pojedynczym wymiarze. „Obiekt” oznacza tutaj dowolny obiekt gry, a nie obiekt używany w programowaniu obiektowym.
Sterowanie grą
Klawisze są przesuwane z W = w górę, A = w lewo, S = w dół, D = w prawo. Naciśnij Esc, aby wyjść z gry, f, aby przełączyć liczbę klatek na sekundę (nie jest zsynchronizowany z wyświetlaczem, więc może być szybki), klawisz Tab, aby przełączyć informacje debugowania i p, aby go zatrzymać. Po wstrzymaniu zmiany napisów i wąż miga,
W wężu znajdują się główne obiekty gry
- Wąż
- Pułapki i owoce
Do celów rozgrywki tablica ints pomieści każdy obiekt gry (lub część dla węża). Może to również pomóc podczas renderowania obiektów w buforze ekranu. Zaprojektowałem grafikę do gry w następujący sposób:
- Poziome ciało węża - 0
- Pionowy korpus węża - 1
- Głowa w 4 x 90-stopniowych obrotach 2-5
- Ogon w obrotach 4 x 90 stopni 6-9
- Krzywe zmiany kierunków. 10-13
- Jabłko - 14
- Truskawka - 15
- Banan - 16
- Pułapka - 17
- Wyświetl plik grafiki węża snake.gif
Dlatego warto stosować te wartości w typie siatki zdefiniowanym jako blok [SZEROKOŚĆ * WYSOKOŚĆ]. Ponieważ w siatce jest tylko 256 lokalizacji, postanowiłem zapisać ją w tablicy jednowymiarowej. Każda współrzędna na siatce 16 x 16 jest liczbą całkowitą 0–255. Użyliśmy ints, abyś mógł powiększyć siatkę. Wszystko jest zdefiniowane przez #defines z WIDTH i HEIGHT zarówno 16. Ponieważ grafika węża ma 48 x 48 pikseli (GRWIDTH i GRHEIGHT #defines), okno jest początkowo zdefiniowane jako 17 x GRWIDTH i 17 x GRHEIGHT, aby było nieco większe niż siatka.
Ma to zalety w szybkości gry, ponieważ użycie dwóch indeksów jest zawsze wolniejsze niż jeden, ale oznacza to, że zamiast dodawać lub odejmować 1 od współrzędnych Y węża w celu poruszania się w pionie, odejmujesz SZEROKOŚĆ. Dodaj 1, aby przejść w prawo. Jednak podstępnie zdefiniowaliśmy również makro l (x, y), które konwertuje współrzędne xiy w czasie kompilacji.
Co to jest makro?
# zdefiniować l (X, Y) (Y * WIDTH) + X
Pierwszy wiersz to indeks 0-15, drugi 16-31 itd. Jeśli wąż znajduje się w pierwszej kolumnie i przesuwa się w lewo, to sprawdzanie trafienia w ścianę, przed przejściem w lewo, musi sprawdzić, czy współrzędna% WIDTH == 0, a dla współrzędnej prawej ściany% WIDTH == WIDTH-1. % Jest operatorem modułu C (jak arytmetyka zegara) i zwraca resztę po dzieleniu. 31 dział 16 pozostawia resztę 15.
Zarządzanie wężem
W grze używane są trzy bloki (tablice int).
- snake [], bufor pierścieniowy
- shape [] - Przechowuje indeksy graficzne Snake
- dir [] - Utrzymuje kierunek każdego segmentu węża, w tym głowę i ogon.
Na początku gry wąż ma dwa segmenty z głową i ogonem. Oba mogą wskazywać w 4 kierunkach. Na północy głowa ma indeks 3, ogon wynosi 7, dla głowy wschodniej jest 4, ogon wynosi 8, dla głowy południowej jest 5, a ogon wynosi 9, a na zachodzie głowa jest 6, a ogon ma 10. Podczas gdy wąż ma dwa segmenty, głowa i ogon są zawsze oddalone od siebie o 180 stopni, ale po wzroście węża mogą wynosić 90 lub 270 stopni.
Gra rozpoczyna się od głowy skierowanej na północ w miejscu 120 i ogonem skierowanym na południe w 136, mniej więcej w centrum. Przy niewielkim koszcie około 1600 bajtów pamięci możemy uzyskać zauważalną poprawę prędkości w grze, utrzymując lokalizacje węża we wspomnianym powyżej buforze pierścieniowym węża [].
Co to jest bufor pierścieniowy?
Bufor pierścieniowy to blok pamięci używany do przechowywania kolejki o stałym rozmiarze i musi być wystarczająco duży, aby pomieścić wszystkie dane. W tym przypadku jest to tylko dla węża. Dane są wypychane z przodu kolejki i usuwane z tyłu. Jeśli przód kolejki uderzy w koniec bloku, wówczas się zawija. Dopóki blok jest wystarczająco duży, przód kolejki nigdy nie dogoni tyłu.
Każde położenie węża (tj. Pojedyncza współrzędna int) od ogona do głowy (tj. Do tyłu) jest przechowywane w buforze pierścieniowym. Daje to korzyści prędkości, ponieważ bez względu na czas węża, tylko głowa, ogon i pierwszy segment po głowie (jeśli istnieje) muszą być zmieniane podczas ruchu.
Przechowywanie go do tyłu jest również korzystne, ponieważ gdy wąż dostanie jedzenie, wąż wzrośnie, gdy będzie następny ruch. Odbywa się to poprzez przesunięcie głowicy o jedno miejsce w buforze pierścieniowym i zmianę starej lokalizacji głowicy na segment. Wąż składa się z głowy, 0-n segmentów), a następnie ogona.
Gdy wąż zjada jedzenie, zmienna atefood jest ustawiona na 1 i zaznaczona w funkcji DoSnakeMove ()
Moving the Snake
Używamy dwóch zmiennych indeksu, headindex i tailindex, aby wskazać lokalizacje głowy i ogona w buforze pierścieniowym. Te zaczynają się od 1 (headindex) i 0. Tak więc miejsce 1 w buforze pierścieniowym zawiera położenie (0-255) węża na planszy. Lokalizacja 0 zawiera lokalizację ogona. Gdy wąż przesuwa się o jedno miejsce do przodu, zarówno tailindex, jak i headindex są zwiększane o jeden, zawijając się do 0, gdy osiągną 256. Więc teraz miejsce, w którym była głowa, jest tam, gdzie jest ogon.
Nawet z bardzo długim wężem, który jest kręty i zwinięty w powiedzmy 200 segmentów. tylko indeks głowy, segment obok głowy i indeks ogona zmieniają się przy każdym ruchu.
Uwaga ze względu na sposób SDL działa, musimy narysować całego węża w każdej ramce. Każdy element jest wciągany do bufora ramki, a następnie odwracany, aby został wyświetlony. Ma to jednak jedną zaletę, ponieważ możemy narysować węża płynnie przesuwając kilka pikseli, a nie całą pozycję siatki.