Das erste Spiel Universität zu Köln

Slides:



Advertisements
Ähnliche Präsentationen
Klassen - Verkettete Liste -
Advertisements

DVG Dateien Dateien. DVG Dateien 2 Die Klasse File Die Klasse File stellt die Verbindung zwischen dem Filesystem des Rechners und dem.
1. 2 Heute entwickeln wir eine kleine DirectInput-Bibliothek,auf die wir bei unseren zukünftigen Projekten immer wieder zurückgreifen werden. Die Themen.
Sortieren I - Bubblesort -
Universität Dortmund, Lehrstuhl Informatik 1 EINI II Einführung in die Informatik für Naturwissenschaftler und Ingenieure.
Symbole beim Picturepublisher in der Standartleiste hinzufügen
der Universität Oldenburg
Datenstrukturen Look-Up Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung.
VO2 Laden und Initialisieren der Sounds. Wir wollen Sounds in unsere Applikation laden Menü erweitern –um den Menüpunkt Sound –mit dem Identifier ID_ULTRIS_SOUND.
Ultris Version 8: Erzeugen der Formen und Anzeigen der Vorschau
Ultris V10 Bewegen, Drehen und Kollisionserkennung.
Java: Objektorientierte Programmierung
Java: Dynamische Datentypen
Ein Beispiel in Java.
Polymorphie (Vielgestaltigkeit)
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
V09 Fallende Formen, Geschwindigkeitsregelung und Timing Daniel Ebner Spieleprogrammierung mit DirectX und C++
Softwaretechnologie II (Teil 1): Simulation und 3D Programmierung
Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander.
Vers. 6: Der Konfigurationsdialog Quelle: Spieleprogrammierung mit DirectX und C++, U. Kaiser und P. Lensing, Galileo Computing (2007)
Spieleprogrammierung mit DirectX und C++
V03 Laden und Speichern von Spielfeldern und der Spielfeldeditor.
Windows Explorer.
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
Zusammenfassung Vorwoche
Excel Kurs Teil I Excel starten, Verknüpfungen auf dem Desktop anlegen. F. Bäumer.
Einführung in Visual C++
Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.
3D Programmierung Version 12 - Highscores. Die vom Spieler erzielte Punktzahl wird mit 5 vorgegebenen Punktzahlen verglichen und, falls nötig, in die.
Einfach verkettete Listen (OOP)
Objektorientierte Modellierung
Eine Bewerbung schreiben
Anlegen von Ordnern, speichern und Öffnen von Bildern und Briefen
Ich möchte gerne mehrere Bilder auf ein Folie
Wie man eine einfache Präsentation erstellt...
Universität zu Köln Institut für Historisch-Kulturwissenschaftliche Informationsverarbeitung Prof. Dr. Manfred Thaller AM 3 Übung: Softwaretechnologie.
Moin. Ich benutze PPT 2002 und möchte drei Bilder nacheinander 1
1 Workshop Service Inbetriebnahme und Rücksicherung Saia Service an PCD's Inbetriebnahme und Rücksicherung III. Workshop Inbetriebnahme und Laden der Datensicherung.
Abteilung für Telekooperation Übung Softwareentwicklung 1 für Wirtschaftsinformatik Dr. Wieland Schwinger
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Dateien auf dem Desktop erstellen
3D- Spieleprogrammierung
Einführung in die Programmiersprache C 3.Tag Institut für Mathematische Optimierung - Technische Universität Braunschweig.
Game Development mit LUA Integration und Kommunikation von LUA mit C++ Referat von Paul van Hemmen Seminar: Reusable Content in 3D und Simulationssystemen.
Softwaretechnologie II (Teil 1): Simulation und 3D Programmierung Medienwiss./Medieninformatik AM3 Visuelle Programmierung I Referent: Janek Rudolf.
Von der Planung bis zum Hauptmenü Seminar: Softwaretechnologie II Dozent: Prof. Manfred Thaller Referent: Jan Bigalke.
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Programmierung Wintersemester 2009/10 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fakultät.
Wie man eine einfache Präsentation erstellt...
Variablenkonzept Klassisch, in Java Basistyp
Einführung. Steuerelemente Hier erstellt man das Fenster, mit dem der Anwender später arbeiten soll: Werte eingibt Buttons klickt Auswahlen trifft.
Saia Service an PCD's Wartung
LEGO NXT Roboter in Java programmieren
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Inf K1/2 Sj 13/14 GZG FN W.Seyboldt 1 SFZ FN Sj. 13/14 Python Klassen und Objekte.
Universität zu Köln Historisch-Kulturwissenschaftliche Informationsverarbeitung Softwaretechnologie II (Teil 1): Simulation und 3D Programmierung WS 2013/14.
Breakanoid – Bälle & Blöcke Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung WS 12/13 Übung: Visuelle Programmierung I –
1. Charakteranimation 2. PlugIns schreiben und laden Universität zu Köln Institut für Historisch-Kulturwissenschaftliche Informationsverarbeitung WS 2010/2011.
A Workshop About this chapter General description Units Time Schedule
Historisch-Kulturwissenschaftliche Informationsverarbeitung Übung: Softwaretechnologie II / Visuelle Programmierung Dozent: Prof. Dr. Manfred Thaller WS.
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
Was macht er? Wie funktioniert er? Wie sieht er aus?
Tutorium Software-Engineering SS14 Florian Manghofer.
C++ FÜR cOMPUTERSPIELENTWICKLER
Ein Spiel mit der SDL - Teil I. Ein Spiel mit der SDL  kostenlose Bibliothek – Simple DirectMedia Layer Grafik darstellen Benutzereingaben abfragen Sounds.
LSI8204ELP & Onboard SATA Controller Allgemeines: – Nicht konfigurierte Festplatten werden automatisch als Single Disks bzw. Logical Drives (einzelne Laufwerke)
Java-Kurs Übung Grafik in Java - das Abstract Windowing Toolkit
Implementieren von Klassen
 Präsentation transkript:

