piątek, 8 maja 2020

Reminiscencje Twórcy Systemów cz. 7: Pierwsza gra, pierwszy system, pierwsza porażka

W 2003 pisanie gier jest trudniejsze niż dzisiaj. Nie ma kompleksowych game makerów czy środowisk typu Unity. Żeby napisać grę musisz stworzyć wszystko prawie od zera. SDL oferuje funkcje typu wczytaj obrazek, naklej go na powierzchnię (surface), odśwież surface ekranu. Jest obsługa zdarzeń (kliknięcia i ruchy myszą, naciśnięcie klawisza itd.), wczytanie i odegranie dźwięków - to wszystko wystarcza do stworzenia gry 2d.

Mam marzenie: zrobić grę przypominającą Fields of Glory - pierwszą najważniejszą grę, gdy dostałem własnego PC-ta. Była to strategia w rozdzielczości 320x240, w której wojska Napoleona walczyły z Anglikami i Prusakami. Powiedzmy taki poprzednik gier z serii Total War. W mojej grze naparzałyby się czerwone kurtki (Anglicy z XVIII wieku) z niebieskimi. Kojarzycie te filmy, w których linia żołnierzy z charakterystycznymi białymi pasami skrzyżowanymi na korpusie, maszeruje z muszkietami na ramieniu, dobosze przygrywają na werblach, przygrywają piszczałki. W końcu zatrzymują się, oficer wykrzykuje komendę, wycelowują muszkiety, ‘fire!’ i salwa zmiata pierwszą linię wroga. A potem odpowiada salwa z drugiej strony, czasem idą na bagnety, a czasem strzelają aż ktoś zacznie uciekać. Świetnie została pokazana taka bitwa w Patriocie z Melem Gibsonem (battle of Camden).

Przez 2-3 tygodnie rysuję pixel po pixelu w Gfx2 żołnierza w 8 kierunkach: stoi, maszeruje, nabija muszkiet, oddaje strzał. Powstają też grafiki terenu i równoległoboki, z których powstanie pole bitwy (tzw. rzut izometryczny jak w Cywilizacji 2). Oprócz rysowania dużo planuję w zeszycie, jak będę programował grę i tworzę biblioteki narzędziowe. Ponieważ chcę być “pro” w C++, używam gdzie się da ówczesne nowinki typu template’y, przeciążanie operatorów. Wydaje mi się, że im lepiej poznam tajniki języka, tym lepszy będzie projekt.

Nie będę się rozpisywał nad pracami. Liczą się twarde wnioski. Udaje mi się osiągnąć pierwszy etap, tj. pole bitwy, po którym maszeruje kolumna o zdefiniowanej liczbie żołnierzy (N x M). Idą tam gdzie się kliknie, najpierw obracają się, każdy żołnierz maszeruje na swoje miejsce w formacji i dopiero wtedy idą wszyscy razem. Wygląda to dla mnie super, ale kod jest tak skomplikowany, że nie sposób zrobić coś więcej. Obliczenia matematyczne mieszają się ze sterowaniem, zależności chodzenia w formacji z poszukiwaniem swojej pozycji w formacji. Po kilku miesiącach prac stwierdzam, że projekt mnie przerósł i na pierwszą grę muszę wybrać prostszy temat.

W moim kodzie wszystko wydaje się ważne - wiedza o strukturze przechowywania grafiki, hierarchia przesłaniania żołnierzy na polu. Mnóstwo “istotnych” danych przechowywanych w enumach i odrębnych klasach. Wydaje mi się, że tak powinno być, bo tak pokazują tutoriale do C++. Ilość powiązań jawnych i niejawnych jest nie do ogarnięcia.

W programowaniu obiektowym klasa powinna być jak czarna skrzynka: udostępniać prosty, logiczny interfejs i przyjmować jak najmniej argumentów. Jeśli tych ustawień jest zbyt dużo, to prawdopodobnie znak, że klasa obejmuje elementy z różnych poziomów abstrakcji i agreguje zależności. W takim systemie nie jesteś zazwyczaj w stanie używać obiektów klasy bez znajomości jej implementacji.

Piszę o tym, ponieważ przez lata pracowałem z systemami w firmach, które cierpiały na ten grzech. Dodawanie funkcjonalności było makabrą, ludzie kopiowali działające klasy, zmieniali nazwy zmiennych, dodawali nowe enumy w 10 różnych miejscach i jakoś działało. Od kilku kluczowych klas byli ‘eksperci’, którzy napisali je lata wcześniej i tylko oni wiedzieli jak coś do nich doklejać. Żeby rozwijać system w oparciu o te klasy należało nie tylko znać ich rozbudowane API, ale też masę niejawnych zależności typu kolejność odpalania funkcji, ograniczenia w ustawieniu obiektów, które wpadały do klasy. Jakiekolwiek niestandardowe użycie API z założenia nie mogło działać.

Wielu programistów nie wychodzi poza schemat, który opisałem przy swojej pierwszej grze. Biegle operują w C++, są w stanie dość szybko dotrzeć do celu i napisać kod, który działa - pierwszy etap systemu wygląda jak powinien. Podobnie jak ja wtedy chcą na siłę używać nowinki oferowane przez język. Niestety rozwijanie takiego systemu jest syzyfową pracą. W odrębnym artykule opiszę, jak rozwiązać ten problem.

PS Na obrazku grafiki wykonane po pixelach w Gfx2, jakie zrobiłem na przełomie 1999/2000 do gry o Kampanii Wrześniowej, którą robiliśmy z Kamilem. Podobnie powstawali żołnierze do mojej pierwszej gry.


2 komentarze:

  1. Jakiej biblioteki graficznej użyłeś? Moje pierwsze doświadczenia z programowaniem były podobne, zrobiłem snake'a w c++h, potem kolejna gra to hexagonalne pola tworzące swiat i typka, który chodzil i odkrywał go. Jednak wydajność sdl, choć sam sdl był łatwy w użyciu, była mizerna

    OdpowiedzUsuń
    Odpowiedzi
    1. Tworzyłem gry 2d, więc korzystałem z funkcji SDL. Bez problemu odświeżała ekran 30 fps i niewykorzystany czas oddawałem do systemu. Jak niewiele się działo, to zużycie procka chodziło na 10%, w programach edukacyjnych praktycznie zero wpływu. Ale były też gry gdzie na raz odświeżałem kilka warstw pełnoekranowych i potrafiły zająć cały CPU.

      Usuń

W ramach eksperymentu wyłączam moderację komentarzy.

Zasady komentowania:
- żadnego spamu i reklam (także linków do serwisów w nazwie użytkownika),
- komentarze obraźliwe będą usuwane,
- proszę o zachowanie kultury i brak kłótni; różnice zdań należy wyrażać poprzez dyskusję wspartą argumentami.

Podtwórca