27
V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen Grafikoperationen enthält: class display - unter Rückgriff auf definierte Klassen CDisplay und CSurface in Ddutil.h und Ddutil.cpp - CDisplay: - allgemeine Schnittstelle zum Grafiksystem - stellt übergreifende Dienste bereit - CSurface: - Instanzen nehmen einzelne darzustellende Bitmaps auf - Elemente der Oberfläche werden aus Bitmap -

V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Embed Size (px)

Citation preview

Page 1: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

V03 Laden und Initialisieren der Grafiken

- Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen- eine Klasse anlegen, die alle erforderlichen Grafikoperationen enthält: class display- unter Rückgriff auf definierte Klassen CDisplay und CSurface in Ddutil.h und Ddutil.cpp- CDisplay:

- allgemeine Schnittstelle zum Grafiksystem- stellt übergreifende Dienste bereit

- CSurface: - Instanzen nehmen einzelne darzustellende Bitmaps auf- Elemente der Oberfläche werden aus Bitmap - Datei in Klasse CSurface eingelesen

Page 2: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

CDisplay: Funktionen von Interesse

class CDisplay{public:HRESULT CreateWindowedDisplay;// erzeugt ein Display in einem FensterHRESULT CreateSurfaceFromBitmap(...);// erzeugt eine Surface aus einer BitmapdateiHRESULT Blt(...);// kopiert eine Surface in das DisplayHRESULT Present();// aktualisiert Display im FensterLPDIRECTDRAW7 GetDirectDraw();// gibt Zeiger auf das mit Display verbundene

DirectDraw - Objekt zurückHRESULT UpdateBounds();// aktualisiert Display bei Größenänderung des Fensters};

Page 3: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

CSurface: Funktionen von Interesse

class CSurface

{

HRESULT DrawBitmap();

};

- Funktion überträgt eine Bitmap aus einer Datei in eine Surface bzw. Oberfläche

Page 4: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

1. Schritt- Klasse display wird mit dem CDisplay versehen und mit

erforderlichen Oberflächen bzw. Csurfaces- hinzu kommen Konstruktor display und Destruktor ~display und

eine Freigabefunktion namens free_allclass display{private:CDisplay dsply;CSurface *hgrnd;// Zeiger auf ein CSurface CSurface *fldst;CSurface *fllst;CSurface *prvst;CSurface *deckel;CSurface *ziff [10];public:display ();// Initialisierung der Zeiger mit 0void free_all();// gibt Speicher frei~display() { free_all(); }// Aufruf von Freigabefunktion};

Page 5: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Konstruktor: alle Zeiger werden mit 0 initialisiert

display::display ()

{

int i;

hgrnd = 0;

fldst = 0;

fllst = 0;

prvst = 0;

deckel = 0;

for ( i = 0; i < 10; i++)

ziff [i] = 0;

}

Page 6: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

free_all wird vom Destruktor aufgerufen undbeseitigt alle dynamisch angelegten Objekte, d.h. gibt Speicher freivoid display :: free_all (){int i;if (hgrnd)delete hgrnd;if (fldst)delete fldst;if (fllst)delete fllst;if (prvst)delete prvst;if (deckel)delete deckel;for (i = 0; i < 10; i++){ if ( ziff [i] )delete ziff [i];}}

Page 7: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

2. Schritt- Hinzufügen einer Initialisierungsmethode- erzeugt Display und Surfaces- Initialisierung des Displays durch die Funktion

CreateWindowedDisplay- diese übernimmt den Handle des umschließenden Fensters (wnd)

und die gewünschten Abmessungen des Displays (ultris_nettobreite, ultris_nettohoehe)

- Zur Erinnerung: HWND ultris_window- Handle; Zeiger, der Zugriff auf Hauptfenster ermöglicht; in WinMain

Funktionclass display{private:...public:.. HRESULT init (HWND wnd);};

Page 8: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

HRESULT display :: init (HWND wnd)

{

HRESULT hr; //Fehlercode hr,erfolgt Rücksprung in aufrufendes Programm

int i;

char fname[20];

hr = dsply.CreateWindowedDisplay (wnd, ultris_nettobreite, ultris_nettohoehe);

if (hr < 0)

return hr;

hr = dsply.CreateSurfaceFromBitmap (&hgrnd, “ul_hgrnd.bmp”, ultris_nettobreite, ultris_nettohoehe);

if (hr < 0)

return hr;

hr = dsply.CreateSurfaceFromBitmap (&fldst, “ul_feld.bmp”, 20, 20);

if (hr < 0)

return hr;

hr = dsply.CreateSurfaceFromBitmap(&fllst, “ul_stein.bmp”, 20, 20);

Page 9: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

if (hr < 0)return hr;hr = dsply.CreateSurfaceFromBitmap (&prvst, “ul_prev.bmp”,

15, 15);if (hr < 0)return hr;hr = dsply.CreateSurfaceFromBitmap (&deckel, “ul_adeck.bmp”,

240, 100);if (hr < 0) // überprüft ob bei Erzeugung Fehler aufgetretenreturn hr; // Rücksprung in aufrufendes Programmfor (i=0; i < 10; i++){sprintf (fname, “ul_z%d.bmp”, i);hr = dsply.CreateSurfaceFromBitmap ( &ziff[i], fname, 20,

40); if (hr < 0)return hr;}return S_OK;}