Das erste Spiel Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung WS 12/13 Übung: Visuelle Programmierung I – Simulation und 3D Programmierung Prof. Dr. Manfred Thaller Referentin: Marietta Steinhöfel

Gliederung Das Spielprinzip Das Spielgerüst Der Code Die Grundklasse CBreakanoid Das Titelbild Das Hauptmenü Sound

1. Das Spielprinzip Name: „Breakanoid“ (Arkanoid + Breakout) „2.5D-Grafik“: Grafik: 3D Bewegung: 2D (xz-Ebene) Kamera synchron zu Schläger

2. Das Spielgerüst Die Spielzustände enum-Aufzählung verwaltet Spielzustände: EGameState (speichert Wert für Spielzustand) Intro = Titelbild, über das man zum Hauptmenü gelangt GS_INTRO Hauptmenü = Hintergrundbild & Vordergrund (Auswahl Menüeintrag) GS_MAIN_MENU Spiel GS_GAME Kein Spielzustand GS_NONE Breakanoid.h

2. Das Spielgerüst Die Breakanoid-Klasse Grundklasse, die das ganze Spiel verwaltet: CBreakanoid Instanz der Klasse wird in WinMain-Funktion erzeugt Methode wird aufrufen, um Spiel zu starten Am Ende wird Instanz wieder gelöscht Breakanoid.h

2. Das Spielgerüst Die Spielzustandklassen Zusätzlich Klassen für Spielzustände: CIntro CMainMenu CGame Zeiger auf Instanzen d. Klassen in CBreakanoid gespeichert & erstellt Breakanoid.h

2. Das Spielgerüst Methoden CBreakanoid- und Spielzustandklassen haben folgende Methoden: Load lädt Daten für ganzes Spiel (CBreakanoid::Load) oder bestimmte Zustände (CIntro::Load) Unload Herunterfahren Init Initialisierung des kompletten Spiels bzw. Spielzuständen Aufruf der Load -Methode Exit Macht Schritte rückgängig Aufruf der Unload-Methode

