Teil 5: Managing A Game II

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

Aufgaben

  1. Füge den GameManager mit den Funktionsaufrufen Initialize, BeginFrame, Idle, EndFrame und Uninitialize an den richtigen Stellen in der main Funktion ein.
  2. Erstelle eine Klasse CGSGame, die von IGameState abgeleitet ist und implementiere drei leere Funktionen Enter, Idle und Leave.
  3. Bewege in der Idle-Funktion einen Pixel so über den Backbuffer, daß er sich 100 Pixel pro Sekunde bewegt. Benutze die Funktionen SDL_LockSurface und SDL_UnlockSurface, um direkt in den Backbuffer zu schreiben.

No Comments

Cancel