====== 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]].