Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Das erste Spiel Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung WS 12/13 Übung: Visuelle Programmierung I – Simulation.

Ähnliche Präsentationen


Präsentation zum Thema: "Das erste Spiel Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung WS 12/13 Übung: Visuelle Programmierung I – Simulation."—  Präsentation transkript:

1 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

2 Gliederung 1. Das Spielprinzip 2. Das Spielgerüst 3. Der Code Die Grundklasse CBreakanoid Das Titelbild Das Hauptmenü Sound

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

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

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

6 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

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

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

9 3. Der Code

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

11 Variablen I // CBreakanoid-Klasse class CBreakanoid { public: // Variablen 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 EGameStatem_GameState;// Speichern des Aktuellen Spielzustands floatm_fTime;// Stoppuhr = zählt wie viele Sek. Zustand schon aktiv ist [...]

12 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

13 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

14 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; }

15 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); }

16 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); }

17 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

18 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; }

19 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; }

20 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; }

21 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; }

22 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; }

23 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(); TB_SAFE_DELETE(g_pBreakanoid); MessageBox(NULL, "Fehler beim Initialisieren des Spiels!", "Fehler", MB_OK | MB_ICONEXCLAMATION); return 1; }

24 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(); TB_SAFE_DELETE(g_pBreakanoid); return 0; }

25 3.2 Das Titelbild

26 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 };

27 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]

28 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); return TB_OK; }

29 Das Titelbild Rendern I //Intro.cpp // Vertizes für das Titelbild struct STitleVertex { tbVector3vPosition; floatfRHW; D3DCOLORColor; tbVector2vTex0; static const DWORDdwFVF; }; const DWORD STitleVertex::dwFVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1; Rendern erfolgt durch ein Rechteck, das mit der Textur des Bildes überzogen wird  ‚Transformierte Vertizes‘

30 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);

31 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 […]

32 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 […]

33 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; }

34 3.3 Das Hauptmenü

35 Das Hauptmenü Variablen // MainMenu.h class CMainMenu { public: // Variablen LPDIRECT3DTEXTURE9m_pBackground;// speichert Hintergrundbild Intm_iCursor;// Menücursor = merkt sich Ausgewählten der 3 Einträge. [0]=erster. BOOLm_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 {}

36 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

37 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) { SBackgroundVertexaVertex[4]; char*apcMenuEntry[3] = {"Spiel starten", "Hilfe anzeigen", "Spiel beenden"}; tbVector2vPosition; tbColorColor; […]

38 Das Hauptmenü Render II If-Abfrage prüft, ob Hilfetext oder Menüeinträge gezeichnet werden sollen 1. 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 Einheiten nach unten verschieben: vPosition.y = 0.4f + (float)(iEntry) * 0.125f; …

39 Das Hauptmenü Render III 2. 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); 3. 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 3. 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));

40 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; […]

41 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); break; case 2:// Spiel beenden PostQuitMessage(0); break; }

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

43 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); }

44 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]) { g_pBreakanoid->m_apSound[0]->PlayNextBuffer(); m_iCursor++; tbDelay(80); }


Herunterladen ppt "Das erste Spiel Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung WS 12/13 Übung: Visuelle Programmierung I – Simulation."

Ähnliche Präsentationen


Google-Anzeigen