Hinweise zur deutschen Version

Diese Version wurde von Nicolas Hollmann übersetzt. Meine Homepage mit weiteren Tutorials zum Thema Programmierung findest du auf www.entwicklerpages.de. Ich bedanke mich bei MetaCipher, dafür dass ich dieses exzellente Tutorial in die deutsche Sprache übersetzen durfte, damit es im Deutschen ein weiteres Tutorial zur SDL gibt.

Diese Tutorials sollen allen helfen, die einige Erfahrung mit C++ oder einer anderen Programmiersprache haben. Wenn du es schwer hast den Ideen im Zusammenhang mit dem Code zu folgen, lies dir doch bitte zuerst unsere Tutorials zu C++ durch. Es ist nicht wichtig alles über C++ zu wissen, allerdings kann dir alles, was du über diese Sprache weißt später helfen.

In diesem Tutorial werden wir CodeBlocks mit dem gcc bzw. Mingw als unsere IDE benutzen (Macuser können Xcode oder make benutzen). Wenn du willst, kannst du auch eine andere IDE oder einen anderen Compiler benutzen, allerdings könnte es für dich schwerer werden, wenn du noch nicht soviel Erfahrung mit dem Linken von Programmbibliotheken (Libraries) hast. Wenn du CodeBlocks benutzen willst, kannst du es dir auf http://www.codeblocks.org/ kostenlos runterladen (Einer der Downloads beinhaltet das Mingw Packet). Wir empfehlen dir, eine stabile Version (binary release) zu benutzen, es sei denn du möchtest ein wenig mehr Zeit investieren um einen Nightly Build zu benutzen (Versionen, die noch einige Bugs haben).

Diese Tutorials gehen vorallem um die SDL (Simple DirectMedia Layer), eine 2D Cross-Platform Grafikbibliothek. Mit dieser Bibliothek können wir fantastische Grafiken auf den Bildschirm zeichnen und alle lustigen Sachen machen um ein eigenes Spiel zu schreiben. Du musst diese Bibliothek auf http://www.libsdl.org/ herunterladen. Achte darauf die Mingw32 tar-Datei unter "Development Libraries" und die zip-Datei unter "Runtime Libraries/Win32" herunterzuladen. Wenn du Visual Studio benutzt, musst du anstelle der Mingw32 Datei die Visual C++ Datei unter "Development Libraries" herunterladen. Sobald du die Datein heruntergeladen hast, empfehlen wir dir die .dll Datei, die du in der .zip findest, in deinen system32 Ordner zu verschieben. Auf diese Weise wird die DLL-Datei immer gefunden wenn wir eine SDL Anwendung schreiben.

Öffne jetzt die tar-Datei (die, die sich unter "Development Libraries" befand) und dekomprimiere den Inhalt in einen Ordner (z.B. C:\SDL). Jetzt starte CodeBlocks damit wir ein paar Einstellungen ändern können. Klicke auf "Settings" in der Menüleiste und dann auf den "Search Directories" Tab. Du must C:\SDL\include im "Compiler" Tab und C:\SDL\lib im "Linker" Tab hinzufüge (ändere C:\SDL in den Pfad wo du die Dateien dekomprimiert hast). Wenn du fertig bist klick auf "Okay".

Wichtiger Hinweis: Ich benutze unten (und in allen anderen Tutorials) #include <SDL.h>, wobei es eigentlich #include <SDL/SDL.h> sein sollte. Ich habe alle meine Header-Datein einen Ordner nach oben verschoben. Wenn du also meinem Code exakt folgen willst, musst du alle deine SDL-Header-Datein einen Ordner nach oben verschieben, sodass sie im "include" Ordner und nicht im "SDL" Ordner sind. Entschuldigung für die Verwirrung die das verursacht hat.

Starte ein neues "blank" Projekt und nenne es wie du willst. Klicke auf "Project" in der Menüleiste und dann auf "Properties". In dem neuen Fenster klickst du rechts unten auf "Project's build options...". Dann gehst du in den "Linker Settings" Tab und fügst Folgendes zu der Liste unter "Link Libraries" hinzu:

mingw32
SDLmain
SDL

Die Reihenfolge ist wichtig, also benutze die Pfeile um die Liste neu anzuordnen, bis sie so aussieht wie die oben. Falls du wissen willst, was wir hier machen, wir "linken" einfach Codes zusammen, oder in anderen Worten, wir nehmen den Code der SDL und fügen ihn mit unserem zusammen. Das machen wir indem wir die Include-Datein zum Kompilieren (d.h. übersetzen) und die lib-Datein zum Linken angeben. Sobald wir das gemacht haben wird unser Code zusammen mit der SDL zu einer Anwendung gebunden.

