End of chapter 2 - Polymorphism (till Side 60 without making GameObject abstract...
(where it´s working/Comments unfortunately only in German :-)):
main.cpp
Code:
#include <Windows.h>
#include <iostream>
#include "Game.h"
// Globale Variablen
Game* g_game = 0; // Zeiger für ein zu erzeugendes Spielfeld-Objekt (mit 0 initialisiert)
int main(int argc, char* argv[])
{
// Instanz erstellen
g_game = new Game();
// Das Spielefenster dieser Instanz erstellen
if (g_game->init("Animation-TextureManager als Singelton", 100, 100, 640, 480, false)) // letzter Parameter true = Fullscreen, false = normales fenster
// Sinn ist, keine SDL-Anweisung in der main zu haben.
// Die Spielschleife
while (g_game->running())
{
g_game->handleEvents();
g_game->update(); // noch nicht benutzt, da keine Physics ausgeführt werden
g_game->render();
SDL_Delay(10); // --> Wird auf Seite 55 hinzugefügt, damit das Objekt sich nicht zu schnell bewegt
}
//Programm aufräumen und beenden
g_game->clean();
return 0;
}
GameObject.h
Code:
// GameObject.h
// inheritance = vererben
// derive = ableiten
#include <iostream> // im Buch nicht erwähnt, aber nötig wegen cout
#include "TextureManager.h" // im Buch nicht erwähnt, aber nötig um den Zeiger pRenderer der Funktion draw nutzen zu können
#ifndef __GameObject___
#define __GameObject___
class GameObject
{
public:
GameObject(){}
~GameObject(){}
virtual void load( //--> Seite 57 (virtual wird bei allen Funktionen dieser Klasse vorangesetzt) -->
int x, // Durch das "virtual" wird, sofern durch einen Zeiger aufgerufen,
int y, // die Definition des Objekts benutzt und nicht die, des Zeigers
int width,
int height,
std::string
textureID);
virtual void draw(
SDL_Renderer* pRenderer);
virtual void update();
// virtual void clean(); --> Auf Seite 60 hab ich erst gemerkt, das dieses rausgeschmissen
// werden muss. Wann hab ich nur die Funktion nach Game verlagert?
protected:
std::string m_textureID;
int m_currentFrame; // Spalte des Quell-Frames in der Grafikdatei
int m_currentRow; // Zeile des Quell-Frames in der Grafikdatei
int m_x; // x-Position auf dem Bildschirm, wo das Object platziert wird
int m_y; // y-Position auf dem Bildschirm, wo das Object platziert wird
int m_width; // Breite des Frames
int m_height; // Höhe des Frames
};
#endif
GameObject.cpp
Code:
#include "GameObject.h"
void GameObject::load(int x, int y, int width, int height, std::string
textureID)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
m_textureID = textureID;
m_currentRow = 1;
m_currentFrame = 1;
}
void GameObject::draw(SDL_Renderer* pRenderer)
{
TextureManager::Instance()->drawFrame(
m_textureID,
m_x,
m_y,
m_width,
m_height,
m_currentRow,
m_currentFrame,
pRenderer);
}
void GameObject::update()
{
m_x += 1;
m_currentFrame = int(((SDL_GetTicks() / 100) % 6)); // --> Seite 45 wurde dies bei Game::update eingefügt. Aber später vergessen
// in die Klasse GameObject mit zu übernehmen. Somit fand keine Animation
// von "GameObject m_go" statt. Im Gegensatz wird die Animation von
// "Player m_player" in Player::Update realisiert, da es ein Objekt der
// Kindklasse "Player" ist und nicht direkt von GameObjekt instanziiert
// wurde. Leider wurde dies im Buch völlig vergessen, so dass keine
// Animation zu sehen war.
}
Game.h
Code:
#ifndef __Game__ // Falls Game noch nicht definiert wurde.....
#define __Game__ // .... dann definiere Game so.....
#include<SDL.h> //Einbindung der SDL-Bibliothek
#include<SDL_image.h>
#include "TextureManager.h" // Texturemanager wird eingebunden --> Seite 45
#include "GameObject.h" // --> im Buch nicht erwähnt (muss ab Seite 54 hinzugefügt werden um Gameobject m_go zu erstellen)
#include "Player.h" // --> im Buch nicht erwähnt (muss ab Seite 54 hinzugefügt werden um Player m_player zu erstellen)
#include "Enemy.h" // --> im Buch nicht erwähnt (muss ab Seite 56 hinzugefügt werden um Player m_player zu erstellen)
#include <vector> // --> im Buch nicht erwähnt ( muss ab Seite 54 hinzugefügt werden, um einen Vector für Gameobjekte zu erstellen)
class Game
{
public:
Game() {} // Konstruktor
~Game() {} // Destruktor
//Klassenmember
GameObject* m_go; // --> Seite 58 (wird in einen Zeiger verwamdelt durch das '*'-Symbol
GameObject* m_player; //--> Seite 58 (wird hinzugefügt)
// Player m_player; --> muss ab Seite 56 ausdokumentiert werden, da sich das mit dem Zeiger "GameObject* m_player" beißt und in der
// Game.cpp dann auch "m_player" unterstrichen wird
// Neue Gameobjekte --> Seite 56
GameObject* m_enemy; // --> Wird erst auf Seite 59 hinzugefügt
GameObject* m_enemy1;
GameObject* m_enemy2;
GameObject* m_enemy3;
// Abbruchvariable (zum unterbrechen der Spielschleife)
bool running() { return m_bRunning; }
std::vector<GameObject*> m_gameObjects; // --> Seite 56 + 58 (Vorsicht!!! wurde vom Autor versehentlich 2 mal eingefügt)
// Funktionsliste
bool init(const char* title, int xpos, int ypos, int
width, int height, bool fullscreen);
void init();
void render();
void update();
void handleEvents();
void clean();
void draw(); // --> neue Funktion, die auf Seite 56 eingeführt wird (Laut buch, nur in Game.cpp. Das schreiben in die Game.h wurde nicht erwähnt)
// Diese Zeiger und Attribute können nur einmal erstellt werden, da Game nur einmal erstellt werden kann und von außerhalb
// kein Zugriff auf die privaten Attribute und Zeiger gestattet ist.
private:
SDL_Window* m_pWindow; // Ein Zeiger wird erstellt, dem das zu erstellende Fenster zugeordnet werden kann
SDL_Renderer* m_pRenderer; // Ein Zeiger wird erstellt, dem das zu erstellende Renderobjekt zugeordnet werden kann
// SDL_Texture* m_pTexture; // Ein Zeiger wird erstellt, dem ein Textureobjekt zugeordnet werden kann --> Wird rausgeschmissen, weil jetzt
// Über den Texturemanager geladen wird --> Seite 45
SDL_Rect m_sourceRectangle; // Ein Zeiger wird erstellt, dem ein Quellviereck für Grafik zugeordnet werden kann
SDL_Rect m_destinationRectangle; // Ein Zeiger wird erstellt, dem dem ein Zielviereck für Grafik auf dem virtuellen Bildschirm zugeordnet werden kann
bool m_bRunning;
int m_currentFrame;
TextureManager* Instance(); // Hier wird ein Objekt des TextureManagers erstellt --> Seite 44
};
#endif /* defined(__Game__) */
Game.cpp
Code:
#include "Game.h"
#include <iostream>
using namespace std;
bool Game::init(const char* title, int xpos, int ypos, int width,
int height, bool fullscreen)
{
// SDL soll initialisiert werden
if (SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
cout << "SDL wurde erfolgreich initialisiert" << endl;
// Zur unterscheidung zwischen Fullscreen oder Fenstermodus
int flags = 0;
if (fullscreen)
{
flags = SDL_WINDOW_FULLSCREEN;
}
// Fenster wird initialisiert
cout << "Fenster wird initialisiert" << endl;
m_pWindow = SDL_CreateWindow(title, xpos, ypos,
width, height, flags);
if (m_pWindow != 0) // Wenn Fensterinitialisierung erfolgreich wird der Renderer initialisiert
{
cout << "Fenster wurde erfolgreich initialisiert" << endl;
cout << "Renderer wird initialisiert" << endl;
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
if (m_pRenderer != 0) // Wenn die Rendererinitialisierung erfolgreich war
{
cout << "Renderer wurde erfolgreich initialisiert" << endl;
SDL_SetRenderDrawColor(m_pRenderer, // Hintergrundfarbe wird gesetzt (RGB/A)
255, 0, 0, 255);
if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG)
{
SDL_Quit();
return 1;
}
else
{
cout << "IMG_Init wurde erfolgreich mit PNG - Format initialisiert" << endl;
}
}
else
{
return false; // Rendererinitialisierung fehlgeschlagen
}
}
else
{
return false; // Fensterinitialisierung fehlgeschlagen
}
}
else
{
return false; // SDLinitialisierung fehlgeschlagen
}
cout << "Alles initialisiert" << endl;
if (!TheTextureManager::Instance()->load("Data/animate-alpha.png",
"animate", m_pRenderer))
{
return false;
cout << "Fehler beim Datei Laden" << endl;
}
// m_go.load(100, 100, 128, 82, "animate"); // --> Seite 54 --> "animate" = TextureID --> wurde wieder eintfernt beim Kapitel Polymorphismus
// m_player.load(300, 300, 128, 82, "animate"); // --> kann aufgrund des gleichen Namens wie des eingeführten Zeigers auf Seite 56 so nicht mehr verwendet werden
m_go = new GameObject(); // --> Seite 58
m_player = new Player(); // --> Seite 56 und 58 (wurde vom Autor versehentlich zweimal beschrieben)
m_enemy = new Enemy(); // --> wird erst auf Seite 59 hinzugefügt
m_enemy1 = new Enemy(); // --> Seite 56 (Zuvor muss aber erst eine Enemy.h erstellt werden, in Game.h
m_enemy2 = new Enemy(); // includet und eine Klasse Enemy erstellt werden, die von GameObject erbt
m_enemy3 = new Enemy(); // (nicht im Buch)). Diese ist erstmal exakt aufgebaut, wie die Klasse Player
// --> Seite 58 --> Objekte Laden
m_go->load(100, 100, 128, 82, "animate");
m_player->load(300, 300, 128, 82, "animate");
m_enemy->load(0, 0, 128, 82, "animate"); // --> wird erst auf Seite 59 hinzugefügt
// --> Die verschiedenen Gameobjekte werden in das Vektorarray geschoben
m_gameObjects.push_back(m_go); // --> Seite 58
m_gameObjects.push_back(m_player); // --> Seite 56 und 58 (wurde vom Autor versehentlich zweimal beschrieben)
m_gameObjects.push_back(m_enemy); // --> wird erst auf Seite 59 hinzugefügt
m_gameObjects.push_back(m_enemy1); // --> Seite 56
m_gameObjects.push_back(m_enemy2); // --> Seite 56
m_gameObjects.push_back(m_enemy3); // --> Seite 56
m_bRunning = true; // Initialisierungen waren alle erfolgreich
//Loop-Bool wird auf true gesetzt für die Spielschleife
return true;
} // Ende der Initialisierung
// --> Seite 56 --> neue eingeführte Funktion (es werden alle Objekte des Arrays
// durchlaufen und gemalt. Wichtig ist nur, das sie von Gameobject abstammen (Polymorphismus)
void Game::draw()
{
for (std::vector<GameObject*>::size_type i = 0; i !=
m_gameObjects.size(); i++)
{
m_gameObjects[i]->draw(m_pRenderer);
}
}
TextureManager.h
Code:
#ifndef __TextureManager__
#define __TextureManager__
#include <iostream> // --> im Buch nicht erwähnt
#include <string> // --> im Buch nicht erwähnt
#include <map> // --> im Buch nicht erwähnt
#include "SDL.h" // --> im Buch nicht erwähnt
class TextureManager
{
public:
// --> eingeführt --> Seite 42
bool load( // Funktion zum Laden der Grafikdatei
std::string fileName, //Filename
std::string id, //ID- der zu Rendernden Texture
SDL_Renderer* pRenderer); //verwendeter Renderer
// draw --> eingeführt --> Seite 42
void draw( // Funktion zum Malen eines feststehenden Frames auf den virtuellen Bildschirm
std::string id, //ID- der zu Rendernden Texture
int x, // Destination-X
int y, // Destination-Y
int width, // Die Breite des zu benutzenden Frames
int height, // Die Höhe des zu benutzenden Frames
SDL_Renderer* pRenderer, // Der zu benutzende Renderer
SDL_RendererFlip flip = SDL_FLIP_NONE); // Flag zur Angabe, ob das Frame gespiegelt werden soll
// drawframe --> eingeführt --> Seite 42
void drawFrame(std::string id, int x, int y, int width, int
height, int currentRow, int currentFrame, SDL_Renderer*
pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
// Funktion zur erzeugung der Singelton-Klasse des Texturemanagers --> Seite 46
// Nur wenn noch keine Instanz des Texturemanagers exisitiert, wird eine erzeugt. Ansonsten
// wird die bereits erzeugte zurück gegeben.
static TextureManager* Instance()
{
if (s_pInstance == 0)
{
s_pInstance = new TextureManager();
return s_pInstance;
}
return s_pInstance;
}
private:
TextureManager() {} // --> Um den Texturemanager zu einem Singelton zu machen wird zuerst der Konstruktor
// von public zu privat verschoben, somit kann er nur über die Instancefunction
// aufgerufen werden --> Seite 46
~TextureManager() {} // --> im Buch nicht erwähnt
static TextureManager* s_pInstance; // --> im Buch nicht erwähnt, aber notwendig S.46
// --> eingeführt --> Seite 42
std::map<std::string, SDL_Texture*> m_textureMap; // Ein container der unsere zu Rendernden Objekte enthält.
// gespeichert wird immer ein Wertepaar. Die Objektreferenz
// und ein eindeutiger Schlüssel (ID)
// Speicherart ist üblicherweise ein Red-Black-Tree,
// ein selbstregulierender binärer Suchbaum.
};
typedef TextureManager TheTextureManager; // --> Seite 46 --> Hier wird ein Aliasname vergeben und überall,
// wo man Texturemanager schreiben müsste, kann man
// jetzt genauso TheTextureManager schreiben --> neu zu Texturemanager goes Singelton
#endif
TextureManager.cpp
Code:
#include "TextureManager.h"
#include "SDL_image.h"
#include "SDL.h"
TextureManager* TextureManager::s_pInstance = 0; // --> eingeführt S.46
// --> eingeführt --> Seite 43
bool TextureManager::load( //Funktion zum Laden der Grafikdatei und zum zuordnen einer ID in unserem red-black-tree-container (map)
std::string fileName, // Filename
std::string id, // zugeordnete FileID im red-black-tree
SDL_Renderer* pRenderer) // zu verwendender Renderer
{
SDL_Surface* pTempSurface = IMG_Load(fileName.c_str()); // File wird geladen und ist unter dem Zeiger pTempSurface zu erreichen
if (pTempSurface == 0) // Wenn nicht ladbar
{
std::cout << IMG_GetError(); // Gebe Fehler aus
return false;
}
SDL_Texture* pTexture = // erstelle Zeiger auf ein Texture-Objekt
SDL_CreateTextureFromSurface(pRenderer, pTempSurface); // erstelle das Texture-Objekt, das dem Zeiger pTexture zugeordnet wird
SDL_FreeSurface(pTempSurface);
// Datei wurde Ordnungsgemäß geladen und wird nachfolgend unserem red-black-tree-container hinzugefügt
if (pTexture != 0)
{
m_textureMap[id] = pTexture;
return true;
}
// Wenn nicht geladen werden konnte
return false;
}
// --> eingeführt --> Seite 43
void TextureManager::draw( // Funktion zum Malen eines feststehenden Frames auf den virtuellen Bildschirm
std::string id, //ID- der zu Rendernden Texture
int x, // Destination-X
int y, // Destination-Y
int width, // Die Breite des zu benutzenden Frames
int height, // Die Höhe des zu benutzenden Frames
SDL_Renderer* pRenderer, // Der zu benutzende Renderer
SDL_RendererFlip flip) // Flag zur Angabe, ob das Frame gespiegelt werden soll
{
SDL_Rect srcRect; // Quell-Viereck wird erstellt
SDL_Rect destRect; // Ziel-Viereck wird erstellt
srcRect.x = 0; // x-Position des oberen linken Ecks des Quellrechtecks
srcRect.y = 0; // y-Position des oberen linken Ecks des Quellrechtecks
srcRect.w = destRect.w = width; // Breite des Quell- und Ziel-Rechtecks
srcRect.h = destRect.h = height; // Höhe des Quell- und Ziel-Rechtecks
destRect.x = x; // x-Position des oberen linken Ecks des Zielrechtecks auf dem virtuellen Bildschirm
destRect.y = y; // x-Position des oberen linken Ecks des Zielrechtecks auf dem virtuellen Bildschirm
SDL_RenderCopyEx( // Zu verwendender Renderer
pRenderer,
m_textureMap[id], // ID des Grafikobjekts
&srcRect, // Zeiger auf Quell-Rechteck
&destRect, // Zeiger auf Ziel-Rechteck
0,
0,
flip);
}
// --> eingeführt --> Seite 44
void TextureManager::drawFrame(
std::string id, //ID- der zu Rendernden Texture
int x, // Destination-X
int y, // Destination-Y
int width, // Die Breite des zu benutzenden Frames
int height, // Die Höhe des zu benutzenden Frames
int currentRow, // Reihe des zu Rendernden Frames in Source-Grafik-Datei --> Unterschied zum feststehenden Frame
int currentFrame, // Spalte (das wievielte Frame in der Reihe) des zu Rendernden Frames in Source-Grafik-Datei --> Unterschied zum feststehenden Frame
SDL_Renderer *pRenderer,
SDL_RendererFlip flip)
{
SDL_Rect srcRect; // Quell-Viereck wird erstellt
SDL_Rect destRect; // Ziel-Viereck wird erstellt
srcRect.x = width * currentFrame; // x-Position des oberen linken Ecks des Quellrechtecks --> Unterschied zum feststehenden Frame
srcRect.y = height * (currentRow - 1); // y-Position des oberen linken Ecks des Quellrechtecks --> Unterschied zum feststehenden Frame
srcRect.w = destRect.w = width; // Breite des Quell- und Ziel-Rechtecks
srcRect.h = destRect.h = height; // Höhe des Quell- und Ziel-Rechtecks
destRect.x = x; // x-Position des oberen linken Ecks des Zielrechtecks auf dem virtuellen Bildschirm
destRect.y = y; // x-Position des oberen linken Ecks des Zielrechtecks auf dem virtuellen Bildschirm
SDL_RenderCopyEx( // Zu verwendender Renderer
pRenderer,
m_textureMap[id], // ID des Grafikobjekts
&srcRect, // Zeiger auf Quell-Rechteck
&destRect, // Zeiger auf Ziel-Rechteck
0,
0,
flip);
}
Enemy.h
Code:
// Enemy.h
// abgeleitet (derived) von GameObject
#include <iostream> // im Buch nicht erwähnt, aber nötig wegen cout
#include "GameObject.h" // im Buch nicht erwähnt aber notwendig, da sonst nicht von GameObject geerbt werden kann
#ifndef __Enemy__
#define __Enemy__
class Enemy : public GameObject // inherit from GameObject
{
public:
Enemy(){}
~Enemy(){}
void load(int x, int y, int width, int height, std::string
textureID);
void draw(SDL_Renderer* pRenderer);
void update();
void clean();
Enemy.cpp
Code:
#include "Enemy.h"
// Wird erst auf Seite 59 eingefügt
void Enemy::load(
int x,
int y,
int width,
int height,
std::string textureID) // Fehler im Buch (vor dem string fehlt 'std::'
{
GameObject::load(x, y, width, height, textureID);
}
void Enemy::draw(SDL_Renderer* pRenderer)
{
GameObject::draw(pRenderer);
}
void Enemy::update()
{
m_y += 1;
m_x += 1;
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
}
Player.h
Code:
// Player.h
// abgeleitet (derived) von GameObject
#include <iostream> // im Buch nicht erwähnt, aber nötig wegen cout
#include "GameObject.h" // im Buch nicht erwähnt aber notwendig, da sonst nicht von GameObject geerbt werden kann
#ifndef __Player__
#define __Player__
class Player : public GameObject // inherit from GameObject
{
public:
Player(){}
~Player(){}
void load(int x, int y, int width, int height, std::string
textureID);
void draw(SDL_Renderer* pRenderer);
void update();
void clean();
};
#endif /* defined(Player) */
Player.cpp
Code:
#include "Player.h"
void Player::load(
int x,
int y,
int width,
int height,
std::string textureID) // Fehler im Buch (vor dem string fehlt 'std::'
{
GameObject::load(x, y, width, height, textureID);
}
void Player::draw(SDL_Renderer* pRenderer)
{
GameObject::draw(pRenderer);
}
void Player::update()
{
m_x += 2;
m_currentFrame = int(((SDL_GetTicks() / 100) % 6)); // --> wurde im Buch auf Seite 53 vergessen.
// Wird dies nicht eingefügt, gibt es keine
// Animation von "Player m_player", welches
// in Game.h ein Klassenmember ist. Im
// Gegensatz wird die Animation von
// "GameObject m_go" in GameObject::Update
// realisiert. Leider wurde dies im Buch
// völlig vergessen, so dass keine
// Animation zu sehen war.
}
a lot of Code.. I know :-).. Just if you want to copy and paste, to try and verify, with what I was doing.. following I will Post my Damn-Mistake-Version of implementing chapter three.. But don´t laugh.. You will find a lot of mistakes I made, for sure.. Just If you want to try.. I would understand, if you have better things to do as correcting so many mistakes :-)
Best Regards
Sonntag69