Przejdź do treści

Projekt graficzny i quiz

Zakończenie kursu

Gorąco zachęcamy Was do wypełnienia niezbyt długiej ankiety na zakończenie kursu.

Część techniczna

W części technicznej pokażemy, jak w środowisku Code Blocks zainstalować bibliotekę Allegro. Jest to bodaj najprostsza biblioteka służąca do pisania programów graficznych w C++. Na początku należy ściągnąć następujący plik ZIP zawierający bibliotekę w wersji 5.0.10, przeznaczoną do kompilatora MinGW w wersji 4.7.1, który znajduje się w naszym środowisku Code Blocks. Opisany proces instalacji wymaga możliwości umieszczenia plików w poszczególnych folderach instalacji środowiska Code Blocks.

Tak będzie wyglądał nasz pierwszy program. Wyjaśnimy go w części programistycznej lekcji.

#include <allegro5/allegro.h>
using namespace std;

const int SZER = 640;
const int WYS = 480;

int main() {
    al_init();
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard();
    ALLEGRO_KEYBOARD_STATE klawiatura;
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.0));
        al_flip_display();
        al_get_keyboard_state(&klawiatura);
    } while (!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE));
    al_destroy_display(okno);
    return 0;
}

Oto kolejne kroki instalacji:

Zobacz tekst nagrania
  1. Otwieramy plik z archiwum ZIP i kopiujemy folder znajdujący się w środku np. na pulpit.
  2. Odszukujemy, gdzie na komputerze zainstalowaliśmy środowisko Code Blocks. Przechodzimy tam do folderu MinGW (jest to folder z plikami naszego kompilatora C++).
  3. Z folderu include biblioteki kopiujemy wszystkie pliki i foldery do folderu include kompilatora.
  4. Z folderu lib biblioteki kopiujemy pliki liballegro-5.0.10-monolith-mt-debug.a i liballegro-5.0.10-monolith-mt.a do folderu lib kompilatora. (Moglibyśmy skopiować wszystkie pliki, ale nie ma takiej potrzeby.)
  5. Możemy już zamknąć folder CodeBlocks. Włączamy środowisko Code Blocks.
  6. Tworzymy nowy pusty projekt (Plik -> Nowy -> Projekt..., "Empty project"). W ekranie powitalnym klikamy "Next". Wpisujemy tytuł projektu i wybieramy folder, w którym ma zostać zapisany projekt (np. lekcja 10), klikamy Next. W następnym ekranie również klikamy Next, jeśli obie opcje kompilacji są zaznaczone. Projekt utworzony.
  7. Wchodzimy w menu Projekt, Opcje kompilacji... Wybieramy zakładkę "Ustawienia programu linkującego". Po lewej klikamy na Debug i dodajemy bibliotekę o nazwie liballegro-5.0.10-monolith-mt-debug. Potem po lewej klikamy na Release i dodajemy bibliotekę liballegro-5.0.10-monolith-mt.

    Warto wiedzieć, jaki jest sens celów Debug oraz Release. Jeśli chcemy odpluskwiać nasz program lub ogólnie wyszukiwać w nim błędy, właściwym celem kompilacji jest właśnie Debug. Natomiast gdy będziemy kompilowali projekt po to, by działał możliwie efektywnie, np. będziemy chcieli zacząć go jakoś rozpowszechniać, to wtedy należy kompilować go do celu Release. Póki co domyślnie używaliśmy celu Debug.

  8. Czas dodać do projektu plik. Wybieramy Plik -> Nowy -> Pusty plik. Zaznaczamy w okienku, że chcemy go dodać do projektu, oraz zapisujemy go, najlepiej pod nazwą main.cpp. Wklejamy powyższą zawartość pliku. W kolejnym okienku, które się pojawi, po prostu zatwierdzamy plik do obu celów kompilacji.

  9. Zapisujemy projekt (Plik -> Zapisz projekt). To bardzo ważny krok – zapisujemy w nim ustawienia kompilacji projektu oraz przypisanie do niego pliku main.cpp.
  10. Teraz uda nam się już skompilować projekt, jednak jeszcze nie uda się go uruchomić. Aby to było możliwe, musimy skopiować z folderu lib biblioteki Allegro plik liballegro-5.0.10-monolith-mt-debug.dll do folderu bin/Debug projektu. Gdyby projekt był kompilowany także do celu Release, należałoby także skopiować plik liballegro-5.0.10-monolith-mt.dll do folderu bin/Release.
  11. Możemy uruchomić projekt. Wyświetli się okno z żółtym tłem. Aby zamknąć okno, wciskamy klawisz Esc.

