====== Pong ====== ===== Klasse erstellen ===== Nun brauchen wir Klassen (oder Strukturen; ich werde Klassen benutzen), in denen wir das Bild und die Position der Bilder halten. Wir könnten natürlich alles direkt als Variablen in der main-function speichern, aber es wird sich zeigen, dass es der Einfachheit und Übersicht hilft, wenn man das ganze in Klassen zusammenfasst. Da wir bereits eine pong.h besitzen, können wir in dieser direkt weiter schreiben: class Sprite { privat: SDL_Rect position; SDL_Surface* bmp; Sprite(); }; So sollte es zuerst einmal aussehen. Denn an diesem Punkt sollte uns schon ein Problem auffallen. Wem es nicht auffällt, der soll bitte hier stoppen und sich einmal ein paar Sekunden den Kopf darüber zerbrechen. Das Problem ist, dass wir zwar SDL_Rect und SDL_Surface deklarieren, aber die Headerdatei kein SDL kennt. Also müssen wir dieses erst include, bevor es weiter gehen kann. Also kommt das oben in die //pong.h// #ifdef __APPLE__ #include #else #include #endif Und da wir gerade einen schönen Breakpoint haben, kann ich direkt ein weiteres Problem ansprechen, worauf wir später stoßen werden: Die Pongs bewegen sich nur auf der Y-Achse, der Ball jedoch auf X und Y. Deshalb brauch der Ball einen [[theory:math:vectoranalysis:arithmetics|Vektor]]! Und ein weiteres Merkmal fällt uns auf: Der Ball braucht mehr Eigenschaften als ein Pong, hat aber im Grunde die gleichen. Deshalb sollte man für den Ball eine neue Klasse erstellen und die Sprite Klasse hineinerben. Bevor ich weiter auf [[theory:math:vectoranalysis:arithmetics|Vektoren]] eingehe, schreiben wir erstmal beide Klasse in einen annehmbaren Zustand. class Sprite { protected: // nicht mehr privat // Variablen SDL_Rect position; public: // eigentlich könnte alles public sein, da das Projekt relativ klein ist // Variablen SDL_Surface *bmp; // Constructor Sprite() { bmp = SDL_LoadBMP( "pong.bmp" ); // ein Konstruktor ist nicht vererbbar } // Functions inline SDL_Rect* get_position( void ) { return &position; } virtual void set_position( int x, int y ) { position.x = x; position.y = y; } virtual void update_position( int y ) // Nur y, da Spieler nur auf der Y Achse sich bewegen { position.y += y; } }; // Ball braucht Vektoren, deshalb eine neue Klasse class Ball : public Sprite // Ball erbt von Sprite { public: // Variables bool moving; // ob der Ball sich bereits bewegt oder nicht // -- hier kommen später unsere Vektoren -- // void set_position( int x, int y ) // oben nur virtual, weil wir hier die Änderung auch in unseren Vektor eintragen muss { position.x = x; position.y = y; // Vektor wird zu x & y } void update_position( int x, int y ) // oben nur virtual, weil wir hier die Änderung auch in unseren Vektoren eintragen mus { position.x += x; position.y += y; // Auf Vektor wird x & y addiert } // Constructor Ball() { bmp = SDL_LoadBMP( "ball.bmp" ); moving = false; // am Anfang steht der Ball position.x = SCREEN_WIDTH / 2 - bmp->w / 2; // Ball wird zentriert auf der X Achse position.y = SCREEN_HEIGHT / 2 - bmp->h / 2; // Ball wird zentriert auf der Y Achse // Vektor ebenfalls auf diesen Wert setzen } }; Die Kommentare mit den Vektoren werden später durch die richtigen Anweisungen ersetzt. Aber nun haben wir schonmal 2 Klassen. Das ist ein guter Ansatz. Diese können wir nun in unserem Hauptprogramm vor der Hauptschleife deklarieren. //main.cpp// Sprite ping, pong; // Ping links, Pong rechts; Auch wenn man im Spiel eigentlich Pong ist Ball ball; // Beim Ball wird folgendes vom Konstruktor übernommen: // Dies ist hier nicht möglich, da Ping und Pong andere Startpositionen haben ping.set_position( 0, SCREEN_HEIGHT / 2 - ping.bmp->h / 2 ); // linker Bildschirmrand in der Mitte pong.set_position( SCREEN_WIDTH - pong.bmp->w, SCREEN_HEIGHT / 2 - pong.bmp->h / 2 ); // rechter Bildschirmrand in der Mitte Es wird auffallen, dass Ping und Pong direkt am Rand vom Bildschirm sind. Dies sind sehr unschön aus. Deshalb definieren wir in der //pong.h// folgendes: #define DEFAULT_GAP 24 // gap between players and screen end in px und editieren die set_position Parameter wie folgt: ping.set_position( DEFAULT_GAP, SCREEN_HEIGHT / 2 - ping.bmp->h / 2 ); // linker Bildschirmrand in der Mitte pong.set_position( SCREEN_WIDTH - pong.bmp->w - DEFAULT_GAP, SCREEN_HEIGHT / 2 - pong.bmp->h / 2 ); // rechter Bildschirmrand in der Mitte Nun zurück zu den [[theory:math:vectoranalysis:arithmetics|Vektoren]]. Um für unseren Ball [[theory:math:vectoranalysis:arithmetics|Vektoren]] zu nutzen, sollte wir eine Klasse für [[theory:math:vectoranalysis:arithmetics|Vektoren]] schreiben.\\ Zuerst einmal wird eine //vector.h// erstellt. Die sieht so aus: #ifndef VECTOR_H_INCLUDED #define VECTOR_H_INCLUDED #endif VECTOR_H_INCLUDED In dieser //vector.h// erstellen wir unsere Klasse. Ich nenne sie mal //CVector//((Class Vector)). Nun müssen wir uns überlegen, was unser [[theory:math:vectoranalysis:arithmetics|Vektor]] können muss. Er muss einen x sowie einen y Wert halten. Außerdem muss er mit diesen rechnen können. Dazu fällt uns spontan [[cpp:operator:overload|Operator Overload]] ein! Versucht zuerst einmal selbst die Klasse zu schreiben. Wenn ihr fertig seid könnt ihr mit meiner vergleichen: class CVector { public: // Variables int x, y; // Koordinaten // Constructor CVector() { x = y = 0; }; CVector( int, int ); // Operator Overloads CVector operator + ( CVector ); CVector operator - ( CVector ); CVector operator = ( CVector ); }; CVector::CVector( int x, int y ) { this->x = x; this->y = y; } CVector CVector::operator+ ( CVector param ) { CVector temp; temp.x = this->x + param.x; temp.y = this->y + param.y; return( temp ); } CVector CVector::operator- ( CVector param ) { CVector temp; temp.x = this->x - param.x; temp.y = this->y - param.y; return( temp ); } CVector CVector::operator= ( CVector param ) { this->x = param.x; this->y = param.y; return( param ); } So in der Art sollte eure Klasse aussehen. Nun müssen wir noch die //pong.h// editieren, weil wir dort die [[theory:math:vectoranalysis:arithmetics|Vektoren]] in die Ball Klasse hinzufügen müssen. Zuerst sollten wir aber __#include "vector.h"__ in unsere //pong.h// hinzufügen! // Ball braucht Vektoren, deshalb eine neue Klasse class Ball : public Sprite // Ball erbt von Sprite { privat: // da wir current_coord mit "update_position" zusammen mit position verändern // Variables CVector current_coord; // derzeitige Position vom Ball public: // Variables bool moving; // ob der Ball sich bereits bewegt oder nicht CVector final_coord; // Ziel des Balls void set_position( int x, int y ) // oben nur virtual, weil wir hier die Änderung auch in unseren Vektor eintragen muss { position.x = current_cord.x = x; position.y = current_cord.y = y; } void set_position( CVector new ) { current_cord = new; position.x = new.x; position.y = new.y; } void update_position( int x, int y ) // oben nur virtual, weil wir hier die Änderung auch in unseren Vektoren eintragen mus { position.x += x; position.y += y; current_cord.x += x; current_cord.y += y; } void update_position( CVector temp ) { current_cord = current_cord + temp; position.x += temp.x; position.y += temp.y; } // Constructor Ball() { bmp = SDL_LoadBMP( "ball.bmp" ); moving = false; // am Anfang steht der Ball position.x = current_cord.x = SCREEN_WIDTH / 2 - bmp->w / 2; // Ball wird zentriert auf der X Achse position.y = current_cord.y = SCREEN_HEIGHT / 2 - bmp->h / 2; // Ball wird zentriert auf der Y Achse } }; So in der Richtung sollte nun unsere Klasse aussehen. Ist dies der Fall können wir weiter zum [[sdl:pong:loadimages|nächsten Kapitel]].