2. Das Spielgerüst Verwaltung der Spielzustände Spielzustand ändern mit Methode CBreakanoid::SetGameState bekommt gewünschten Wert übergeben (GameState) Aktuellen Zustand verlassen z.B.: CIntro::Exit Methode aufrufen Neuen Zustand initialisieren z.B.: CMainMenu::Init Beginn & Ende = kein Spielzustand GS_NONE

3. Der Code

3. 1 Die Grundklasse Breakanoid Breakanoid.cpp Breakanoid.h

Variablen I // CBreakanoid-Klasse class CBreakanoid { public: tbConfigm_Config; // Konfiguration TriBase-Engine PDIRECT3DSTATEBLOCK9m_pStateBlock; // Statusblock für Direct3D // Instanzen der Spielzustände – ihre Zeiger werden in Klasse gespeichert CIntro* m_pIntro; // Intro CMainMenu* m_pMainMenu; // Hauptmenü CGame* m_pGame; // Spiel EGameState m_GameState; // Speichern des Aktuellen Spielzustands floatm_fTime; // Stoppuhr = zählt wie viele Sek. Zustand schon aktiv ist [...] Hier ist die Deklaration für die Variablen der Klasse class CBreakanoid .h

Variablen II // Globale Variablen extern CBreakanoid*g_pBreakanoid; // Breakanoid-Zeiger extern float*g_pfButtons; // Array mit float-Werten zur Abfrage der Eingabegeräte  speichert Zustand analoger Knöpfe extern BOOL*g_pbButtons; // Array für digitale Werte d. Knöpfe 2. Außerdem drei gloable Variablen .h

Methoden SetGameState I // Setzt einen neuen Spielzustand tbResult CBreakanoid::SetGameState(EGameState NewGameState) //erwartet EGameState-Wert { tbResult r = TB_OK; // 1. Alten Spielzustand entladen Switch (m_GameState) // GameState: speichert aktuellen/alten Zustand case GS_INTRO: m_pIntro->Exit(); break; // Alten Zustand herunterfahren case GS_MAIN_MENU :m_pMainMenu->Exit(); break; //durch Aufruf der Exit-Methode case GS_GAME :m_pGame->Exit(); break; } // Zeit zurücksetzen m_fTime = 0.0f; // Stoppuhr auf null zurück, weil Zustand nicht mehr aktiv Weil Klasse Spielzustände verwaltet, brauchen wir eine Methode, die den aktuellen Spielzustand setzt, um Spieler zu einem Menüpunkt zu schicken: Die Methode heißt: SetGameState und erwartet einen EGameState-Wert .cpp

Methoden SetGameState II // 2. Neuen Spielzustand laden m_GameState = NewGameState; // Neuen Spielzustand initialisieren switch (m_GameState) // Init-Methoden Aufruf für entsprechenden Spielzustand { case GS_INTRO: r = m_pIntro->Init(); break; case GS_MAIN_MENU: r = m_pMainMenu->Init(); break; case GS_GAME: r = m_pGame->Init(); break; } // Eventuelle Fehler abfangen if(r) TB_ERROR("Fehler beim Laden des Spielzustands!", TB_ERROR); return TB_OK;

Methoden Load I // Lädt das Spiel tbResult CBreakanoid::Load() // lädt relevante Spiel-Daten { char acFilename[256]; // Direct3D initialisieren - Aufruf DirectX-Klassen der TriBase-Engine: // Einstellungen des Konfig.Dialog liegen schon in m_Config (Header) // IDI_ICON1 =Ressource, die Icon des Spiels enthält (zB. Spielszene) if(tbDirect3D::Instance().Init(&m_Config, "Breakanoid", NULL, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1)))) // Fehler! TB_ERROR("Fehler beim Initialisieren von Direct3D!", TB_ERROR); } Load-Methode: CBreakanoid::Load()  Laden des Spiels (relevante Daten!) CBreakanoid::Unload()  Löschen, Speicher frei geben