Część programistyczna

Na początku rzucimy nieco światła na nasz pierwszy program. Później postaramy się go jakoś sensownie rozwinąć.

Zobacz tekst nagrania

Zanim wejdziemy w technikalia, zacznijmy od ogólnych obserwacji:

  • Aby użyć biblioteki, musieliśmy umieścić odpowiednią instrukcję #include podającą tzw. plik nagłówkowy biblioteki.
  • W programie używamy funkcji, typów danych i stałych zdefiniowanych w bibliotece. Nazwy wszystkich funkcji biblioteki Allegro rozpoczynają się od przedrostka al_, a nazwy typów danych i stałych rozpoczynają się od przedrostka ALLEGRO_ i ponadto są pisane wielkimi literami. Warto przyglądać się dokładniej tym nazwom, gdyż mogą nam one wiele wyjaśnić o naturze danej funkcji czy typu.
  • Wiele funkcji biblioteki Allegro zamiast zmiennych przyjmuje i zwraca wskaźniki na zmienne. Wskaźnik reprezentuje adres miejsca w pamięci komputera, pod którym jest pamiętana dana zmienna. Wskaźników w języku C++ używa się istotnie rzadziej niż w jego poprzedniku, języku C, tak więc nie były nam one wcześniej potrzebne. Aby zadeklarować zmienną typu "wskaźnik na zmienną jakiegoś typu", tzw. zmienną wskaźnikową, w deklaracji przed nazwą zmiennej umieszczamy gwiazdkę (patrz deklaracja zmiennej okno poniżej). Jeśli mamy zmienną jakiegoś typu i chcemy poznać wskaźnik na tą właśnie zmienną, przed nazwą tej zmiennej piszemy "&".
  • Kolory na ekranie określa się jako mieszankę trzech barw podstawowych: czerwonego, zielonego i niebieskiego, tzw. RGB. (W szkołach ze względów historycznych uczy się nieco innego zestaw barw podstawowych.) Każdy inny kolor stanowi mieszankę tych barw w jakimś procencie. W poniższym programie zajmuje się tym funkcja al_map_rgb_f, która w argumentach przyjmuje natężenia barw podstawowych (0.0 - brak, 1.0 - maksymalne natężenie) i zwraca odpowiadający kolor. Przykładowo, nasz żółty kolor to 100% R, 100% G i 0% B. Tak naprawdę każdy z tych współczynników jest później przeliczany na liczbę z zakresu od 0 do 255, ale my dla wygody wybraliśmy reprezentację procentową. Proponujemy Ci pobawić się z tym wierszem programu. Jak myślisz, jak wyświetlić czarne okienko, a jak białe? Jak fioletowe, a jak pomarańczowe?

Pozostałe szczegóły wyjaśniamy w komentarzach w kodzie programu. Wiele z tych elementów to elementy stałe, których będziemy używali niezmienionych we wszystkich programach.

#include <allegro5/allegro.h> // plik nagłówkowy potrzebny do używania biblioteki
using namespace std;

// szerokość i wysokość okna
const int SZER = 640;
const int WYS = 480;

int main() {
    al_init(); // inicjacja biblioteki - niezbędna na początek
    // poniższa funkcja tworzy okno o podanych rozmiarach i zwraca wskaźnik na to okno;
    // przypisaliśmy ten wskaźnik do zmiennej "okno"
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard(); // inicjacja klawiatury - jeśli chcemy jej używać
    ALLEGRO_KEYBOARD_STATE klawiatura; // zmienna reprezentująca stan klawiatury
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.0)); // ustawienie koloru okna - patrz komentarz powyżej
        al_flip_display(); // wyświetlenie wszystkiego, co narysowaliśmy do tej pory, na ekran
        // pobranie bieżącego stanu klawiatury;
        // funkcja przyjmuje wskaźnik na stan klawiatury, więc piszemy "&"
        al_get_keyboard_state(&klawiatura);
    } while (!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE)); // czy wciśnięto klawisz Esc
    al_destroy_display(okno); // funkcja zamykająca okno
    return 0;
}