Klicke zweimal auf OK und dann kann es endlich losgehen!

Erstelle zwei neue Datein und nenne sie CApp.h und CApp.cpp; sie werden den Kern für unsere Anwendung darstellen. Öffne nun CApp.h und füge folgenden Code ein.

#ifndef _CAPP_H_
    #define _CAPP_H_
 
#include <SDL.h>
 
class CApp {
 
    public:
 
        CApp();
 
        int OnExecute();
 
};
 
#endif

Jetzt öffne CApp.cpp und füge den Code hinzu:

#include "CApp.h"
 
CApp::CApp() {
}
 
int CApp::OnExecute() {
    return 0;
}
 
int main(int argc, char* argv[]) {
    CApp theApp;
 
    return theApp.OnExecute();
}

Die CApp Klasse ist sozusagen die Bühne für unsere gesammte Anwendung. Lass uns ein wenig abschweifen, damit wir dir erklären können, wie Spiele für gewöhnlich aufgebaut sind. Die meisten Spiele bestehen aus 5 Funktionen, die spezielle Aufgaben in einem Spiel abhandeln. Diese Funktionen sind in der Regel:

Initialize

Diese Funktion lädt (für gewöhnlich beim Spielbeginn) alle Resourcen wie Texturen, Maps, NPCs oder Ähnliches.

Event

Diese Funktion reagiert auf Eingabe Ereignisse z.B. von der Maus, Tastatur, Joystick oder anderen Geräten.

Loop

Diese Funktion verarbeitet Datenänderungen wie z.B. dass sich ein NPC über den Bildschirm bewegt oder dass die Lebensanzeige immer schwächer wird.

Render

Diese Funktion sorgt dafür dass alles auf dem Bildschirm angezeigt wird (Dieser Vorgang wird Rendern gennant). Sie manipuliert aber KEINE Daten.

Cleanup

Diese Funktion entlädt alle belegten Ressourcen und sorgt dafür dass sich das Spiel ordnungsgemäß beendet.

Es ist wichtig zu verstehen, dass ein Spiel aus einer gigantischen Schleife besteht. In dieser Schleife werden Events behandelt, Daten geupdated und Bilder gerendert. Daher könnte eine ganz einfache Grundstruktur so aussehen:

Initialize();
 
while(true) {
    Events();
    Loop();
    Render();
}
 
Cleanup();

In jedem Schleifendurchgang machen wir etwas mit den Daten und Rendern das entsprechende Bild. Events sind zusätzliche Mittel für den Spieler um Daten zu manipulieren. Sogesehen brächte man in einem Spiel keine Events, allerdings könnte der Spieler dann auch rein garnichts im Spiel bewirken (Zum Beispiel die Spielfigur nach rechts bewegen).

Lasst uns das mit einem Beispiel erläutern. Sagen wir, wir hätten einen Ritter, der Held des Spiels. Alles was wir wollen, ist ihn durch die Gegend zu bewegen. Wenn ich den Pfeil nach links klicke, soll er sich auch nach links bewegen. Wir müssen jetzt herausfinden, wie wir das in einer Schleife machen können. Erstens wissen wir, dass wir prüfen wollen, ob es ein Event gab (ein Tastaturevent). Da wir wissen das Events da sind um Daten zu manipulieren, wissen wir auch, dass wir Variablen brauchen dessen Werte wir manipulieren können. Mithilfe dieser Variablen können wir dann bestimmen, wo unser Ritter auf dem Bildschirm dargestellt wird.

if(Key == LEFT) X--;
if(Key == RIGHT) X++;
if(Key == UP) Y--;
if(Key == DOWN) Y++;

//... irgendwo anders in unserem Code ...
 
RenderImage(KnightImage, X, Y);

Das funktioniert, da wir in jedem Schleifendurchgang prüfen ob Key gleich LEFT, RIGHT, UP oder DOWN ist, und wenn ja eine Variable vergrößern bzw. verkleinern. Wenn unser Spiel mit 30 FPS (frames per second = Bilder in der Sekunde) läuft und wir auf den linken Pfeil drücken, würde unser Ritter sich 30 Pixel in der Sekunde nach links bewegen. Wenn du die Game Loop jetzt noch nicht verstehst, wirst du es schon bald tun. Spiele brauchen sie um richtig zu funktionieren.

Zurück bei unserem Konzept mit den 5 Funktionen können wir jetzt 5 Datein zu unserem Projekt hinzufügen.

CApp_OnInit.cpp CApp_OnEvent.cpp CApp_OnLoop.cpp CApp_OnRender.cpp CApp_OnCleanup.cpp

