Sprite-Sheet-Klasse

Aus Spieleprogrammierer-Wiki
Wechseln zu: Navigation, Suche

In Spielen ist es üblich, Bilder zu verwenden, um die Spielfiguren, den Hintergrund, Gegenstände oder das User Interface darzustellen. Bedingt durch das Laden und Entladen vieler einzelner Bilder in den Speicher kann die Performance leiden. Eine gute Alternative zu einer Vielzahl von Einzelbildern sind Sprite Sheets. Sie haben nicht nur den Vorteil, dass die Performance verbessert wird, auch der benötigte Speicherplatz auf einem Datenträger veringert sich tendenziell. Der Nachteil dabei ist, dass zum Verwalten eines Sprite Sheets eine Extra Code benötigt wird. Der Aufbau und die Funktionsweise einer entsprechenden Klasse wird in diesem Artikel beschrieben.

Inhaltsverzeichnis

Allgemeines zum Sprite Sheet


Beispiel für ein Sprite Sheet

Ein Sprite Sheet ist eine Bilddatei, in der mehrere Einzelbilder enthalten sind, die nicht miteinander in Beziehung stehen müssen. Sowohl die Breite und Höhe des Sprite Sheets, als auch der Einzelbilder sollten einer Potenz von 2 entsprechen, z. B. 4, 8, 16, 32 oder höher. Diese Größe gewährleistet, dass auch ältere Grafikkarten damit problemlos arbeiten können. Die Einzelbilder sollten idealerweise gleich groß sein. Es ist nicht zwingend erforderlich, macht aber den Zugriff auf die Einzelbilder wesentlich leichter. Sofern die Transparenz nicht über den Alphakanal geregelt wird, sollte die Hintergrundfarbe einheitlich sein, da diese Farbe dann im Spiel transparent gemacht wird. Häufig verwendete Hintergrundfarben sind Lila (255, 0, 255), ein dunkleres Lila (127, 0, 127), Zyan (0, 255, 255) oder ein dunklerer Blau-Grün-Ton (0, 127, 127). Die Farbe kann frei gewählt werden, wobei man allerdings sicherstellen muss, dass die Farbe im Spiel nicht sichtbar sein wird und somit nicht im Bild verwendet werden darf.

Die Farbe wird nur dann als Hintergrundfarbe interpretiert undn icht dargestellt, wenn der Farbwert exakt stimmt. Somit ist es möglich, eine für den Menschen nicht von dieser unterscheidebare Farbe zu verwenden, die sich in einem der Farbwerte nur um 1 Unterscheidet. Weiterhin resultiert daraus, dass das gewählte Dateiformat eine Verlustfreie Kodierung ermöglichen muss, da ansonsten aufgrund der Kompression nicht die gesamte Farbfläche erkannt wird und einzelne Pixel nicht transparent dargestellt werden.

Aufbau der Sprite Sheet Klasse


Klassendiagramm einer möglichen Sprite Sheet Klasse

Der Aufbau einer Sprite Sheet Klasse muss nicht zwingend genauso aussehen, wie hier Beschrieben. Es gibt auch andere Varianten eine solche Klasse aufzubauen, deshalb ist dies nur stellvertretend für eine Vielzahl an Möglichkeiten.

Wie in dem Bild des Klassendiagramms Rechts zu sehen ist, enthält die Klasse CSpriteSheet folgende Klassenvariablen:


Weiterhin hat die Klasse CSpritesheet folgende Klassenmethoden:

Funktionsweise der Sprite Sheet Klasse


