Dziedziczenie i polimorfizm -- przykłady
To laboratorium jest opcjonalne i zawiera przykłady mające na celu pokazanie dziedziczenia i polimorfizmu na większych przykładach.
Z racji, że nie jest to „regularne” laboratorium, nie obowiązuje ono do kolokwium, a za rozwiązanie zadań nie będą przyznawane plusy/punkty. Oczywiście w przypadku jakichkolwiek pytań czy wątpliwości zapraszam do zadawania pytań / pisania maili / przychodzenia na konsultacje! Udzielę wszelkich wyjaśnień
Fin de Siecle
Jest to prosta gra platformowa na licencji GPL, która bardzo silnie bazuje na koncepcji polimorfizmu . Rysunki z gry są autorstwa Michała Klimczyka.
Kompilacja i uruchomienie
Aby skompilować grę, zainstaluj następujące pakiety:
sudo apt-get install libsdl1.2-dev libsdl-ttf2.0-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev
Następnie pobierz kod źródłowy: Fin De Siecle, rozpakuj go i skompiluj poleceniem
make
Uruchom grę poleceniem
./exe
Organizacja kodu
Wszystkie obiekty reprezentujące jakieś byty w grze (woda, drzewa, bohater, przeciwnicy, etc.) są przechowywane w jednej liście (tak naprawdę w kilku listach w zależności od tego, czy obiekt jest dynamiczny - jak woda, czy statyczny - jak drzewo). Aby było to możliwe, wszystkie klasy muszą dziedziczyć po tej samej klasie bazowej (w tym wypadku object - wszystkei, dynamic - dynamiczne, colisives - generujące kolizje). W głównej pętli gry (plik game.cpp, metoda game()) lista jest przeszukiwana, obiekty są rysowane i wykonują jakieś swoje zaprogramowane czynności. Jednakże zachowania drzew, bohatera czy przeciwników muszą być inne!
Wszystkie obiekty są polimorficzne. W momencie gdy wywoływana jest pętla rysująca elementy dynamiczne obiekty (plik game.cpp linia 164), wywoływana jest metoda draw na każdym obiekcie z listy. W liście znajdują sie przeciwnicy, woda, etc. Ale dzięki polimorfizmowi, zawsze wywoływana jest odpowiednia metoda rysująca odpowiedni obiekt, mimo że jest ona wywoływana z klasy bazowej dla tych obiektów:
list<dynamic*>::iterator it;
for(it = dynamics.begin();it != dynamics.end();it++)
(*it)->draw(screen);
Dla chętnych
Dopisz nowego przeciwnika do gry, który będzie rzucał się w pościg za głównym bohaterem gdy go zobaczy. Stwórz klasę identyczną do klasy policeman - pamiętaj aby zaimplementować wszystkie metody potrzebne do poprawnego „funkcjonowania” przeciwnika (zamiast metody gdzie policjant rzuca pałką, napisz metodę która będzie wprowadzała Twoją postać w sprint).
Aby twoja postać została wczytana, musisz dokonać dwóch modyfikacji:
W pliku load_unload.cpp w metodach load i unload dopisz odpowiednie linijki wczytujące postać i zwalniające po niej pamięć.
W pliku z planszą gry -
gdata/level0.dat dopisz linijkę, która będzie opisywać współrzędne Twojego bohatera na planszy. Na przykład chcą postawić postać, która ma identyfikator
myMan na 1000 pikselu planszy licząc od lewej i na 300 pikselu licząc od dołu wpisujemy linijkę:
myMan 1000 300
Math Evaluation
Jest to ewaluator wyrażeń matematycznych, którego działanie opiera się na polimorfizmie.
Kompilacja i uruchomienie
Pobierz i rozpakuj archiwum Math Evaluation a następnie skompiluj program poleceniem
make
Uruchom program poleceniem
./exe
Istnieje możliwość podawania wyrażeń do ewaluacji jako parametrów programu, ale w tym celu trzeba odkomentować odpowiednią linijkę w funkcji main.
Ćwiczenie
Tekstowa gra RPG. Dla chętnych: można połączyć się w grupy dwuosobowe.
Scenariusze (ta lub inne strony, albo własny pomysł):
-
-
Wersja podstawowa
Program powinien składać się z następujących klas:
Klasa GameStep i dziedziczących po nich: Start, Battle, Riddle, Travel, Fate
Klasa Heroe
Klasa Artefact
GameStep
Klasa GameStep powinna być klasą abstrakcyjną posiadającą następujące wirtualne metody czyste:
void play(Heroe&)
GameStep* getNext()
Klasy dziedziczące po GameStep powinny:
Start - po wywołaniu play na bohaterze, losować jego atrybuty (punkty zdrowia, sila, zrecznosc), po wywolaniu getNext, powinna być losowo zwrócony jeden z obiektów GameStep.
Battle - po wywołaniu
play powinna rozpocząć się walka. Obiekt klasy
Battle posiada także atrybuty takie jak punkty zdrowia, sila i zrecznosc. Walka toczy się dopóki przeciwnik, albo bohater nie stracą wszystkich punktów życia. Obliczanie strat w punktach życia dla stron może odbywać się na przykład według takiego wzoru:
sila*rand()%zrecznosc
Travel - po wywołaniu play na bohaterze, powinno pojawić się pytanie, czy chce wyruszyć w niespokojne okolice, gdzie czeka go walka, czy też woli podróżować po bezpiecznej drodze. W zależności od wyboru, metoda getNext zwracać będzie albo Fate - gdy wybierze podróż, albo Battle gdy wybierze walkę.
Fate - Może oznaczać szczęście albo pecha - losowane randomowo. W przypadku szczęścia bohater znajduje eliksir, albo artefakt, który zwiększa jego atrybuty. W przypadku pecha ma 20% szans na utratę swoich wcześniejszych artefaktów.a
Riddle - Tylko po bitwach. Zagadka, której rozwiązanie skutkuje otrzymaniem jednej magicznej runy, lub całkowitym uleczeniem gracza. Gracz wygrywa gdy zbierze 7 magicznych run. Przegrywa, gdy straci wszystkie punkty życia.
Artefact
Klasa Artefact powinna być klasą abstrakcyjną, po której dziedziczyć będą różne artefakty. Dodające siłę, zręczność, czy punkty życia. Bohater (czyli obiekt klasy Heroe) powinien posiadać listę artefaktów. Zaprojektuj sam jak powinna wyglądać ta klasa.
Heroe
Klasa Heroe powinna reprezentować bohatera. Powinna posiadać listę artefaktów, która to lista będzie wymagana do obliczenia aktualnej siły, zręczności i punktów życia bohatera. Liczba magicznych run może być zwyczajnym polem typu integer (chyba że ktoś wpadnie na pomysł, że magiczne runy też dodają jakieś atrybuty).
Resztę klasy zaprojektuj według uznania.
Fabuła
Można pokusić się o wprowadzenie fabuły do gry, poprzez ustalenie jakichś zbiorów z których są losowane walki, zagadki itp. Na przykład jeśli bohater posiada 1 magiczną runę, to losuj ze zbioru „A”, jeśli 2 to ze zbioru „B”, itd. (W takim wypadku metoda getNext musiałaby pobierać jako parametr obiekt bohatera w celu ustalenia w jakiej fazie gry się znajduje).
Wersja zaawansowana
Rozbudowa polegałaby na pewno na dodaniu mapy, broni, questów.