Danach geh zurück zur CApp.h und füge folgende Funktionen und Variablen hinzu:

#ifndef _CAPP_H_
    #define _CAPP_H_
 
#include <SDL.h>
 
class CApp {
    private:
        bool    Running;
 
    public:
        CApp();
 
        int OnExecute();
 
    public:
 
        bool OnInit();
 
        void OnEvent(SDL_Event* Event);
 
        void OnLoop();
 
        void OnRender();
 
        void OnCleanup();
};
 
#endif

Danach öffnest du alle der 5 neuen Datein und fügst die entsprechende Funktion hinzu:

#include "CApp.h"
 
bool CApp::OnInit() {
    return true;
}
#include "CApp.h"
 
void CApp::OnEvent(SDL_Event* Event) {
}
#include "CApp.h"
 
void CApp::OnLoop() {
}
#include "CApp.h"
 
void CApp::OnRender() {
}
#include "CApp.h"
 
void CApp::OnCleanup() {
}

Jetzt fügen wir in unserer CApp.cpp alle Funktionen zusammen:

#include "CApp.h"
 
CApp::CApp() {
    Running = true;
}
 
int CApp::OnExecute() {
    if(OnInit() == false) {
        return -1;
    }
 
    SDL_Event Event;
 
    while(Running) {
        while(SDL_PollEvent(&Event)) {
            OnEvent(&Event);
        }
 
        OnLoop();
        OnRender();
    }
 
    OnCleanup();
 
    return 0;
}
 
int main(int argc, char* argv[]) {
    CApp theApp;
 
    return theApp.OnExecute();
}

Du hast vermutlich die neuen Variablen bemerkt, allerdings sehen wir uns zuerst an, was überhaupt passiert. Als erstes versuchen wir unser Spiel zu initialisieren. Wenn ein Fehler auftritt, geben wir -1 zurück, was dann das Programm beendet. Wenn alles richtig verlaufen ist, starten wir die Gameloop. Innerhalb der Gameloop benutzen wir SDL_PollEvent um zu prüfen ob Events geworfen worden sind, die wir dann einzeln an OnEvent weiterleiten. Wenn wir mit allen Events fertig sind, rufen wir OnLoop auf, wo die Daten verarbeitet werden. Zuletzt rendern wir unser Spiel. Den Teil wiederholen wir ständig. Verlässt der Benutzer das Spiel, rufen wir OnCleanup auf, wo alle belegten Ressourcen wieder freigegeben werden. So einfach ist das.

Jetzt schauen wir uns SDL_Event und SDL_PollEvent an. SDL_Event ist eine Struktur, die Informationen über einzelne Events speichert. SDL_PollEvent ist eine Funktion, die alle Events aus einer Art Warteschlange holt. Diese Warteschlange kann jede beliebige Anzahl von Events beinhalten, weswegen wir eine Schleife brauchen um an alle Events zu kommen. Zum Beispiel drückt der Benutzer die A Taste und bewegt gleichzeitig die Maus während der OnRender Funktion. Die SDL erkennt das und packt zwei Events in die Warteschlange, eins für den Tastendruck und eins für die Mausbewegung. Mithilfe von SDL_PollEvent können wir uns die Events holen und an OnEvent witerleiten, wo sie dann entsprechend behandelt werden. Sobald keine Evnts mehr in der Warteschlange sind, gibt SDL_PollEvent false zurück und beendet damit die Event-Schleife.

Die andere neue Variable, Running, ist unser Ausgang aus der Gameloop. Wenn wir sie auf false setzen, beendet sie die Gameloop und damit das ganze Programm. Zum Beispiel könnten wir Running auf false setzen wenn der Benutzer Escape drückt und somit das Spiel beenden.

Du solltest jetzt in der Lage sein die Codes ohne Probleme kompilieren zu können, allerdings wirst du bemerken, dass du das Programm nicht beenden kannst. Du wirst vermutlich den Task-Manager benutzen müssen um das Programm zu stoppen.

Jetzt, da alles eingerichtet ist, erstellen wir das Fenster in dem unser Spiel dargestellt wird. Öffne CApp.h und füg eine SDL Oberfläche (engl. SDL surface) zum Code hinzu:

#ifndef _CAPP_H_
    #define _CAPP_H_
 
#include <SDL.h>
 
class CApp {
    private:
        bool            Running;
 
        SDL_Surface*    Surf_Display;
 
    public:
        CApp();
 
        int OnExecute();
 
    public:
        bool OnInit();
 
        void OnEvent(SDL_Event* Event);
 
        void OnLoop();
 
        void OnRender();
 
        void OnCleanup();
};
 
#endif

