Kurs SDL2 – cz. 1

Biblioteka SDL2 uchodzi za jedną z najlepszych do tworzenia prostych gier 2D w C++, jednocześnie jest nieskomplikowana i nie ma dużych zależności. Wydaje się że jest to idealny wybór dla początkującego gamemakera chcącego napisać lekką grę w C++. W tym kursie poprowadzę Cię od podstaw do własnej, prostej gry. To co, zaczynamy?

SDL2 - Hello, world!

Hello, world!

 Instalacja biblioteki i plików nagłówkowych

To, w jaki sposób będziemy instalować SDL2 zależy od naszego środowiska i systemu operacyjnego:

Windows

Windows – z oficjalnej strony SDL2 ściągamy odpowiednie Runtime binaries czyli 32 lub 64 bitowe. Ściągamy również pliki nagłówkowe Development Libraries w wersji odpowiedniej dla naszego kompilatora. Nie wiesz które wybrać? Jeżeli piszesz w Visual Studio to wybierz wersję VC, w przeciwnym wypadku mingw (chyba że używasz kompilatora od Visuala pod innym IDE, ale wtedy chyba wiesz co robisz 😉 )
Pobrane pliki wypakuj z archiwów do jednego folderu w wygodnej lokalizacji, na przykład C:/SDL2-2.0.0

[collapse]
Linux

Linux – skorzystaj z repozytorium (w zależności od dystrybucji – Centrum oprogramowaniaSynaptic, albo i apt-get lub dowolne inne narzędzie do pobierania i instalacji pakietów) i zainstaluj pakiety libsdl2-2.0-0 oraz libsdl2-dev
W taki sam sposób będziesz instalować inne biblioteki wymagane w kolejnych częściach kursu, np. libsdl2-image-2.0-0 wraz z libsdl2-image-dev.

SDL2 - instalacja w Linux Mint

SDL2 – instalacja w Linux Mint

[collapse]

 Konfiguracja IDE

Windows - Code::Blocks

  1. Tworzymy nowy projekt, może być pusty lub aplikacja konsolowa:
    Oczywiście wybieramy C++, nazywamy projekt i wybieramy lokalizację dla plików.
  2. Z menu wybieramy Project, a następnie Build options i przechodzimy na kartę Linker Settings. Naciskamy przycisk Add, wpisujemy nazwę biblioteki i zatwierdzamy – należy ten krok powtórzyć dla każdej biblioteki na liście. Zwróć uwagę żeby po lewej stronie był zaznaczony projekt, a nie któraś z opcji Debug czy Release!
  3. Przechodzimy na zakładkę Search directoriesCompiler i podajemy tam ścieżkę do podkatalogu include/SDL2 z pobranej paczki.
  4. Na pod-zakładce Linker wykonujemy to samo działanie, dodając folder lib z paczki.

Uwaga: podane ścieżki są przykładowe, u siebie możesz mieć bibliotekę SDL2 w dowolnym miejscu. Ważne jest to, żeby ścieżki dodane na powyższych zakładkach prowadziły do odpowiedniego miejsca.

[collapse]
Linux - Code::Blocks

  1. Tworzymy nowy projekt, może być pusty lub aplikacja konsolowa:
    Oczywiście wybieramy C++, nazywamy projekt i ustawiamy lokalizację dla plików.
  2. Z menu wybieramy Project, a następnie Build options i przechodzimy na kartę Linker Settings. Naciskamy przycisk Add, wpisujemy SDL2 i zatwierdzamy. Zwróć uwagę żeby po lewej stronie był zaznaczony projekt, a nie któraś z opcji Debug czy Release!

[collapse]
Linux - Qt Creator

  1. Utwórz nowy projekt aplikacji C++ nieużywający Qt:
  2. W widoku plików otwórz plik projektu .pro i na końcu dopisz LIBS += -lSDL2:

[collapse]

Ładowanie SDL2

Skoro mamy skonfigurowane IDE wraz z bibliotekami to czas sprawdzić czy wszystko działa poprawnie. Pierwszy kod który powinien się skompilować i uruchomić:

W systemie Windows należy do katalogu z plikiem binarnym („wyjście” kompilatora) wrzucić pliki DLL pobrane ze strony SDL2

Wierzę że powyższy kod uruchomił się Wam bez problemu. Co on właściwie robi? Całkiem dużo, bo inicjalizuje cały subsystem SDL2. Myślę że nie ma co się rozdrabniać i wybierać subsystemów, zatem używamy SDL_INIT_EVERYTHING żeby mieć wszystko załadowane. W razie błędu funkcja zwróci wartość różną od zera – wtedy wystarczy wypisać błąd uzyskany z funkcji SDL_GetError() na wybrane wyjście, u nas jest to konsola. W razie błędu nie chcemy również kontynuować wykonania, stąd return. Na sam koniec kodu wywołujemy SDL_Quit() w celu opuszczenia systemu SDL2.

Tworzenie okienka

Powyższy kod utworzy okienko (linia 2), sprawdzi czy się utworzyło poprawnie (5-8), utworzy renderer (11) i również go sprawdzi (12-15). Nie należy zapomnieć o posprzątaniu obiektów na koniec (przed linijką z SDL_Quit()):

Ale wytłumaczmy kod. W linii 2 tworzymy wskaźnik typu SDL_Window* – obiekt tego typu zostanie zwrócony przez funkcję SDL_CreateWindow. Jej argumenty to po kolei:

  • "Hello world!" – tytuł okienka
  • 100, 100 – pozycja okienka (X,Y); możesz podać zamiast wartości liczbowych stałą SDL_WINDOWPOS_CENTERED w celu wycentrowania okienka na środku ekranu
  • 640, 480 – wielkość okienka (szerokość, wysokość)
  • SDL_WINDOW_SHOWN – flaga; pełna lista tutaj, my na razie używamy tylko tej jednej