Czas wprowadzić do naszej aplikacji trochę dynamiki. Narysujemy w oknie kółko i wprawimy je w ruch.

Zobacz tekst nagrania

Do narysowania kółka potrzebny jest dodatkowy moduł biblioteki Allegro o nazwie Primitives, czyli zawierający podstawowe figury geometryczne. Aby go użyć, trzeba załączyć odpowiedni plik nagłówkowy i wywołać funkcję inicjującą ten moduł – patrz program poniżej. W module jest dostępna np. funkcja rysująca wypełnione koło o danym środku, danym promieniu i danym kolorze – patrz poniżej.

Może nas w tym miejscu najść refleksja, skąd właściwie należało wiedzieć o użyciu tego modułu i jakich funkcji on dostarcza. Nie trzeba tego wszystkiego zapamiętywać! My uzyskaliśmy te informacje z oficjalnej dokumentacji biblioteki (po angielsku). W menu po lewej stronie dokumentacji znajduje się najpierw lista podstawowych funkcjonalności biblioteki, a dalej właśnie lista dodatkowych modułów (ang addons). Jak już przejrzymy z grubsza to menu i dowiemy się, że jest taki moduł jak Primitives, to możemy przejść do odpowiedniej strony dokumentacji ( tutaj). Na górze znajdujemy informację, jakie są podstawowe funkcje do uruchomienia modułu. Niżej znajdziemy listę funkcji w module – rysowanie linii, trójkątów, prostokątów, elips, okręgów i kół itp. Teraz wystarczyło wziąć dla siebie odpowiednią funkcję ( tutaj). Inną drogą zdobycia takiej informacji jest wyszukanie w Internecie hasła np. "circle allegro 5.0" – akurat przekierowuje nas to na stronę funkcji rysującej okrąg.

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h> // załączamy moduł primitives
using namespace std;

const int SZER = 640;
const int WYS = 480;

int main() {
    al_init();
    al_init_primitives_addon(); // inicjujemy moduł primitives
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard();
    ALLEGRO_KEYBOARD_STATE klawiatura;
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.6));
        int x = SZER / 2, y = WYS / 2, r = 30;
        al_draw_filled_circle(x, y, r, al_map_rgb_f(0.0, 0.0, 1.0)); // kółko.
        al_flip_display();
        al_get_keyboard_state(&klawiatura);
    } while(!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE));
    al_destroy_display(okno);
}

Kółko narysowane na środku ekranu nie jest nadmiernie ekscytujące. Zmienimy nasz program tak, aby kółko przemieszczało się tam i z powrotem w poziomie, odbijając się od boków okna. Nie będzie nam do tego potrzebna właściwie żadna nowa funkcja biblioteki – wystarczy podstawowa umiejętność programowania. Wprowadzamy zmienną dx określającą kierunek przemieszczania się kółka, początkowo równą 1. Zmieniamy wartość tej zmiennej na przeciwną, gdy tylko kółko dotknie brzegu okna.

No dobrze, żeby kółko nie latało za szybko, dobrze jest do pętli wprowadzić funkcję al_rest, która po prostu oczekuje podaną liczbę sekund.

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
using namespace std;

const int SZER = 640;
const int WYS = 480;

int main() {
    al_init();
    al_init_primitives_addon();
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard();
    ALLEGRO_KEYBOARD_STATE klawiatura;
    int x = SZER / 2, y = WYS / 2, r = 30;
    int dx = 1;
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.6)); // RGB
        al_draw_filled_circle(x, y, r, al_map_rgb_f(0.0, 0.0, 1.0));
        x += dx;
        if (x + r == SZER) dx = -1;
        if (x - r == 0)    dx =  1;
        al_rest(0.001);
        al_flip_display();
        al_get_keyboard_state(&klawiatura);
    } while (!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE));
    al_destroy_display(okno);
    return 0;
}

Teraz już bez problemu zmienimy nasz program, tak by kółko przemieszczało się po całym oknie. Wprowadzamy drugą zmienną dy, działającą tak samo jak zmienna dx. Innymi słowy, ruch pionowy i poziomy kółka są niezależne, a w praktyce wygląda to tak, jakby kółko odbijało się od boków okna zgodnie z zasadą odbicia – jak piłeczka.

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
using namespace std;

const int SZER = 640;
const int WYS = 480;