Methoden Load II […] // DirectInput initialisieren if(tbDirectInput::Instance().Init()) // Speicher für die analogen Knöpfe reservieren g_pfButtons = new float [tbDirectInput::Instance().GetNumButtons()]; // So viel Speicher für Array reserviert - wie analoge Knöpfe g_pbButtons = new BOOL [tbDirectInput::Instance().GetNumButtons()]; // Und nun noch DirectSound... if(tbDirectSound::Instance().Init(&m_Config, NULL, DSSCL_PRIORITY, FALSE)) // FALSE, weil in dem Spiel kein 3D-Sound ist { // Fehler! TB_ERROR("DirectSound konnte nicht initialisiert werden!", TB_ERROR); }

Methoden Init I // Initialisiert das Spiel komplett tbResult CBreakanoid::Init() { tbResult r; // TriBase-Engine initialisieren und den Konfigurationsdialog aufrufen: if (tbInit()) return TB_ERROR; r = tbDoConfigDialog(&m_Config); //Konfigurationsdialog Aufruf & abspeichern in m_Conig //TB_CANCELED = wird von tbDoConfigDialog zurück geliefert, wenn Benutzer im Dialog auf ABRECHEN klickt if(r == TB_CANCELED) return TB_CANCELED; else if(r) TB_ERROR("Engine konnte nicht initialisiert werden!", r); // Laden... if(Load()) TB_ERROR("Fehler beim Laden des Spiels!", TB_ERROR); //Spieldaten laden durch LOAD Methoden Aufruf Initialisierung des gesamten Spiels in CBreakanoid::Init() es wird auf die bereits besprochenen Methoden SetGameState und Load zurück gegriffen Init Methode wird später in Hauptfunktion WinMain aufgerufen CBreakanoid::Exit() Macht Inittialisierungsschritte von Init-Methode rückgängig: Setzt Status auf SetGameState (GS_NONE) Ruft Unload auf  löscht die Instanzen

Methoden Init II // Klassen für alle Spielzustände erstellen  als Instanzen durch NEW m_pIntro = new CIntro; m_pMainMenu = new CMainMenu; m_pGame = new CGame; // Wir beginnen beim Intro! SetGameState(GS_INTRO); // SetGameState setzt Spielzustand aufs Titelbild (Intro) return TB_OK; }

Methoden Move I // Bewegt das Spiel tbResult CBreakanoid::Move(float fTime) // liefert seit letztem Frame vergangene Zeit in Sek. { tbResult r = TB_OK; // Eingabegeräte abfragen. Wertet Eingabe des Benutzers aus + speichert tbDirectInput::Instance().GetState(g_pfButtons, g_pbButtons); […] // Aktuellen Spielzustand bewegen switch(m_GameState) //ruft Move-Funktion für jeweilige Klasse auf case GS_INTRO: r = m_pIntro->Move(fTime); break; case GS_MAIN_MENU: r = m_pMainMenu->Move(fTime); break; case GS_GAME: r = m_pGame->Move(fTime); break; } Render- (zeichnen) und Move-Funktion (bewegen) werden einmal Pro Frame aufgerufen Move bewegt das Spiel Render zeichnet alles Funktionen erwartet FLOAT-Paramter = liefert seit letzten Frame vergangene Sekunden  damit Programm überall gleich schnell läuft

Methoden Move II // Eventuelle Fehler abfangen if(r) TB_ERROR("Fehler beim Bewegen des Spielzustands!", TB_ERROR); // Frame-Zeit-Wert zu Zustand-Laufzeit-Stopuhr addieren m_fTime += fTime; return TB_OK; }

Methoden Render // Rendert das Spiel tbResult CBreakanoid::Render(float fTime) { […] // Aktuellen Spielzustand rendern switch(m_GameState) { case GS_INTRO: r = m_pIntro->Render(fTime); break; case GS_MAIN_MENU: r = m_pMainMenu->Render(fTime); break; case GS_GAME: r = m_pGame->Render(fTime); break; } // Eventuelle Fehler abfangen if(r) TB_ERROR("Fehler beim Rendern des Spielzustands!", TB_ERROR); return TB_OK;

