Animation
Spätestens jetzt werden Sie wohl auch das Bedürfnis haben, Bilder, die Sie anzeigen können, auf dem Bildschirm zu bewegen (natürlich in 2D).Das Prinzip ist im Endeffekt immer dasselbe - auch wenn es bei steigendem Umfang schon recht komplex werden kann. Zuerst benötigen Sie immer eine Spielfläche, ein einfaches Koordinatensystem, worauf Sie anschließend die Grafik bewegen können. Des Weiteren wird ein Koordinatensystem benötigt, welches die x- und y-Koordinaten der Grafik beinhaltet, wo sich diese im Augenblick befindet.
Die Koordinaten der linken oberen Ecke sind somit (0/0) und der rechten unteren Ecke (639/479) (bei Verwendung einer Auflösung von 640 x 480 Bildpunkten). Alles, was also vom Ausgangspunkt (0/0) rechts liegt, erhöht den x-Wert und alles, was darunterliegt, erhöht den y-Wert.
Da Sie im folgenden Beispiel einen Ball verwenden, sprechen wir nun auch konkret von einem Ball als Grafik. Neben der aktuellen Position des Balls benötigen Sie auch die Position, in welche Richtung und in welcher Geschwindigkeit sich der Ball beim nächsten Bildwechsel bewegt. In einer Struktur verpackt sieht es in etwa so aus:
typedef struct ball {
int x, y; /* Aktuelle Position */
int dx, dy; /* Bewegung für nächste Position */
} ball_t;
Des Weiteren benötigen Sie eine Funktion, um den Wert des Balls mit einem Startwert zu initialisieren. Dann braucht es eigentlich nur noch eine Funktion, die den Ball auf dem Bildschirm zeichnet und eine, welche die nächste neue Position des Balls berechnet. Alles in allem sind dies eigentlich nur Funktionen, die relativ wenig mit SDL zu tun haben, und Ihre Gedanken recht strapazieren können.
Hinweis
Mir sei bitte verziehen, dass ich den Teil SDL mehr SDL-lastig halte und weniger tief in die Spieleprogrammierung und deren Algorithmen eingehe. Die Beispiele, die hier allerdings verwendet werden, stellen keine allzu großen Anforderungen an den geübten C-Programmierer dar.
Somit stellt eine Animation zunächst nichts anderes da, als ein dauerndes Neuzeichnen des Bildschirms. Ziel bei der Programmierung von Animationen sollte es immer sein, den Bildschirm möglichst schnell und häufig neu zu zeichnen, ohne dass das Auge davon etwas mitkriegt. Dass dies nicht immer einfach ist, dürfte klar sein. Jeder neu gezeichnete Bildschirm wird im Augenblick als Frame bezeichnet, und die Anzahl der Bilder, die in einer bestimmten Zeiteinheit neu gezeichnet werden, wird als Framerate bezeichnet. Und, je höher diese Framerate ist, umso weniger bekommt das Auge vom Bildwechsel mit.
Sie könnten mit den jetzigen Kenntnissen auch schon eine Animation mit einer Grafik erstellen. Allerdings würde es bei rechenintensiveren Aufgaben garantiert zu schlechten Framerates kommen. Daher sollten hier noch zwei Möglichkeiten erwähnt werden, um die Framerate Ihrer Anwendung zu erhöhen.
Wie Sie ein Surface (Framebuffer) auf dem Bildschirm darstellen können, wissen Sie ja bereits. Wenn Sie hierbei allerdings eine enorme Menge von Grafiken darstellen wollen und bei einer Animation immer wieder neu in den Framebuffer zeichnen und wiederholen müssen, kann es je nach Hardware passieren, dass das Zeichnen ins Ruckeln gerät. Den Effekt kennen Sie sicherlich bei neueren Spielen mit etwas älterer Hardware, wenn enorm viele Grafiken und Effekte auf einmal am Bildschirm dargestellt werden sollen. Um dieses Ruckeln der einzelnen Frames zu vermeiden (bzw. zu verringern), verwendet man die doppelte Frame-Pufferung (Double-Buffering). Wie der Name schon sagt, stehen Ihnen hiermit zwei Zeichenebenen zur Verfügung. Dabei befindet sich in einem Frame das Bild, welches gerade auf dem Bildschirm dargestellt wird, und im anderen Frame die nächste Zeichnung, die bereits aufgebaut wird. Ein Mini-Stack der Grafikkarte, wenn Sie so wollen. Wenn der Bildaufbau fertig ist, müssen die beiden Bilder vertauscht werden. Dann findet sich das zuvor noch in der Grafikkarte vorhandene Frame auf dem Bildschirm, und das alte Frame wird wieder in der Grafikkarte zum Neuzeichnen das nächsten Frames verwendet.
Hinweis
Einen Haken hat die Sache allerdings, die Grafikkarte muss das Tauschen der beiden Frames - auch als Flipping bekannt - unterstützen.
Den doppelten Framebuffer können Sie ganz einfach mit der Funktion SDL_SetVideoMode() und dem Flag SDL_DOUBLEBUF aktivieren. Anschließend müssen Sie noch zum Tauschen der beiden Frames die Funktion SDL_Flip() verwenden.
int SDL_Flip(SDL_Surface *screen);
Unterstützt die Hardware nun die doppelte Frame-Pufferung, führt diese Funktion einen Flip aus und kehrt zurück. Unterstützt Ihre Hardware keinen doppelten Framebuffer, ist der Aufruf dieser Funktion gleichwertig mit einem Aufruf von SDL_UpdateRect(screen, 0, 0, 0, 0). Bei Erfolg gibt SDL_Flip() 0, sonst bei einem Fehler -1 zurück. Ansonsten müssen Sie sich um nichts zu kümmern, da SDL Ihnen den Verwaltungsaufwand des doppelten Framebuffers abnimmt. Sie verwenden also einfach die Funktion SDL_Flip() anstatt zuvor SDL_UpdateRect(). Mehr ist dazu nicht nötig.
Eine zweite erhebliche Geschwindigkeitsbremse stellt das dauernde Konvertieren zwischen den Pixeln des Bildformates (bspw. JPG, PNG, ...) und den Pixeln des tatsächlich auf dem Bildschirm gezeichneten Wertes da. Bei jedem neuen Frame wird praktisch das Format der Grafik in das korrekte Format für das Blitting konvertiert. Das ist ziemlich zeitaufwendig und wirkt sich zudem negativ auf die Framerate aus.
Dieses Problem können Sie mit der Funktion SDL_DisplayFormat() umgehen. Damit wird der Pixel einer Grafik in ein optimales Format für das Blitting konvertiert.
SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface);
Die Funktion erwartet als Parameter ein Surface (das der Grafik) und gibt darauf ein neues Surface zurück, welches nicht mehr konvertiert werden muss, und Ihnen somit zum schnellen Blitting gleich zur Verfügung steht. Bei einem Fehler wird NULL zurückgegeben. Beachten Sie außerdem, dass das alte Surface unangetastet bleibt und Sie es, sofern Sie das Orginal-Format nicht mehr benötigen, wieder freigeben - da Sie sonst ein Speicherleck haben.
Außerdem zerstört diese Funktion den Alpha-Kanal des Bildes (nicht das Pre-Alpha-Blending) - da das Alpha-Blending ebenfalls eine Geschwindigkeitsbremse für ein schnelles Blitting ist - sofern keine RLE-Beschleunigung unterstützt wird. Wenn Sie den Alpha-Kanal ebenfalls mitkonvertieren wollen, können Sie die Funktion SDL_DisplayFormatAlpha() verwenden, welche, außer dass der Alpha-Kanal mitkonvertiert wird, genauso anzuwenden ist wie SDL_DislplayFormat().
