1 / 44

Das erste Spiel

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

tod
Download Presentation

Das erste Spiel

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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 • Das Spielprinzip • Das Spielgerüst • 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) • 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

  5. 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

  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: • 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

  8. 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

  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 EGameState m_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. HauptfunktionWinMain 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. HauptfunktionWinMain 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 TitelbildCIntro // 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 TitelbildDie 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 TitelbildInitialisieren, 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 TitelbildRendern I //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; • Rendern erfolgt durch ein Rechteck, das mit der Textur des Bildes überzogen wird  ‚Transformierte Vertizes‘

  30. Das TitelbildRendern 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 TitelbildRendern 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 TitelbildRendern 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 TitelbildMove // 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 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 {}

  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) { SBackgroundVertex aVertex[4]; char* apcMenuEntry[3] = {"Spiel starten", "Hilfe anzeigen", "Spiel beenden"}; tbVector2 vPosition; tbColor Color; […]

  38. 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; …

  39. 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));

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

More Related