Page 10: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

hr = dsply.CreateSurfaceFromBitmap (&fldst, “ul_feld.bmp”, 20, 20);

if (hr < 0)

return hr;

- einzelne Surfaces werden durch Aufruf der Funktion

CreateSurfaceFromBitmap dynamisch aus jeweiliger Bitmap - Datei

erzeugt- als Parameter übergeben:

- Adresse eines Zeigers, in dem Referenz auf Surface abgelegt wird

- Name der Bitmap - Datei

- Abmessung (Breite, Höhe) der Bitmap

- tritt bei Erzeugung eines Elements ein Fehler auf- Fehlercode hr - erfolgt Rücksprung in aufrufendes Programm- ein Surface entspricht einer Bitmap

Page 11: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Kontinuierlich wirkende Animation für das menschliche Auge

- Kinofilm besteht aus Folge von Standbildern- in der Abfolge der Standbilder eine Frequenz von mehr als 20 Bildern

bzw. Frames pro Sekunde = Eindruck einer kontinuierlichen Bewegung

- unser Display hat Front- und Backbuffer

- im Frontbuffer: befindet sich Bild, das gerade angezeigt wird

- im Backbuffer: neues Bild, das heißt den nächsten Frame aufbauen- zur Darstellung des nächsten Frames - umschalten zwischen Front-

und Backbuffer

- zwei benötigte Funktionen:

- Funktion um Surfaces in den Backbuffer des Displays zu laden ( Blt)

- Funktion um zwischen Front- und Backbuffer umzuschalten (Present)

Page 12: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

1. Funktion Blt = Abkürzung für Blocktransfer- Bitmap einer Surface (Quellsurface) anbestimmte Position des Displays (Zielbereich)transferieren oder “blitten”

HRESULT CDisplay::Blt (DWORD x, DWORD y, CSurface * pSurface, RECT* prc)

HRESULT CDisplay::Blt (x - Koordinate des Zielbereichs, y-Koordinate des Zielbereichs, Quell - Surface, ausgewähltes Rechteck der Quell Surface (kann fehlen))

- wenn kein Teilrechteck (prc) angegebenwird,so wird die gesamte Bitmap geblittet

2. Funktion zum Umschalten zwischen Front- und BackbufferHRESULT CDisplay::Present()

Page 13: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Blt - Methodenclass display{private:...public: ... void hintergrund () {dsply.Blt (0, 0, hgrnd);}// hintergrund beginnt an Position (0,0), füllt gesamten// Fensterinhalt, Csurface ist hgrndvoid abdeckung () {dsply.Blt (60, 0, deckel); }// abdeckung beginnt an stelle (60,0), void ziffer (int pos, int val) {dsply.Blt (120+pos*20, 50, ziff

[val] );}// Zifferndarstellung beginnt bei 120, deshalb 120+// eine Ziffernabbildung ist 20 Pixel breit// int pos ist Index der Ziffer// int val entscheidet über Art der Bitmapvoid feldstein (int z, int s) {dsply.Blt (80+s*20, 100+z*20,

fldst);}// Segment eines gefallenen Steins liegt in einer Zeile z und

Spalte s // Feld beginnt bei x = 80 und y = 100// Zeilen- und Spaltenbreite beträgt jeweils 20 Pixel// Koordinaten berechnen sich daher x = 80+s*20 und y = 100+z*20

Page 14: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

void fallstein (int z, int s, int offset){ dsply.Blt (80+s*20, 100+z*20+offset, fllst);}

