Znajdź i zjedz
Spis treści
Rozpoczęcie pracy
Naszą pracę nad projektem zaczynamy oczywiście od uruchomienia Greenfoot’a. Następnie tworzymy nowy projekt który możemy nazwać np. „Znajdź i zjedz”. Zrobimy to klikając na „Scenario” => „New” => Następnie wskazując folder docelowy projektu i podając nazwę projektu („Folder name”) klikamy przycisk „Create”.
Po kliknięciu na przycisk „Create”, naszym oczom ponownie ukazuje się główne okno Greenfoot’a
z nowymi elementami w panelu hierarchii klasy po prawej stronie.
Stworzenie Świata
Nasza gra oczywiście potrzebuje stworzenia świata, w którym będzie rozgrywać się jej akcja. W tym przypadku, taki świat możemy nazwać podwórkiem, na którym nasz bohater (np. jeż) będzie mógł swobodnie się poruszać. Tak więc na bazie klasy „World” tworzymy własną klasę np. „Podworko”. Nasze „Podowrko” odziedziczy wszystkie właściwości po klasie „World”.
Pamiętamy: |
Aby tego dokonać:
Kliknijmy prawym klawiszem na klasę „World”.
Z nowo otwartego menu wybierzmy „New subclass...”. Na ekranie pojawi się poniższe okno:
W polu „New class name” wpiszmy „Podworko” będzie to nazwa tworzonej klasy, następnie wybieramy tło naszego podwórka. Aby zamknąć okno kliknijmy „Ok”.
Na zakończenie kompilujemy stworzoną klasę, klikając przycisk „Compile”.
Pamiętamy: Obrazki wybieramy z biblioteki (w środkowej kolumnie „Image Categories” grupa backgrounds) lub z pliku, klikając “Import from file”. |
Po skompilowaniu możemy obejrzeć kod klasy „Podworko”. Klikamy na klasę „Podworko” prawym klawiszem i wybieramy „Open editor”. Edytor możemy również otworzyć przez podwójne kliknięcie, lewym klawiszem myszy klasy „Podworko”.
Wiemy już, że instrukcja w pierwszej linijce mówi kompilatorowi, że dana klasa będzie korzystać z wszystkich klas (i udogodnień) z pakietu „greenfoot”. Linijka 3 do 8 to komentarz, a linijki 16-20 to deklaracja konstruktora.
Pamiętamy: Pierwszy parametr to szerokość |
To co nas najbardziej teraz interesuje to linijka 19. Wpisany tekst „super(600, 400, 1);” oznacza, że nasze podwórko, stworzone zgodnie z zasadami nadklasy czyli konstruktora World, ma wymiary 600x400 kwadratów o boku 1 piksela. Metoda „super” jest zaś odwołaniem do konstruktora nadklasy „World”.
Spróbujmy zmienić te parametry i skonstruować nasz świat z bloków.
Jeden blok będzie miał rozmiary 32x32 piksele. A nasz świat będzie miał szerokość 25 bloków i wysokość 20 bloków.
Po ponownym skompilowaniu kodu, możemy podejrzeć zmiany klikając prawym przyciskiem na tle i wybierając „Inspect”.
Stworzenie Bohatera
Analogicznie do tworzenia Podworka, klikając prawym przyciskiem myszy na „Actor” i wybierając „New subclass...” możemy stworzyć nową klasę. Nazwijmy ją np. „Bohater”. Nasz bohater stanie się klasą dziedziczącą z klasy „Actor”. Pamiętajmy również o przypisaniu naszemu bohaterowi obrazka. Może być to na przykład jeż (zdjęcie po lewej).
Oprócz zaimportowania obrazka czy też wyboru standardowych dostępnych grafik, mamy możliwość edycji obrazka(zdjęcie po prawej). Wystarczy przy wyborze obrazka lub później po kliknięciu „Set image…” wybrać „Edit” które pojawia się w rozwijanym menu w lewym dolnym rogu. Kliknięcie „Edit” przeniesie nas do domyślnego edytora graficznego zainstalowanego na naszym komputerze, gdzie będziemy mogli np. zmniejszyć nasz obrazek.
Obrazek możemy również ustawić za pomocą kodu, który wpiszemy w metodę przygotowującą dla nas świat. |
Jeżeli obrazek ustawimy za pomocą kodu, póki co nasz kod się nie skompiluje, ponieważ nie dodaliśmy obiektu Bohatera do świata.
Dodajmy więc bohatera :)
Pamiętamy: addObject metoda dodaje obiekty do świata. Przyjmuje 3 argumenty:
Współrzędne definiują gdzie pojawi się obiekt. |
Nasz kod powinien wyglądać następująco:
Współrzędne 3, 3 oznaczają, że nasz bohater stoi na 4 bloku od lewej i 4 bloku od góry, ponieważ liczenie zaczyna się od 0.
W metodzie prepare() będziemy dodawać obiekty do naszego świata, tym samym przygotowując świat na uruchomienie programu.
Obiekty do świata możemy również dodać innym sposobem. Ten sposób przydaje się szczególnie gdy chcemy dodać wiele obiektów, lub nie znamy współrzędnych, miejsca w którym chcemy postawić nasz obiekt, a sprawdzanie współrzędnych za pomocą „Inspect” wydaje się czasochłonne.
Co zrobić?
Klikamy prawym przyciskiem na klasę „Bohater”, wybieramy „new Bohater()” i kładziemy stworzony obiekt na naszym tle. Dalej, klikamy prawym przyciskiem na tle i wybieramy „Save the World”.
Następnie podwójne kliknięcie na „Podwórko”, otwieramy edytor i powinniśmy zobaczyć dokładnie taki sam kod jak powyżej, tylko zamiast wpisywać go ręcznie pomógł nam Greenfoot.
Czy wiesz, że? Jeżeli chcemy umieścić w świecie klika takich samych obiektów, nie musimy za każdym razem powtarzać: Klikamy prawym przyciskiem na klasę „Bohater”, wybieramy „new Bohater()” i kładziemy stworzony obiekt na naszym tle. Wystarczy, że przytrzymamy „shift” w trakcie kładzenia obiektu na tle. |
Ożywienie bohatera.
Stworzyliśmy podwórko oraz bohatera, możemy to wszystko już zobaczyć w głównym oknie programu. Spróbujemy teraz „ożywić” naszego jeża. Zacznijmy od kliknięcia przycisku „Run”.
Co się stało? Nic! A dlaczego?
No właśnie, nic się nie zadziało bo komputer nie otrzymał żadnych poleceń co ma robić jeż. Potrzebujemy więc napisać kolejne linijki kodu żeby pomóc naszemu małemu przyjacielowi się poruszać po podwórku.
Co nam potrzeba?
Potrzebna nam będzie metoda wbudowana w Greenfoot:
move(dystans wyrażony liczbą całkowitą) - porusz tego aktora, określony dystans (distance) w kierunku w którym akurat jest zwrócony.
Gdzie ją wpiszemy?
Wpiszemy ją w metodę „act” która jest odpowiedzialna za nieustanne wykonywanie czynności.
Kod powinien wyglądać następująco:
Pamiętamy: metoda public void act() {to, co ma się wykonywać nieustannie} jest odpowiednikiem scratchowej pętli zawsze. |
Klasa(class) i metoda pozwala na ustawianie praw dostępu do składowych przy pomocy słów kluczowych private i public, zwanych specyfikatorami dostępu. Specyfikator public mówi, że klasa jest dostępna publicznie, czyli zewsząd. Specyfikator private (ang. prywatny) sprawia, że dostęp do składowej ma tylko ta klasa. Próba odwołania się do składowej prywatnej spoza klasy spowoduje błąd kompilacji. |
Kompilujmy i uruchamiamy naszą grę. Co zrobił nasz bohater?
Przemieścił się bardzo szybko z lewej strony do prawej. Wartość „move” podana w nawiasie odpowiada za to po ile pól na raz przemieszcza się nasz bohater. Zwiększając liczbę przyśpieszymy przemieszczanie. Ale jak je spowolnić, gdy naszą wartością jest 1 a pól mamy tylko 25?
Są na to dwa sposoby:
1. Greenfoot oferuje nam suwak speed (na dole programu) którym możemy regulować prędkość działania programu. Spróbujcie ją zmniejszyć suwakiem (nasz jeż powinien poruszać się wolniej).
2. Możemy ją też ustawić „na sztywno” za pomocą wbudowanej metody Greenfoot’a:
setSpeed
public static void setSpeed(int speed)
Wartość wpisana w nawias musi być w granicy od 1 do 100. Spróbujmy ustawić wartość na 30.
Działa? Po skompilowaniu, nasza prędkość automatycznie ustawia się na 30 procent. W ten sposób możemy dowolnie regulować prędkość wykonywania naszego programu.
Kolejne pytanie to:
Czy możemy skręcić? Jeszcze nie. Dlaczego? Bo znów brakuje nam kodu, który będzie odpowiedzialny za skręcanie naszego bohatera.
Nasz bohater będzie sterowany klawiaturą, a dokładniej strzałkami: lewo i prawo. Będziemy potrzebowali do tego stworzyć w edytorze Bohatera nową metodę która, po pierwsze będzie sprawdzać czy dany klawisz jest wciśnięty, a po drugie, wykonywać czynność przypisaną klawiszowi. Z pomocą przyjdzie nam instrukcja warunkowa if.
Pamiętamy: |
Metodę możemy nazwać dowolnie, warto jednak żeby nazwa mówiła nam czego będzie dotyczyć.
Proponowana nazwa może brzmieć: sprawdzWcisnietyKlawisz. I tak jeżeli wciśniemy lewo lub prawo chcielibyśmy, żeby nasz bohater obrócił się o np. 90 stopni. Użyjemy tutaj dwóch wbudowanych metod:
- isKeyDown (klasa Greenfoot)
- „turn (klasa Actor)
Pamiętamy: isKeyDown(„nazwaKlawisza”) |
Gotowa metoda powinna wyglądać następująco:
Kompilujemy i sprawdzamy.
I jak działa?
Chyba o czymś zapomnieliśmy.
Udało nam się zdefiniować nową metodę, ale musimy jeszcze ją wywołać?
Kto pamięta jak to zrobić?
Do metody „act” wpisujemy „sprawdzWcisnietyKlawisz();” i już powinno działać :)
Nasz jeż chodzi i skręca, reaguje na strzałki w lewo i prawo :)
Ciekawostka: Czasami może się zdarzyć, że przytrzymanie klawisza spowoduje bardzo szybkie wykonywanie polecenia. Możemy to spowolnić używając metody Greenfoot’a delay np. Greenfoot.delay(1); |
Tworzenie ogrodzenia
Nasz bohater już ładnie spaceruje po podwórku, ale nie chcemy przecież żeby nam uciekł ;) Stwórzmy zatem jakieś ogrodzenie/płot. Klikamy zatem na „Actor” wybieramy „new subclass” i nazywamy naszą nową klasę np. „Ogrodzenie”. Tym razem możemy załadować własną grafikę np. ogrodzenie.png (dostępne do ściągnięcia wraz z tym scenariuszem).
Pod „Actor” pojawiła się nowa klasa „Ogrodzenie”. Oczywiście klikając prawym klawiszem myszki na „Ogrodzenie” i trzymając „shift” możemy utworzyć dowolną liczbę obiektów dookoła naszego podwórka, lecz stworzy to bardzo wiele linijek kodu, a chcielibyśmy tego uniknąć.
Co możemy zrobić?
Pamiętamy, że nasze podwórko zaczyna się w lewym górnym rogu, ma szerokość 25 bloków i wysokość 20 bloków. Pozycja X rośnie kiedy przesuwamy się w prawo, a pozycja Y rośnie kiedy przesuwamy się w dół.
Możemy to zobrazować poniższą tabelką:
0.0 | 1.0 | 2.0 |
0.1 | 1.1 | 2.1 |
0.2 | 1.2 | 2.2 |
Nasz kod w klasie Podworko powinien wyglądać tak jak na powyższym zdjęciu.
Kilka wyjaśnień:
- Nazwaliśmy dwie zmienne x i y i zadeklarowaliśmy im wartość początkową 0 (użyliśmy do tego znaku „=„ ).
int jest skrótem od integer czyli liczba całkowita, bo taką wartość będzie przybierać nasza zmienna.
x++ dodaje 1 do zmiennej x i jest równoznaczne z „x = x+1”.
metody getWidth() i getHeight() są dziedziczone z klasy World, pozwalają nam pobrać wymiary podwórka (szerokość i wysokość).
Pętle mają za zadanie tak długo dodawać obiekty ogrodzenia aż będą równe szerokości i wysokości.
Zastosowaliśmy jeszcze -1 co sprawi, że klocki w rogach się na siebie nie nałożą.
== Interakcja między bohaterem a ogrodzeniem == T
Ogrodzenie już stoi ale jeż jakby się nim nie przejmował. Dalej może chodzić gdzie chce. Czas z tym coś zrobić. Naszym kolejnym zadaniem będzie napisanie kodu odpowiedzialnego za to co spotka jeża kiedy dotknie ogrodzenia. A spotkać go może wiele rzeczy :) Dla celów naszego projektu przyjmijmy, że dotknięcie ogrodzenia zakończy naszą grę, zatrzymując bohatera.
Jak to zrobić?
Oczywiście definiując nową metodę dla bohatera. Nazwijmy ją „ogrodzenie”.
Do stworzenia tej metody przydadzą nam się kolejne 2 gotowe metody:
isTouching(); - znajdziemy ją w klasie Actor, a sprawdza czy ten aktor dotyka inne obiekty danej klasy. Przyjmuje parametr .class - klasę obiektów których szukać. Jeżeli wpiszemy „null” będzie sprawdzać czy dotknęliśmy jakiegokolwiek innego actora .
Greenfoot.stop(); - znajdziemy ją w klasie Greenfoot, pauzuje wykonywanie programu, tym samym może go zakończyć.
Po zdefiniowaniu naszej metody nie zapomnijmy jej wywołać w „Act”.
Dodawanie jedzenia.
Czas nadać jakiś cel naszemu bohaterowi! Jednym z pomysłów może być zjadanie jabłek, które (spadając z jabłoni :) ) będą pojawiać się na podwórku. Przyda się nam zatem nowa podklasa o nazwie np. Jedzenie. Stwórzmy ją i przypiszmy jej jakiś obrazek np. jabłka.
Dodawaniem jedzenia na podwórko zajmiemy się w edytorze klasy Podworko.
Oczywiście musimy stworzyć nową metodę o przykładowej nazwie dodajJedzenie. A w niej użyć addObject. Pamiętamy, że metoda addObject przyjmuje 3 parametry (patrz str. 5). Jednak w tym przypadku chcemy aby nasze jabłko pojawiało się w losowo wybranym miejscu.
Czego potrzebujemy?
I tak znów z pomocą przychodzi nam dokumentacja dołączona do Greenfoota (zaglądamy tam dwukrotnie klikając na World lub Actor). Tym razem użyjemy znalezione w klasie Greenfoot getRandomNumber (górnaGranica) który losuje liczbę całkowitą pomiędzy 0 (włącznie) a liczbą którą określimy w nawiasach (z wyłączeniem jej). Np. Greenfoot.getRandomNumber(5) zwraca liczbę ze zbioru {0,1,2,3,4}.
Żeby nasze jedzenie nie pojawiało się na ogrodzeniu, ani tuż przy nim musimy sprawdzić jaką szerokość ma podwórko. Dzięki getWidth() dowiemy się, że 25, czyli kolumny 0-24. Pomijamy lewy płot (0) i bezpośrednie sąsiedztwo (1) oraz analogicznie prawy płot (24) i jego okolica (23). Mamy więc do dyspozycji całą szerokość z wyłączeniem tych 4 „pasów”. Musimy wylosować jedną z 21 (getWidth()-4) liczb . Jednak z metody getRandomNumber(21) otrzymamy liczby {0,1,2,...,19,20}. Aby otrzymać interesujący nas zakres – od 2 do 22 musimy do wylosowanej liczby dodać 2. Ostatecznie otrzymujemy:
Greenfoot.getRandomNumber( getWidth() - 4 ) + 2,
Greenfoot.getRandomNumber( getHeight() - 4 ) + 2;
Konstruktor metody mógłby wyglądać następująco:
Jednak to tylko połowiczne rozwiązanie, więc będziemy potrzebować czegoś jeszcze.
Dlaczego?
Ponieważ jabłko pojawia się tylko raz, przy uruchomieniu gry.
I co dalej?
Interakcja między bohaterem a jedzeniem
Teraz czas żebyśmy zaprogramowali naszego bohatera na ewentualność spotkania z jabłkiem.
Co powiecie na to?
Jeżeli jeż dotrze do jabłka to je „zje” a na naszym podwórku pojawi się nowe jabłko w losowo wybranym miejscu.
Pamiętamy: |
Czego potrzebujemy i gdzie zdefiniujemy naszą nową metodę?
Wykorzystamy do tego łącząc już znane nam metody:
isTouching
Greenfoot.getRandomNumber
getWorld
getHeight
getWidtht
Oraz nową metodę którą możemy znaleźć w klasie Actor:
setLocation - setLocation(int x, int y) przenosi nasz obiekt do lokalizacji sprecyzowanej za pomocą parametrów x i y.
Wzorem do stworzenia naszej metody może być metoda dodajJedzenie(). A metoda powinna się znaleźć w klasie jedzenie.
Teraz za każdym razem jak nasz bohater dotknie jedzenia, pojawia się ono w innym miejscu.
A jak wygrać tą grę?
Nasz bohater już chodzi, może zjadać jabłka i wpadać na ogrodzenie kończąc/przegrywając tym samym grę. Przydała by się mu teraz możliwość jej wygrania, a ponieważ jego głównym zadaniem jest zjadanie jabłek, to może spróbujemy uzależnić wygraną od ilości zjedzonych jabłek.
Np. Kiedy uda mu się zjeść 10 jabłek to wygrywa a gra się zatrzymuje.
Będziemy potrzebować do tego nowej zmiennej(zadeklarujemy ją w konstruktorze klasy na początku kodu). Do tego nasze jabłka będą musiały być zliczane i przechowywane w stworzonej zmiennej, a do sprawdzenia czy zjedliśmy wystarczającą ilość jabłek użyjemy instrukcji warunkowej if,(to wszystko dodamy do szukajJedzenie).
Gotowy kod powinien wyglądać następująco:
Pamiętamy: == stosujemy przy sprawdzaniu, czy wartości są takie same |
Udało się?
Nasza gra zatrzymuje się po zjedzeniu 10 jabłek. Ale zaraz zaraz, to samo się dzieje jak wpadniemy w ogrodzenie, jak więc rozróżnić czy wygraliśmy czy przegraliśmy?
Jednym z prostszych sposobów jest dodanie dźwięków, np. fanfar na wygraną. I znów z pomocą przyjdzie nam metoda z klasy Greenfoot:
Greenfoot.playSound(„NazwaDźwięku.wav");
Spróbujcie sami. Poszukajcie jakiegoś dźwięku na Internecie lub skorzystajcie z pliku załączonego do scenariusza. Kod z odgrywaniem dźwięku znajdzie się oczywiście w instrukcji warunkowej if.
Ciekawostka: W greenfoot możemy nagrać własny dźwięk, który automatycznie zapisze się w folderze sounds naszego projektu. Aby to zrobić klikamy w górnym menu zakładkę Controls i wybieramy Show Sound Recorder. |
Co dalej? jak możemy rozbudować naszą grę?
Dodać dźwięk w przypadku przegranej.
Dodać przeciwników którzy losowo będą krążyć po podwórku naszego bohatera.
Dodać punkty i licznik pokazujący ich sumę w grze.
Dodać więcej elementów, np. dodatkowe ściany, lub nawet spróbować zrobić labirynt.