Methoden Run // Move- und Render-Funktion (Kapselung) tbResult Move(float fTime) {return g_pBreakanoid->Move(fTime);} tbResult Render(float fTime) {return g_pBreakanoid->Render(fTime);} // Lässt das Spiel laufen tbResult CBreakanoid::Run() { // Nachrichtenschleife betreten. Ruft in jedem Frame Move und Render auf if(tbDoMessageLoop(::Move, ::Render)) // Fehler! TB_ERROR("Fehler in der Nachrichtenschleife!", TB_ERROR); } return TB_OK; Alle Methoden liegen vor Man kann das Spiel laufen lassen in CBreakanoid::Run() Es wird ein tbDoMessageLoop aufgerufen, die (Nachrichtenschleife) kümmert sich darum, dass Move und Render in jedem Frame aufrufen werden Es ist in der Rückriffunktion problematisch direkt auf die Breakanoid-Klasse zuzugreifen. Daher die Kapselung als Alternative  ruft entsprechende Methoden auf

Hauptfunktion WinMain I // Windows-Hauptfunktion int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* pcCommandLine, int iShowCommand) { tbResult r; // Spiel initialisieren g_pBreakanoid = new CBreakanoid; r = g_pBreakanoid->Init(); // Init-Funktionsaufruf // Wenn Benutzer "ABBRECHEN" drückt = Programm beenden if(r == TB_CANCELED) TB_SAFE_DELETE(g_pBreakanoid); return 0; } // Wenn es nicht die Benutzereingabe war, handelt es sich um ein Fehler else if(r) g_pBreakanoid->Exit(); MessageBox(NULL, "Fehler beim Initialisieren des Spiels!", "Fehler", MB_OK | MB_ICONEXCLAMATION); return 1; Komplettes Spielgerüst liegt nun vor es wird in Hauptfunktion WinMain eingesetzt Wird zu Beginn aufgerufen *WINAPI ‚Windows Application Programming Interface‘ = Programm Schnittstelle für Programme auf Windows

Hauptfunktion WinMain II // Spiel laufen lassen if(g_pBreakanoid->Run()) { g_pBreakanoid->Exit(); // Abfangen von Fehlern TB_SAFE_DELETE(g_pBreakanoid); MessageBox(NULL, "Fehler im Spiel!", "Fehler", MB_OK | MB_ICONEXCLAMATION); return 1; } // Spiel verlassen g_pBreakanoid->Exit(); return 0;

3.2 Das Titelbild

Das Titelbild CIntro // Intro.h // Klasse für das Intro class CIntro { public: // Variablen PDIRECT3DTEXTURE9 m_pTitle; // Titelbild-Textur // Konstruktor inline CIntro() : m_pTitle(NULL) {} // Methoden tbResult Init(); // Initialisierung = Betreten des Spielzustands tbResult Exit(); // Herunterfahren = Verlassen d. S. tbResult Load(); // Laden aller Daten tbResult Unload(); // Entladen tbResult Move(float fTime); // Bewegen tbResult Render(float fTime); // Rendern }; Jetzt wird Erster Spielzustand implementiert: GS_INTRO Bestandteile sind: Bild (Title.jpg im DATA Ordner), Text (Titel, Aufforderung) Name der Klasse: CIntro Hat Methoden: Spielzustände 1 Variable: Textur mit Titelbild

Das Titelbild Die Schrift // Load-Methode in Breakanoid.cpp // Schriftarten laden m_pFont1 = new tbFont; // Schriftart 1 // Schrift besteht immer aus zwei Dateien -> liegen im Data-Ordner: if(m_pFont1->Init("Data\\Font1.tga", "Data\\Font1.tbf")) { // Fehler! TB_ERROR("Fehler beim Laden der Schriftart Data\\Font1!", TB_ERROR); } [analog zu Schriftart 2] Laden der Schrift (Text) erfolgt in der Breakanoid – Load Methode  so muss sie nur 1x geladen werden und nicht bei jeder Klasse 2 versch. Schriftarten werden in Variablen gespeichert: m_pFont1  für große, dekorativen Text m_pFont2  für Punktzahlen