// fallendes Steinsegment in Spalte s, jedoch nicht in Zeile,// da es Pixel für Pixel fällt// zu der letzten Zeile z kommt Zugabe offset, um die das// Segment weiter gerückt ist// Offset = Anzahl von Pixeln, die sich Stein n.u. bewegt hatvoid prevstein (int p, int z, int s, int b, int h){ dsply.Blt (290+s*15+(4-b)*15/2, 410-p*70+z*15+(4-h)*15/2,

prvst);}// Berechnung der Position der Vorschausteine// Steine sollen in Ausgabe zentriert dargestellt werden// es gibt bis zu fünf Vorschausteine p = 0,1,2,3,4// linke obere Ecke: x = 290 und y = 410-p*70/* bei Segmentgröße von 15 x 15 Pixeln liegt die linke obere

Ecke eines Segments in Spalte s und Zeile z bei x = 290+s*15 und y = 410-p*70 + z*15 */

/* wenn Anzahl der Segmente des Steins in Höhe h und Breite b bekannt ist, kann man den Randausgleich berechnen, wenn Stein in 4 x 4 Segmente großen Vorschaubereich */

/* man erhält Koordinaten: x = 290+s * 15 + (4-b)*15/2 und y = 410 - p*70+z*15+(4-h) * 15/2 */

};

Page 15: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Spieloberfläche

Ziffernbereich

Deckel

Fallfläche

Hintergrund

Vorschau

Page 16: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Funktion HRESULT present ();- Grafiken aus dem Backbuffer auf den Bildschirm bringen- Phänomen der “Lost Devices”: Zugriff auf Devices wie die

Grafikkarte kann “verloren gehen” -> Device befindet sich dann im “lost state”

- Grafikoperationen, die nur auf internen Speicher zurück greifen, wie etwa das Blitten, werden fehlerfrei durchgeführt

- jedoch wird bei Umschalten von Front- und Backbuffer der lost state kritisch, da jetzt auf Grafikkarte zugegriffen werden muss

- Returncode für Fehlermeldung: DDERR_SURFACELOST- verlorene Daten auf Grafikkarte müssen restauriert

werden: dies macht Funktion restore

Page 17: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Funktion HRESULT present ()- zunächst Ausführen der Present Funktion von

CDisplay- sollte das misslingen, weil der Fehlercode übergeben

wird, so werden die Surfaces restauriert

HRESULT display::present(){HRESULT hr;hr = dsply.Present();if ( hr == DDERR_SURFACELOST )//fehlercode

return restore();return hr;}

Page 18: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Funktion HRESULT restore();- restaurieren aller Surfaces des Displays - neuzeichnen aller Bitmaps in SurfacesHRESULT display::restore(){HRESULT hr;int i;char fname[20];hr = dsply.GetDirectDraw() -> RestoreAllSurfaces();if (hr < 0)return hr;hr = hgrnd->DrawBitmap (“ul_hgrnd.bmp”, ultris_nettobreite,

ultris_nettohoehe);if (hr < 0)return hr;hr = fldst -> DrawBitmap (“ul_feld.bmp”, 20,20);if...fllst, prvst, deckel ...for ( i = 0; i < 10 ; i++){sprintf (fname, “ul_z%d.bmp”, i);hr = ziff [i] -> DrawBitmap (fname, 20, 40);if ( hr < 0)return hr;}return S_OK;}

Page 19: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

zum Abschluss zwei weitere Member - Funktionen...

class display

{

private:

...

public:..

void update () { dsply.UpdateBounds();}

HRESULT cooperative (){ return dsply.GetDirectDraw() -> TestCooperativeLevel ();}

};

- Memberfunktion update:Aufruf, wenn die Lage unseres Hauptfensters auf Bildschirm verändert ist

- es geschieht Aufruf der Methode UpdateBounds, die Display an die neue Lage anpasst

- Memberfunktion cooperative: stellt „Kooperativität“ des Displays fest

Page 20: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

- Klasse display ist jetzt vollständig implementiert, wir legen Instanz dieser Klasse mit dem Namen ultris_display an

display ultris_display;- Objekt muss zum Schluss in Windows Applikation integriert werden- innerhalb WinMain Funktion Display initialisieren bevor Fenster mit

ShowWindow dargestellt wirdint APIENTRY WinMain (...){...if (ultris_display.init (ultris_window) < 0){ // wenn Initialisierung fehl schlägt, Abbruch// der Anwendung mit FehlerdialogMessageBox (ultris_window, “Fehler beim Initialisieren

der Grafik”, “Ultris - Fehlermeldung”, MB_OK | MB_ICONERROR | MB_SETFOREGROUND);

return 0;} // sonst geht es mit Anzeige des Fensters // und Main Event Loop weiterShowWindow (ultris_window, nCmdShow);while (TRUE){...}}

