Teil 10: Geordnetes Chaos I

Wollten wir das bisherige Vorgehen fortsetzen, und alle Spielobjekte in CGSGame anlegen, würde das früher oder später im Chaos enden.

Massenhaft verschiedene Variablen und Arrays/Listen würden nicht nur die Lesbarkeit des Headers beeinflussen. Und letztendlich haben wir ja einen Vorteil: Alle Spielobjekte, sei es der eigene Gleiter, ein Feind oder Weltraumschrott, können von der selben Basisklasse abgeleitet werden. Was liegt also näher, als alle in einer einzigen Liste zu verwalten?

Wir werden hierzu einen STL Vektor verwenden. Einen Vektor kann man sich ähnlich einem Array vorstellen: Alle Elemente liegen linear hintereinander im Speicher, und ein Zugriff per [] Operator ist möglich. Der STL Vektor erweitert das Array allerdings um die Funktionalität einer verketteten Liste, man kann also recht einfach Einträge anhängen oder entfernen. Neben dem Zugriff über [] gibt es eine weitere Möglichkeit, auf die Elemente zuzugreifen: Den sogenannten Iterator. Ein Iterator ist einfach umschrieben nichts weiter als ein Zeiger auf ein Listenelement. Wozu, wenn man doch den [] Operator hat, werden einige jetzt Fragen? Ein Iterator wird z.B. dann benötigt, wenn ein Element aus der Liste gelöscht werden soll – somit fällt das lästige Umkopieren der nachfolgenden Elemente weg. Der Vektor ist, wie die Abkürzung STL = Standard Template Library schon suggeriert, als Template implementiert, d.h. er kann jeden beliebigen Datentyp aufnehmen. Aber ein Beispiel sagt mehr als tausend Worte:

#include <vector> // In diesem Header ist die vector-Template-Klasse deklariert - KEIN .h

// Vektor von int's - die Klasse "vector" steht im Namespace "std"
std::vector< int > meinVektor;

// Vektor mit Werten 0 - 90 füllen
for( int i = 0; i < 10; ++i )
{
   meinVektor.push_back( i * 10 );
}

// Vektor per [] Operator durchlaufen und Werte ausgeben
for( int i = 0; i < meinVektor.size(); ++i )
{
   printf( "Wert an Stelle %d: %d\n", i, meinVektor[ i ] );
}

// Vektor per iterator durchlaufen
std::vector< int >::iterator it; // Die Klassendeklaration des Iterators steht im public Bereich der Vektorklasse
it = meinVektor.begin(); // Den iterator auf den Anfang der Liste setzen
while( it != meinVektor.end() ) // solange nicht das Ende des Vektors erreicht ist
{
   int i = *it; // über den Dereferenz-Operator "*" holt man den Inhalt des Iterators
   printf( "Wert: %d\n", i );
   ++it; // weiter zum nächsten Element
}

// Einen Wert "iSuchWert" aus dem Vektor löschen
std::vector< int >::iterator it;
while( it != meinVektor.end() )
{
   if( *it == iSuchWert )
   {
       it = meinVektor.erase( it ); // aktuelles Element löschen; erase gibt einen neuen gültigen Iterator zurück
   }
   else
   {
       ++it;
   }
}

// Vektor leeren
meinVektor.clear();

Wir werden einen solchen Vektor also von unserer CEntity Basisklasse erstellen. Da wir unsere Spielobjekte dynamisch anlegen werden, verwenden wir konkret Zeiger auf CEntity in der Liste. Damit wir nicht ständig std::vector< CEntity* > tippen müssen, fügen wir noch ein typedef nach der Klassendeklaration von CEntity in Entity.h ein:

Datei /objects/Entity.h:

#ifndef ENTITY_H
#define ENTITY_H

#include "Types.h"
#include <vector>

// ---------------------------------------------------------------------------
class CEntity
{
public:
  CEntity();
  CEntity( const float fX, const float fY );
  virtual ~CEntity();

  virtual void Initialize() = 0;
  virtual void Uninitialize() = 0;
  virtual void Draw() = 0;
  virtual void Idle( DWORD dwDeltaTime ) = 0;

  void SetPosition( const float fX, const float fY );
  void GetPosition( float& fX, float& fY ) const;

protected:
  float m_fX;
  float m_fY;
  bool m_bDestroy; // NEU
};

// ---------------------------------------------------------------------------
typedef std::vector< CEntity* > vecEntityPtr;

#endif

Das Verständnis des STL-Vektors ist für den folgenden Teil essentiell, denn wir werden massiv davon gebrauch machen, um unsere Spielobjekte zu verwalten.

Wer genau hinschaut, entdeckt ausserdem ein neues Flag “m_bDestroy” im Header. Dieses Flag wird unserem Manager verraten, ob das Objekt gelöscht werden kann. Ist das also für den Spieler-Gleiter auf “true“, sieht es ziemlich schlecht für ihn aus …

Mehr davon gibt’s im zweiten Teil.

No Comments

Cancel