Das Titelbild Initialisieren, Laden, Entladen //Intro.cpp tbResult CIntro::Load() // Init ruft Load auf { // Titelbild laden (als Textur) m_pTitle = tbTextureManager::Instance().GetTexture("Data\\Title.jpg"); if(m_pTitle == NULL) TB_ERROR("Fehler beim Laden von Data\\Title.jpg!", TB_ERROR); return TB_OK; } // __________________________________________________________________ tbResult CIntro::Unload() // Exit ruft Unload auf // Die Textur löschen tbTextureManager::Instance().ReleaseTexture(m_pTitle);

Das Titelbild Rendern I Rendern erfolgt durch ein Rechteck, das mit der Textur des Bildes überzogen wird  ‚Transformierte Vertizes‘ //Intro.cpp // Vertizes für das Titelbild struct STitleVertex { tbVector 3vPosition; float fRHW; D3DCOLOR Color; tbVector2 vTex0; static const DWORD dwFVF; }; const DWORD STitleVertex::dwFVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1; Transformatierte Vertizes sind dazu da eine 2D-Grafik mit Direct3D zu zeichnen Weil manchmal reichen 2-Dimensionale-Grafiken aus. So wie in diesem Fall: Es wird ein Rechteck benötigt, um zB Text darzustellen, das pixelgenau auf dem Bildschirm platziert werden soll (Ergebnis: Benutzeroberfläche)

Das Titelbild Rendern II // Rendert den Spielzustand tbResult CIntro::Render(float fTime) { STitleVertex aVertex[4]; // 4 Vertizes, für jede Bildschirmecke ein Vertex // Puffer leeren und Szene beginnen tbDirect3D& D3D = tbDirect3D::Instance(); D3D->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, tbColor(0.0f, 0.0f, 0.0f), 1.0f, 0); D3D->BeginScene(); // ------------------------------------------------------------------ // Vertexformat und Titelbildtextur setzen, Z-Buffer aus D3D.SetFVF(STitleVertex::dwFVF); D3D.SetTexture(0, m_pTitle); D3D.SetRS(D3DRS_ZENABLE, D3DZB_FALSE); Rendern des Bildes: Rechteck besteht aus 4 Vertizes Für jede Bildschirmecke eins Via Dreieckfolge TRIANGLESTRIP gezeichnet

Das Titelbild Rendern III // Die vier Vertizes des Titelbilds erstellen (Rechteck) // Links unten aVertex[0].vPosition = tbVector3(0.0f, D3D.GetScreenSize().y, 0.5f); // Position der Pixelkoordinate aVertex[0].fRHW = 1.0f; // Kehrwert der w-Koordinate aVertex[0].Color = tbColor(1.0f, 0.8f, 0.8f); aVertex[0].vTex0 = tbVector2(0.0f, 1.0f); // Texturkoordinate // Links oben aVertex[1].vPosition = tbVector3(0.0f, 0.0f, 0.0f); aVertex[1].fRHW = 1.0f; aVertex[1].Color = tbColor(0.8f, 1.0f, 0.8f); aVertex[1].vTex0 = tbVector2(0.0f, 0.0f); // ...andere genauso […] Besonderheiten transfortmierter Vertizes: x/y Koordinate (0,0->links oben) = entspricht echte Pixel-Bildschirmkoordinaten ...die anderen Ecken abhängig von ausgewähler Auflösung z-KO muss zwischen 0 und 1 liegen w-KO (Kehrwert davon) zur Transformation

Das Titelbild Rendern IIII // Texturkoordinaten sinusförmig verschieben ("wabbeln") // Texturkoordinaten werden für jedes Vertex, in jedem frame geändert: for(DWORD dwVertex = 0; dwVertex < 4; dwVertex++) { aVertex[dwVertex].vTex0.x += sinf(g_pBreakanoid->m_fTime + (float)(dwVertex)) * 0.01f; aVertex[dwVertex].vTex0.y += cosf(g_pBreakanoid->m_fTime + (float)(dwVertex)) * 0.01f; } // Als Dreiecksfolge zeichnen D3D->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, aVertex, sizeof(STitleVertex)); // Bild mit Trainlges-Trip zeichnen […]