int main() {
    al_init();
    al_init_primitives_addon();
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard();
    ALLEGRO_KEYBOARD_STATE klawiatura;
    int x = SZER / 2, y = WYS / 2, r = 30;
    int dx = 1, dy = 1;
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.6)); // RGB
        al_draw_filled_circle(x, y, r, al_map_rgb_f(0.0, 0.0, 1.0));
        x += dx; y += dy;
        if (x + r == SZER) dx = -1;
        if (y + r == WYS)  dy = -1;
        if (x - r == 0)    dx =  1;
        if (y - r == 0)    dy =  1;
        al_rest(0.001);
        al_flip_display();
        al_get_keyboard_state(&klawiatura);
    } while(!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE));
    al_destroy_display(okno);
}

Udało nam się już dużo osiągnąć. Na koniec pokażemy, jak można pozwolić użytkownikowi sterować naszą aplikacją.

Zobacz tekst nagrania

Chcielibyśmy, aby użytkownik za pomocą strzałek sterował początkowo nieruchomym kółkiem. Oczywiście nie pozwolimy mu wyjść tym kółkiem poza obręb okna. Tutaj potrzebna nam będzie funkcja al_key_down, której używamy cały czas do sprawdzania, czy użytkownik nie wcisnął klawisza Esc. Szczęśliwie, biblioteka Allegro dostarcza nam intuicyjnie nazywające się stałe oznaczające poszczególne klawisze strzałek.

Trzeba w tym miejscu pamiętać o dwóch rzeczach. Po pierwsze, przed wywołaniem funkcji al_key_down powinniśmy odświeżyć zawartość zmiennej klawiatura, korzystając z funkcji al_get_keyboard_state, która to funkcja pobiera bieżący stan wciśnięć klawiszy. Po drugie, układ współrzędnych okna jest nieco inny niż powszechnie używany w naszych szkołach, gdyż współrzędne \(y\) rosną z góry na dół! (Współrzędne \(x\) rosną normalnie, od lewej do prawej.) Tak więc przyciski góra i dół muszą być obsługiwane na odwrót.

Oto nasz ostateczny program:

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
using namespace std;

const int SZER = 640;
const int WYS = 480;

int main() {
    al_init();
    al_init_primitives_addon();
    ALLEGRO_DISPLAY *okno = al_create_display(SZER, WYS);
    al_install_keyboard();
    ALLEGRO_KEYBOARD_STATE klawiatura;
    int x = SZER / 2, y = WYS / 2, r = 30;
    do {
        al_clear_to_color(al_map_rgb_f(1.0, 1.0, 0.6)); // RGB
        al_draw_filled_circle(x, y, r, al_map_rgb_f(0.0, 0.0, 1.0));
        al_flip_display();
        al_get_keyboard_state(&klawiatura);
        if (al_key_down(&klawiatura, ALLEGRO_KEY_RIGHT) && x + r < SZER) ++x;
        if (al_key_down(&klawiatura, ALLEGRO_KEY_LEFT)  && x - r > 0)    --x;
        if (al_key_down(&klawiatura, ALLEGRO_KEY_DOWN)  && y + r < WYS)  ++y;
        if (al_key_down(&klawiatura, ALLEGRO_KEY_UP)    && y - r > 0)    --y;
        al_rest(0.001);
    } while(!al_key_down(&klawiatura, ALLEGRO_KEY_ESCAPE));
    al_destroy_display(okno);
    return 0;
}

Końcowy program pozwala już nabrać pewnej intuicji co do tego, jak łatwo można za pomocą biblioteki Allegro napisać jakąś prostą aplikację albo grę. Za pomocą dodatkowych linii moglibyśmy teraz bez problemu zapisać rysowanie labiryntu, po którym będzie przemieszczało się kółko. Możemy nawet pokusić się o napisanie prostej wersji Pacmana! Zachęcamy Was do próby napisania jakiejś prostej gry lub aplikacji.

Quiz

W tej lekcji dajemy do rozwiązania pięć zadań podsumowujących materiał kursu. Zachęcamy Cię do rozwiązania wszystkich z nich! Przypominamy, że do zaliczenia tego kursu potrzebne jest rozwiązanie łącznie 18 zadań (nie liczymy zadań z *). UWAGA: Dotyczy edycji kursu z roku 2016.

Zadania

Jakie to działanie?

Król

Sad

Ciąg bitoniczny

Choinka