Ich denke jetzt wäre der richtige Zeitpunkt zu erklären was eine SDL Oberfläche ist. Eine SDL Oberfläche ist etwas das dargestellt werden kann. Stell dir vor, wir hätten ein weißes blatt Papier, einen Stift und ein paar Aufkleber. Dieses Blatt Papier könnten wir "darstellungs Oberfläche" (engl. "display surface") nennen. Wir können darauf zeichnen oder Aufkleber platzieren. Die Aufkleber, die wir haben, sind auch Oberflächen; wir können auf ihnen zeichnen oder nochmehr Aufkleber auf sie kleben. Somit ist Surf_Display unser "Blatt Papier" auf dem wir alles zeichnen können.

Ok, öffne die Datei mit CApp_OnInit damit wir diese Oberfläche tatsächlich erstellen können.

#include "CApp.h"
 
bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }
 
    if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
        return false;
    }
 
    return true;
}

Das Erste, was wir tun müssen, ist die SDL selbst zu starten, damit wir auf ihre Funktionen zugreifen können. Wir sagen der SDL, dass sie alles, was sie hat, initialisieren soll. Es gibt noch andere Parameter, allerdings ist es im Moment noch nicht wichtig, diese zu kennen und zu verstehen. Die nächste Funktion die wir benutzen ist SDL_SetVideoMode. Diese Funktion erstellt unser Fenster und unsere Oberfläche. Sie nimmt 4 Parameter: Die Breite des Fensters, die Höhe des Fensters, die (Bit-)Auflösung des Fensters (sollten 16 oder 32 sein) und als letztes Darstellungs-Flags. Es gibt einige Flags, allerdings reichen die Oben jetzt erstmal aus. Das erste Flag sagt der SDL, dass Hardware-Speicher zur Speicherung unserer Bilder und Ähnlichem benutzt werden soll und das zweite Flag sagt, dass "double buffering" verwendet werden soll (was wichtig ist, wenn das Bild später nicht flackern soll). Ein anderes Flag, das jetzt schon für dich interessant sein könnte, ist SDL_FULLSCREEN was den Fullscreen Modus aktiviert.

Jetzt, da unser Display initialisiert ist, lasst uns ein wenig aufräumen um sicher zu stellen, dass alles reibungslos funktioniert. Öffne CApp_OnCleanup.cpp und füge Folgendes ein:

#include "CApp.h"
 
void CApp::OnCleanup() {
    SDL_Quit();
}

Wir beenden einfach die SDL. Du solltest dir merken, dass du in dieser Funktion andere Oberflächen und Ressourcen bereinigen solltest. Damit wird dein Code später übersichtlicher.

Um den Code sauber zu halten, setzen wir den Surf_Display Zeiger im Konstruktor von CApp auf NULL. Öffne CApp.cpp unf füge Folgendes hinzu:

CApp::CApp() {
    Surf_Display = NULL;
 
    Running = true;
}

Versuche den Code zu kompilieren und teste ob es funktioniert. Du solltest ein schönes, leeres Fenster haben. Du wirst bemerken, dass du es immer noch nicht schließen kannst, weswegen du wieder den Task-Manager benutzen musst.

Jetzt wo wir ein fertiges Fenster haben, brauchen wir noch einen Weg um es zu schließen. Öffne deine CApp_OnEvent.cpp und füge Folgendes ein:

#include "CApp.h"
 
void CApp::OnEvent(SDL_Event* Event) {
    if(Event->type == SDL_QUIT) {
        Running = false;
    }
}

Die SDL Event-Struktur ist in mehrere Typen aufgesplittert. Der Typ eines Events kann soziemlich alles sein, von Tastendrückern bis zu Mausbewegungen. Wir prüfen hier einfach, um welches Event es sich handelt. Denn Typ, den wir oben suchen, ist die Anfrage das Fenster zu schließen (d.h. wenn der Benutzer den X Button klickt). Wenn dieses Event eintritt, setzen wir einfach Running auf false, damit sich das Programm beendet. In einem späteren Tutorial dieser Reihe schauen wir uns Events ein wenig genauer an.

jetzt solltest du alles funktionsfähig eingerichtet haben, sodass du eine gute Grundlage für spätere Arbeiten hast. Es wäre eine gute Idee, dieses Projekt in eine "SDL-Vorlage" für CodeBlocks umzuwandeln. Ich werde nicht darauf eingehen, aber Google doch danach.

Wenn du schon eine gute Idee hast, was du mit diesen Codes machen willst (oder einfach mehr über die SDL lernen willst), schau dir doch das nächste Tutorial in dieser Reihe an und lerne mehr über SDL Oberflächen.