Das Titelbild Move // Bewegt den Spielzustand tbResult CIntro::Move(float fTime) { // Wenn eine der typischen Tasten gedrückt wurde: zum Hauptmenü! // Prüft, ob Eingabe getätigt wurde. Akzeptierte Eingaben: if( g_pbButtons[TB_KEY_NUMPADENTER] || // ENTER g_pbButtons[TB_KEY_RETURN] || g_pbButtons[TB_KEY_SPACE] || // LEERTASTE g_pbButtons[TB_MOUSE_BUTTON(0)] || // MAUSTASTEN g_pbButtons[TB_MOUSE_BUTTON(1)]) // gehe mit Verzögerung von 100 Millisek. zum Hauptmenü tbDelay(100); G_pBreakanoid->SetGameState(GS_MAIN_MENU); } return TB_OK; Bewegen des Spielzustandes in der Move-Methode  Übergang im Spiel zeigen

3.3 Das Hauptmenü 1)Klicken aufs Titelbild Ausführen und zeigen! GS_MAIN_MENU mit Klasse CMainMenu Verbindet einzelne Spielteile Wird erreicht durch: 1)Klicken aufs Titelbild 2) Abbrechen des laufenden Spiels 3 Menüpunkte: 1) Starten 2) Hilfe (Klappt nicht…) 3) Abbrechen Wir haben: 3 Texte  ausgewählter: Bewegung + Farbwechsel Bewegendes Hintergrundbild

Das Hauptmenü Variablen // MainMenu.h class CMainMenu { public: // Variablen LPDIRECT3DTEXTURE9 m_pBackground; // speichert Hintergrundbild Int m_iCursor; // Menücursor = merkt sich Ausgewählten der 3 Einträge. [0]=erster. BOOL m_bShowingHelp; // Wird Hilfetext angezeigt? // Konstruktor inline CMainMenu() : m_pBackground(NULL) , m_iCursor(0) , m_bShowingHelp(FALSE) // bei TRUE (Cursor auf ‚Hilfe anzeigen‘): // Menueinträge verschwindet + Hilfekasten wird angezeigt {}

Das Hauptmenü Methoden I CMainMenu::Init() Betreten des Hauptmenüs ruft Load-Methode auf  lädt nur die Textur des Hintergrundbildes Cursor auf null setzen  damit zu Beginn erster Menüeintrag ausgewählt ist CMainMenu::Exit() Verlassen des Hauptmenüs ruft Unload auf  löscht Textur aus Speicher Analog zu Titelbild

