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?
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 /spoiler
Linux
Linux – skorzystaj z repozytorium (w zależności od dystrybucji – Centrum oprogramowania, Synaptic, 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
Konfiguracja IDE
Windows – Code::Blocks
Tworzymy nowy projekt, może być pusty lub aplikacja konsolowa: Oczywiście wybieramy C++, nazywamy projekt i wybieramy lokalizację dla plików.
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!
Przechodzimy na zakładkę Search directories > Compiler i podajemy tam ścieżkę do podkatalogu include/SDL2 z pobranej paczki.
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.
Linux – Code::Blocks
Tworzymy nowy projekt, może być pusty lub aplikacja konsolowa: Oczywiście wybieramy C++, nazywamy projekt i ustawiamy lokalizację dla plików.
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!
Linux – Qt Creator
Utwórz nowy projekt aplikacji C++ nieużywający Qt:
W widoku plików otwórz plik projektu .pro i na końcu dopisz LIBS += -lSDL2:
Ł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
// Create window SDL_Window *w =SDL_CreateWindow("Hello world!",100,100,640,480, SDL_WINDOW_SHOWN); // Check if window is createdif(w ==nullptr){ std::cout <<"Blad SDL_CreateWindow(): "<<SDL_GetError()<< std::endl;return0;} // Create renderer and check it SDL_Renderer *ren =SDL_CreateRenderer(w,-1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if(ren ==nullptr){ std::cout <<"Blad SDL_CreateRenderer(): "<<SDL_GetError()<< std::endl;return0;}
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()):
SDL_DestroyRenderer(ren);SDL_DestroyWindow(w);
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!
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
Na tą część kursu to tyle. Jak masz jakiekolwiek pytania czy problemy to śmiało pisz komentarz, pomogę jak mogę 🙂
2 komentarz do “Kurs SDL2 – cz. 1”
Nullptr was not declared in this scope. Chyba problem z bibiloteka ??
Nullptr was not declared in this scope. Chyba problem z bibiloteka ??
nullptr to słowo kluczowe C++ od wprowadzone do stanfardu C++11. Ustaw w kompilatorze odpowiednią opcję aby móc skompilować ten program bez błędów