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
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};
CSurface: Funktionen von Interesse
class CSurface
{
HRESULT DrawBitmap();
};
- Funktion überträgt eine Bitmap aus einer Datei in eine Surface bzw. Oberfläche
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};
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;
}
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];}}
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);};
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);
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;}
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
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)
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()
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
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 */
};
Spieloberfläche
Ziffernbereich
Deckel
Fallfläche
Hintergrund
Vorschau
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
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;}
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;}
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
- 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){...}}
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();
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
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){
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}}}}
- 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
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
... Vielen Dank!!