Das Hauptmenü Render I Rendern des Bildes wie bei Intro (‚wabbeln‘) Texte für 3 Menüeinträge zeichnen. Dafür gibt es ein Array mit drei Einträgen: tbResult CMainMenu::Render(float fTime) { SBackgroundVertex aVertex[4]; char* apcMenuEntry[3] = {"Spiel starten", "Hilfe anzeigen", "Spiel beenden"}; tbVector2 vPosition; tbColor Color; […]

Das Hauptmenü Render II If-Abfrage prüft, ob Hilfetext oder Menüeinträge gezeichnet werden sollen Position: if(!m_bShowingHelp) { //jeden Text mit Schrifart 'g_pBreakanoid->m_pFont1' rendern g_pBreakanoid->m_pFont1->Begin(); // Die Menüeinträge zeichnen. Jeder der 3 Einträg durchläuft Schleife for(int iEntry = 0; iEntry < 3; iEntry++) // Die Position für den Text dieses Eintrags berechnen vPosition.x = 0.5f; // erster Menüeintrag liegt bei (0.5, 0.4) //jeden weiteren um 0.125 Einheiten nach unten verschieben: vPosition.y = 0.4f + (float)(iEntry) * 0.125f; …

Das Hauptmenü Render III Bewegung: // Wenn der Cursor auf diesem Eintrag liegt, dann schwingt der Text // Wenn render-Eintrag = ausgewählter Eintrag -> dann schwingen if(m_iCursor == iEntry) vPosition.x += 0.05f * sinf(g_pBreakanoid->m_fTime); Farbe berechnen: // Normalerweise ist Eintrag COLOR dunkelblau. // Wenn der Cursor aber darauf liegt, dann ist er heller. if(m_iCursor != iEntry) Color = tbColor(0.3f, 0.3f, 0.9f, 0.75f); // Standardfarbe Blau... else Color = tbColor(0.5f, 0.5f, 1.0f, 1.0f); //... heller & transparenter Text zeichnen: g_pBreakanoid->m_pFont1->DrawText(vPosition, apcMenuEntry[iEntry], // es werden relative und zentrierte Koordinaten/Größen verwendet: TB_FF_ALIGN_HCENTER | TB_FF_ALIGN_HCENTER | TB_FF_RELATIVE | TB_FF_RELATIVESCALING, // Text wird mit 1,5 skaliert -1, Color, Color + tbColor(-0.3f, 0.4f, 0.0f), tbVector2(1.5f, 1.5f));

Das Hauptmenü Move I // Bewegt den Spielzustand tbResult CMainMenu::Move(float fTime) { if(!m_bShowingHelp) // Wenn showing Help = FALSE Pfeiltasten werden bewegt // Wird Taste nach unten/oben gedrückt, wird Cursor durchs Hauptmenüs bewegt if(g_pbButtons[TB_KEY_UP]) // Cursor nach unten bewegen m_iCursor--; tbDelay(80); // … mit Verzögerung } if(g_pbButtons[TB_KEY_DOWN]) // Cursor nach oben bewegen m_iCursor++; tbDelay(80); // Cursor in die Grenzen weisen  es gibt ja nur drei Menüpunkt zum Wählen [0,1,2] if(m_iCursor < 0) m_iCursor = 2; if(m_iCursor > 2) m_iCursor = 0; […]

Das Hauptmenü Move II // Wenn die Enter-, Leer- oder Return-Taste gedrückt wurde, // dann möchte der Benutzer einen Eintrag auswählen oder den Hilfetext wieder ausblenden. if(g_pbButtons[TB_KEY_RETURN] || g_pbButtons[TB_KEY_NUMPADENTER] || g_pbButtons[TB_KEY_SPACE]) { if(!m_bShowingHelp) // Nun kommt es darauf an, was gerade ausgewählt ist! // -> Wenn Hilfetext = FALSE, Cursor navigieren! switch(m_iCursor) case 0: // Spiel starten g_pBreakanoid->SetGameState(GS_GAME); // Spielzustand auf GS_GAME setzen break; case 1: // Hilfe anzeigen m_bShowingHelp = TRUE; tbDelay(100); case 2: // Spiel beenden PostQuitMessage(0); }

Das Hauptmenü Move III else // Ist Hilfetext = TRUE, dann abschalten { // Die Hilfe wieder deaktivieren m_bShowingHelp = FALSE; tbDelay(100); } […]

Das Hauptmenü Sound I Sorgt für Töne beim Bewegen u. Betätigen des Cursors tbDirectSound-Klasse in Breakanoid initialisert! Breakanoid.h: Sound Array [12]  es gibt 12 unterschiedliche Sounds Breakanoid.cpp: Sounds werden in CBreakanoid::Load() geladen: // Sounds laden for(DWORD s = 0; s < 12; s++) { sprintf(acFilename, "Data\\Sound%d.wav", s + 1); m_apSound[s] = new tbSound; if(m_apSound[s]->Init(acFilename, DSBCAPS_STATIC | DSBCAPS_LOCDEFER | DSBCAPS_CTRLFREQUENCY)) // Fehler! TB_ERROR("Fehler beim Laden eines Sounds!", TB_ERROR); }

Das Hauptmenü Sound II MainMenu.cpp: CMainMenu::Move  spielt den Sound ab If-Abfrage: Verschieden Töne für versch. Eingaben: // Sound Nr.1 beim Drücken UP/DOWN if(g_pbButtons[TB_KEY_UP]) { g_pBreakanoid->m_apSound[0]->PlayNextBuffer(); m_iCursor--; tbDelay(80); } if(g_pbButtons[TB_KEY_DOWN]) m_iCursor++;