Page 21: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Callback - Handler unseres Hauptfensters ultris_windowhandler- immer wenn Fenster bewegt oder in seiner Größeverändert wurde, erhalten wir eine Benachrichtigungdurch die Nachricht WM_MOVE- als Reaktion rufen wir die update Funktion unseres Display auf

LRESULT CALLBACK ultris_windowhandler ( ... ){switch (msg){...case WM_MOVE:ultris_display.update();return 0;case WM_PAINT:int i; ultris_display.hintergrund();ultris_display.abdeckung();

Page 22: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

for (i = 0; i< 6; i++)ultris_display.ziffer (i, i+1);for (i = 0; i < 10; i++)ultris_display.feldstein (19-i, i);for (i = 0; i < 10;i++)ultris_display.fallstein ( 1, i, 2*i);for ( i = 0; i < 4; i++)ultris_display.prevstein ( 3, 0, i, 4, 1);ultris_display.present ();break;}...}- hier wird message - orientierte Architektur des Windows - Systems

deutlich- Fensterausgaben dürfen nicht spontan gemacht werden- System fordert zum Neuzeichnen des Hauptfensters mit Message

WM_PAINT auf- wenn diese Message erkannt wird, werden einige Testausgaben

gemacht: Hintergrund, Abdeckung, einige Ziffern und Steine

Page 23: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Funktion WinMain- an die Stelle, in der regelmäßig wiederkehrende, das Spiel betreffendeAktivitäten eingebaut werden können, gelangt man immer dann, wenn keineWindows - Message vorliegt- hier kann also der Spielverlauf programmiert werden- zudem: regelmäßiges Prüfen der Devices und der Kooperativitätint APIENTRY WinMain (...){while (TRUE){if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)){...}else{HRESULT hr;hr = ultris_display.cooperative();// Prüfen der Kooperativität// wenn der Aufruf der Funktion ein negatives Ergebnis// liefert, so liegt ein Problem vorif (hr < 0){

Page 24: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

switch (hr){case DDERR_EXCLUSIVEMODEALREADYSET:// eine andere Anwendung hat sich Grafik Device exklusiv// gesichertSleep (10);//warten bis es frei gegeben wirdbreak;case DDERR_WRONGMODE:// jemand hat Grafikmodus (etwa die Auflösung) geändert// in dieser Situation muss alles noch einmal neu// initialisiert werden// zuvor belegte Ressourcen frei gebenultris_display.free_all();ultris_display.init (ultris_window);PostMessage (ultris_window, WM_PAINT, 0, 0);// hier Message WM PAINT schicken zum Neuzeichnen des Fenstersbreak;}}else{// hier kann Spiel stehen}}}}

Page 25: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

- Es gibt weitere Meldungen im Zusammenhang mit Grafikkarte, auf die man reagieren kann

- Wenn das System im 8 -Bit -Grafikmodus ist (256 Farben), arbeitet die Grafikkarte mit Farbpalette

- Das heißt, auch die Farbpalette muss neu initialisiert werden, wenn sie durch andere Anwendung überschrieben wurde

- Entsprechende Message: WM_QUERYNEWPALETTE- Wir verwenden jedoch für Ultris Bitmaps mit mehr als 256

Farben

Page 26: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

Kurz zurück zum Blitten - - es ist auch möglich statt rechteckiger Objekte andersformige Objekte

zu blitten- Dies geschieht unter Verwendung des Color Keying- Eine in der darzustellenden Bitmap nicht vorkommende Farbe wird

als Color Key für die Surface ausgewählt- Alles, was in Color Key Farbe in der Bitmap vorkommt, wird nicht

transferiert- Equivalent: „Blue Box“ im Fernsehen- Beispiel: Bitmap liegt in einer Datei vor, alles, was nicht gezeichnet

werden soll, ist schwarz- Anlegen einer Surface: CSurface *s- Initialisieren der Surface mit CreateSurfaceFromBitmap- Color Key wird duch Funktionsaufruf gesetzt:

s -> SetColorKey (RGB (0, 0, 0));

- schwarze Bildpunkte werden nicht transferiert

Page 27: V03 Laden und Initialisieren der Grafiken - Grafiken des Spiels laden und testweise auf dem Bildschirm anzeigen - eine Klasse anlegen, die alle erforderlichen

... Vielen Dank!!