Im letzten Teil hast du den GM (GameManager) ausführlich kennengelernt, jetzt kommt der GS (GameState) dran.
Wenn du dir den Quellcode des GM bereits aufmerksam angeschaut hast, wird dir aufgefallen sein, daß von dort drei Funktionen eines GS aufgerufen werden: Enter
, Idle
und Leave
.
Enter
wird aufgerufen, sobald der GS aktiviert wird, Leave
beim Verlassen. Hier können Initialisierungen und Deinitialisierungen stattfinden, die nur in diesem speziellen GS benötigt werden. Zwar könnte man hierfür auch C’tor und D’tor verwenden, ich verfolge jedoch die Ideologie, daß im C’tor nur Variableninitialisierungen stattfinden sollten.
Idle
wird vom GM jedes Frame aufgerufen, dieser reicht hier die letzte Framezeit durch, damit der GS und seine Objekte zeitabhängige Berechnungen durchführen können.
Für den GS entwerfen wir eine Interface-Klasse, von dem die konkreten GSs abgeleitet werden müssen:
Datei /game/IGameState.h
#ifndef GAMESTATE_H #define GAMESTATE_H #include "Types.h" class CGameManager; class IGameState { public: IGameState( CGameManager* pGameManager ) : m_pGameManager( pGameManager ) {}; virtual void Enter() = 0; virtual void Idle( DWORD dwDeltaTime ) = 0; virtual void Leave() = 0; protected: CGameManager* m_pGameManager; }; #endif
Die drei Funktionen Enter
, Idle
und Leave
sind pure virtual, d.h. sie müssen von einem Derivat überschrieben werden. Der C’tor merkt sich die GM-Instanz. Es ist sicher, hier einen Zeiger zu übergeben, weil ein GS nur mit dem GM existieren kann.
Damit fehlt nur noch CGameStateFactory
, die in der GM-Implementierung schon ein paar mal ins Auge gefallen ist. Eine Factory (Fabrik) ist ebenfalls ein Entwurfsmuster, über das du in den weiterführenden Links mehr erfahren kannst. Prinzipiell ist die GameStateFactory nur für das Erstellen eines GS zuständig, der über einen enum
eindeutig identifiziert wird.
Datei /game/GameStateFactory.h
#ifndef GAMESTATEFACTORY_H #define GAMESTATEFACTORY_H // --------------------------------------------------------------------------- #include "GameState.h" // --------------------------------------------------------------------------- class CGameStateFactory { public: enum eGameState { GS_UNDEFINED, GS_GAME }; static IGameState* Create( eGameState eGS, CGameManager* pGameManager ); }; #endif
Datei /game/GameStateFactory.cpp
#include "GameStateFactory.h" #include "GameManager.h" #include "GSGame.h" // --------------------------------------------------------------------------- IGameState* CGameStateFactory::Create( eGameState eGS, CGameManager* pGameManager ) { switch( eGS ) { case GS_GAME: return new CGSGame( pGameManager ); } return 0; }
Und schon wieder gibt es hier eine neue Klasse, die wir bisher noch nicht kennen: CGSGame
. Aber du kannst dir denken, was das ist. Genau, in dieser Klasse wird das tatsächliche Spielgeschehen stattfinden, und zwar schon ab dem nächsten Teil des Tutorials.
Vorerst bleibt jedoch noch die Datei Types.h
offen. Hier definiere ich einige Datentypen, die C++ nicht kennt und die normalerweise in Headern wie windows.h
deklariert werden. Da wir diese Header natürlich zwecks Kompatibilität nicht benutzen möchten, definieren wir uns diese Datentypen einfach selbst.
Datei /Types.h
#ifndef TYPES_H #define TYPES_H typedef unsigned long DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; #endif
Weiterführende Links
- Das Factory Design-Pattern
http://www.google.de/search?q=factory+pattern+c%2B%2B
Hier schlagen wir zwei Fliegen mit einer Klappe: Mini-Lektion “Google benutzen” inklusive 😉
Aufgaben
- Füge den GameManager mit den Funktionsaufrufen
Initialize
,BeginFrame
,Idle
,EndFrame
undUninitialize
an den richtigen Stellen in dermain
Funktion ein. - Erstelle eine Klasse
CGSGame
, die vonIGameState
abgeleitet ist und implementiere drei leere FunktionenEnter
,Idle
undLeave
. - Bewege in der
Idle
-Funktion einen Pixel so über den Backbuffer, daß er sich 100 Pixel pro Sekunde bewegt. Benutze die FunktionenSDL_LockSurface
undSDL_UnlockSurface
, um direkt in den Backbuffer zu schreiben.