Jeżeli obiekt będzie pusty – nullptr (C++11, włącz tą opcję w kompilatorze – przyda się też później) – to przerywamy program ponieważ bez okna nie chcemy nic robić. Na drugim listingu jest pokazana funkcja SDL_DestroyWindow która powoduje… zamknięcie okienka 😉 i zwolnienie pamięci.

Oprócz samego okna potrzebujemy też renderera, czyli „urządzenia” które będzie odpowiadać za rysowanie. To właśnie tego obiektu oczekują wszystkie funkcje rysujące po ekranie. Tak samo jak poprzednio otrzymujemy obiekt SDL_Renderer*, natomiast argumenty funkcji wyglądają tak:

  • w – wskaźnik na obiekt okienka SDL_Window
  • -1 – indeks sterownika, na dłuższy czas zostawiamy tą opcję
  • flagi:
    • SDL_RENDERER_ACCELERATED – włącza akcelerację sprzętową dla renderera, bez tej flagi pracujemy na CPU
    • SDL_RENDERER_PRESENTVSYNC – wymuszamy vsync czyli synchronizację pionową, ilość odświeżeń okna na sekundę będzie zgodna z odświeżaniem monitora (zwykle 60Hz => 60fps)

Tak samo jak poprzednio, zwalniamy renderer za pomocą SDL_DestroyRenderer.

Uwaga: przy zwalnianiu zasobów należy zachować odpowiednią kolejność, tj. odwrotną do tworzenia obiektów.

Ładowanie bitmapy

Na razie nie będziemy zajmować się ładowaniem innych formatów niż BMP. Kod ładujący jest podobny do poprzednich – naturalnie, ścieżka do pliku jest względna:

Wybierzmy na razie bitmapę o takim samym rozmiarze co rozmiar okna, na przykład tą.

Oprócz samego załadowania do pamięci, potrzebujemy naszą bitmapę – w obiekcie typu SDL_Surface – przekonwertować do tekstury, którą będziemy mogli wyświetlić na ekranie.

Zwróć uwagę że w trzeciej linijce (tego listingu) od razu zwalniamy pamięć po obiekcie SDL_Surface tworzonym w poprzednim fragmencie kodu. Jest on nam już niepotrzebny ponieważ mamy teksturę SDL_Texture po konwersji.

Nie zapomnij o usunięciu tekstury za pomocą SDL_DestroyTexture!

Wyświetlanie obrazka i opóźnienie zamknięcia okna

Te cztery linijki powinny być po załadowaniu tekstury, a przed wyczyszczeniem pamięci. Tłumaczenie po kolei co robią:

  • SDL_RenderClear czyści bufor renderera. Żeby zrozumieć jak to działa powinieneś wiedzieć o jednej ważnej rzeczy: karta graficzna ma dwa bufory, które są ciągle zamieniane. Program rysuje na nieaktywnym buforze, wyświetlany jest aktywny. Ekran (renderer – nieaktywny bufor) jest czyszczony przez tą funkcję na ustawiony kolor, domyślnie czarny (0,0,0).
  • SDL_RenderCopy kopiuje teksturę na renderer. Dwa parametry NULL służą do określenia pozycji i rozmiaru tekstury, na razie tego nie używamy (mamy teksturę wielkości okna)
  • SDL_RenderPresent zamienia bufory, czyli wyświetla ten po którym rysowaliśmy.
  • SDL_Delay opóźnia działanie programu o 3 sekundy, inaczej okienko by się samo zamknęło od razu po wyświetleniu

Powyższe kroki mogą być dla Ciebie niejasne ale spokojnie, w kolejnych częściach kursu zrozumiesz działanie buforów i tych funkcji. Jeżeli poprawnie posklejałeś kod, powinieneś w tej chwili otrzymać program dający rezultat widoczny na zrzucie ekranu na samym początku wpisu.

Pełny kod źródłowy możesz pobrać z repozytorium tutaj.

Najczęstsze błędy i ich rozwiązania

  • Program się nie kompiluje, „undefined function …”
    Sprawdź ścieżki do plików nagłówkowych (include) w swoim IDE
  • Program się nie kompiluje, „undefined reference to …”
    Sprawdź ścieżki do bibliotek w swoim IDE
  • Program się nie uruchamia, brakuje pliku DLL – SDL2.dll
    Prawdopodobnie pominąłeś informację w tekście – pliki DLL pobrane z pierwszej paczki muszą być w katalogu ze skompilowanym programem
  • Program się nie uruchamia, brakuje pliku DLL – inny
    Skopiuj plik z katalogu system32 – powinien tam być
  • Okienko od razu się zamyka bez komunikatu błędu
    Brakuje polecenia SDL_Delay, dodaj je i ustaw odpowiedni czas w milisekundach
  • SDL nie znajduje obrazka, pomimo że mam go w poprawnej ścieżce
    Sprawdź w IDE ścieżkę katalogu roboczego programu lub uruchamiaj z eksploratora plików zamiast z IDE
  • Nie mogę zamknąć okienka przed upływem czasu
    To żaden błąd 😉 Nie ma pętli zdarzeń, SDL ignoruje przycisk zamknięcia okna

[collapse]

Na tą część kursu to tyle. Jak masz jakiekolwiek pytania czy problemy to śmiało pisz komentarz, pomogę jak mogę 🙂