Die Funktion der Sprite Sheet Klasse kann man sich so vorstellen, als wenn jemand ein Sichtfenster mit definierter Größe immer an die gewünschte Position eines vorher ausgesuchten Bildes schiebt. Die Größe des „Sichtfenster“ wird über die Set_Pixel_X(int X) und Set_Pixel_Y(int Y) Funktionen eingestellt, die Länge und Höhe werden in Pixel angegeben. Mit der Load(String Path) Funktion kann ein beliebiges Bild geladen werden. Zu gleich werden einige Informationen des Bildes an die Sprite Sheet Klasse weitergegeben, wie z.B. Image_X oder Image_Y. Über die Funktion Get_Sprite(int Index) kann das gewünschte „Sichtfenster“, in Form eines Sprites, geholt werden. Der Index bestimmt dabei welcher Ausschnitt angezeigt werden soll. Wenn man sich Vorstellt, dass das Sprite Sheet in Spalten und Zeilen aufgeteilten ist. Ergibt sich die Spaltenbreite aus dem Wert von Pixel_X und die Höhe der Zeilen aus dem Wert von Pixel_Y. Nun kann man leicht, die entstanden Rechtecke, von links nach rechts durchzählen, begonnen wird dabei mit der Null wie bei einem Array. So lässt sich vom Bildausschnitt auf den Index schließen. Anzumerken ist hier noch das auch die erste Spalte bzw. Zeile jeweils mit 0 beginnt.

In der Funktion Get_Sprite(int Index) wird bei jedem Aufruf Berechnet, an welcher Stelle im Bild, der angegebene Index liegt. Erreicht wird das mit Hilfe der SpriteRow Variable. SpriteRow lässt sich berechnen, wenn die Größe des Bildes und des Bildausschnittes bekannt sind. SpriteRow = Image_X / Pixel_X. Die Maxsprite Variable dient der Überprüfung ob der angegebene Index außerhalb des Wertebereiches liegt, sie kann berechnet werden wenn Image_Y, Pixel_Y und SpriteRow bekannt sind. MaxSprite = SpriteRow * Image_Y / Pixel_Y - 1 (Die -1 wegen der zählweise von der 0 beginnend). Diese Werte sollten in der Load-Funktion und/oder in den Set_Pixel-Funktionen berechnet werden, da sie sich beim Gebrauch der jeweiligen Funktion ändern können.

Beim Aufruf der Get_Sprite(int Index) Funktion wird mit einer Ganzzahldivision die gewünschte Zeile und mit einer Modulo Operationen die Gewünschte Spalte des Einzelbildes ermittelt. Die Zeile wird mit Zeile = Index / SpriteRow berechnet und die Spalte mit Spalte = Index % SpriteRow. Der letzte Schritt ist die Koordinatenpaare des „Sichtfenster“ für die obere linke Ecke (X1;Y1) und die rechte untere Ecke (X2;Y2) zu berechnen. Mit der Rechnung X1 = Spalte * Pixel_X und Y1 = Reihe * Pixel_Y ist das erste Koordinatenpaar berechnet. Das zweite Koordinatenpaar ist mit X2 = X1 + Pixel_X und Y2 = Y1 + Pixel_Y auch schnell berechnet.

Zum Verdeutlichen hier ein konkretes Rechenbeispiel für das oben zu sehenden Sprite Sheets:

Bei dem Aufruf von Get_Sprite(11); würde diese Funktion dann den Bildausschnitt X1=96, Y1=64 und X2=128,Y2=96 in Form des darin enthaltenen Bildes zurückgeben. Im obigen Sprite Sheet Beispiel wäre es der Große Blaue Trank.

Dazu die Rechnung:

Also ergeben sich für die linke obere Ecke die Koordinaten (96;64) und die rechte untere Ecke sind die Koordinaten (128;96). Diese Beispielrechnung lässt sich auch mit jeder beliebigen anderen Zahl zwischen 0 und 15 durchführen und man kommt immer zum gewünschten Bildausschnitt auch kann die Bildgröße sich ändern oder die Größe des Bildausschnittes das Ergebnis bleibt das Selbe.

Pseudocode


Da es eine große Anzahl von Frameworks und Programmiersprachen gibt, sind hier exemplarisch die wichtigsten Funktionen der Sprite Sheet Klasse in Pseudocode. Das Transferieren des Pseudocodes in die gewünschte Programmiersprache / Framework sollte für die Meisten keine Probleme darstellen.

Header Datei

/* Header der Sprite Sheet Klasse 
   Die Klasse wurde dahingehend erweitert, 
   dass gleich die gewünschte Sichtfenstergröße mit an die 
   Lade Funktion übergeben wird */
 
class CSpriteSheet
{
private:
    Framework::Image SpriteSheetIma; // Bildformat des Frameworks
    Framework::Sprite SpriteSheet;   // Spriteformat des Frameworks
    string Path;                     // stringklasse der Programmiersprache
    int Image_X, Image_Y;            // Bildgröße
    int Pixel_X, Pixel_Y;            // Sichtfenstergröße
    int MaxSprite;                   // Maximal Anzahl von Sprites im SpriteSheet
    int SpriteRow;                   // Maximal Anzahl der Sprites in einer Reihe 
public:
    void Set_Pixel_X(int pixel);     // Sichtfenstergröße verändern 
    void Set_Pixel_Y(int pixel);     // Sichtfenstergröße verändern 
    string Get_Path();               // Bilddateipfad holen
    Get_Sprite(int Index);           // gewünschte Sprite / Ausschnitt holen
    Load(string path,                // Bild Ladefunktion und Setzen der Variablen
         int pixel_x = 32,
         int pixel_y = 32);
    CSpriteSheet();                   // Konstruktor
    ~CSpriteSheet();                  // Destroktor
 
};

Load(string path) Funktion

/* Die Bild Lade Funktion der Klasse wurde dahingehend erweitert, 
   dass gleich die gewünschte Sichtfenstergröße mit an die 
   Funktion übergeben wird */
 
bool CSpriteSheet::Load(string path,int pixel_x,int pixel_y)
{
    // Übernehmen des Bildpfades
    Path = path;
    // Übernehmen der Größe des Sichtfensters
    Pixel_X = pixel_x;
    Pixel_Y = pixel_y;
    // Ladefunktion des Frameworks für ein Bild mit Überprüfung auf Fehler
    Wenn Laden Erfolgreich (SpriteSheetImg.Lade_Bild(Path))
    {
        // Übernehmen der Bildgröße mit Funktionen des Frameworks
        Image_X = SpriteSheetImg.Get_Size_X();
        Image_Y = SpriteSheetImg.Get_Size_Y();
        // Festlegen welche Farbe Transparent sein soll mit Hilfe von Framework Funktionen
        SpriteSheetImg.Set_Color_Transparency(FF,00,FF);
        // Das Bild für das Eigentliche SpriteSheet festlegen mit Hilfe der Framework Funktionen
        SpriteSheet.Set_Image(SpriteSheetImg);
        // Berechnen der Maximalen Anzahl an Einzelbilder in einer Reihe
        SpriteRow = Image_X / Pixel_X;
        // Berechnen der Maximalen Anzahl an Einzelbilder insgesamt
        MaxSprite = SpriteRow * Image_Y / Pixel_Y -1;
        zurückgeben true;
    }
    ansonsten
    {
        zurückgeben false;
    }
}

Get_Sprite(int Index) Funktion

/* Die Get_Sprite Funktion */
 
Sprite CSpriteSheet::Get_Sprite(int Index)
{
    // Gültigkeit des Index prüfen
    Wenn (Index <= MaxSprite)
    {
        // Ein Rechteckerzeugen mit Funktionen des Frameworks
        Framework::intRechteck Rechteck;
        // Zeile und Spalte berechenen
        int Spalte = Index % SpriteRow;
        int Zeile = Index / SpriteRow
        // Koordinaten des Rechteckes setzen intRechteck::Set_Coords(X1,Y1,X2,Y2)
        intRechteck.Set_Coords(Spalte * Pixel_X,Zeile * Pixel_Y,Spalte * Pixel_X + Pixel_X,Zeile * Pixel_Y + Pixel_Y);
        // Setzen des Sichtfensters im SpriteSheet mit Framework Funktionen
        SpriteSheet.Set_Rect(intRechteck);
        // Rückgabe des SpriteSheet zu sehen ist dann nur der Ausschnitt
        zurückgeben SpriteSheet;
    }
    ansonsten
    {
        zurückgeben Fehlermeldung;
    }
}
Meine Werkzeuge
Namensräume
Varianten
Aktionen
Navigation
Werkzeuge