Game State Management in C ++

Ursprünglicher Autor: Antony Lewis
  • Übersetzung
Hallo liebe Leser!

Wir verbreiten aktiv die dritte zusätzliche Ausgabe des äußerst erfolgreichen Buches „Learning C ++ through Game Programming“ . Aus diesem Grund bieten wir Ihnen heute die Übersetzung eines interessanten Artikels zu einem der engen Themen im Zusammenhang mit Programmierspielen in C ++ an. Wir bitten Sie auch, an der Umfrage teilzunehmen:

Zum ersten Mal habe ich vor vielen Jahren einen Eindruck von den verschiedenen Zuständen des Spiels bekommen, als ich mir eine Demo angesehen habe. Es war keine „Vorschau auf das kommende Spiel“, sondern etwas Altmodisches , „von scene.org". Auf die eine oder andere Weise gingen solche Demos völlig unmerklich von einem Effekt zum anderen über. Von einigen zweidimensionalen Wirbeln könnte das Spiel sofort zur komplexen Darstellung einer dreidimensionalen Szene übergehen. Ich erinnere mich, es schien mir, dass zur Implementierung dieser Effekte mehrere separate Programme gleichzeitig erforderlich sind.

Mehrere Status sind nicht nur in Demos wichtig, sondern auch in allen Spielen. Jedes Spiel beginnt mit einem Begrüßungsbildschirm und öffnet dann ein bestimmtes Menü, nach dem das Spiel beginnt. Wenn Sie endgültig besiegt sind, geht das Spiel in einen "Spielende" -Zustand über, gefolgt von einer Rückkehr zum Menü. In den meisten Spielen können Sie sich gleichzeitig in zwei oder mehr Zuständen befinden. Während des Spiels können Sie beispielsweise normalerweise ein Menü öffnen.

In der Regel werden mehrere Zustände mit einer Reihe von Anweisungen verarbeitet.if
, Schalter und Schleifen. Das Programm startet mit einem Begrüßungsbildschirm und bleibt in diesem Zustand, bis eine Taste gedrückt wird. Dann wird das Menü angezeigt, es bleibt auf dem Bildschirm, bis Sie eine Auswahl treffen. Dann beginnt das Gameplay in Form eines Zyklus, bis das Spiel vorbei ist. Zu jeder Zeit innerhalb des Spielzyklus sollte das Programm überprüfen, was zu tun ist - zeichnen Sie ein Menü oder zeigen Sie einfach das nächste Bild an. Darüber hinaus sollte der Teil des Programms, der mit der Ereignisverarbeitung beschäftigt ist, prüfen, ob Benutzereingaben den Status des Menüs oder das Spiel als solches beeinflussen. All dies summiert sich zu dem Hauptzyklus, der sehr schwer zu verfolgen ist, was bedeutet, dass es schwierig ist, Fehler zu beheben und zu warten.

Was ist eine Bedingung?

Wie oben erwähnt, ist der Zustand fast ein eigenständiges Programm im Spiel. In jedem Zustand werden Ereignisse auf ihre eigene Weise verarbeitet, und ihre Elemente werden auf dem Bildschirm gezeichnet. In jedem Zustand werden seine eigenen Ereignisse verarbeitet, die Spielwelt aktualisiert und das nächste Bild auf dem Bildschirm gezeichnet. Wir haben drei Methoden identifiziert, die in unserer Zustandsklasse enthalten sein sollten.

Außerdem sollte der Spielstatus in der Lage sein, Grafiken zu laden und sich selbst zu initialisieren sowie unnötige Ressourcen zu entfernen, wenn die Aufgabe gelöst ist. Es passiert, wenn wir einen Zustand anhalten und später wieder aufnehmen möchten. Zum Beispiel möchten wir das Spiel kurz unterbrechen, um ein Menü anzuzeigen. Bisher haben wir eine solche Klasse von Spielstatus:

class CGameState
{
public:
  void Init();
  void Cleanup();
  void Pause();
  void Resume();
  void HandleEvents();
  void Update();
  void Draw();
};


Ein solches Schema sollte alle unsere Bedürfnisse in Bezug auf den Spielstatus vollständig erfüllen. Es stellt sich heraus, dass es eine schöne Basisklasse gibt, von der wir andere erben können, die den einzelnen Status des Spiels entsprechen - Begrüßungsbildschirm, Menü, Gameplay usw.

Zustandsmanager

Als Nächstes müssen Sie einen Mechanismus zur Verwaltung dieser Zustände entwickeln - einen Zustandsmanager. In meinem Code ist der State Manager Teil der Game Engine. Ein anderer Programmierer könnte eine separate Klasse für den State Manager erstellen, aber es schien mir einfacher zu sein, sie direkt zur Engine hinzuzufügen. Auch hier können Sie speziell herausfinden, was die Game Engine tun soll, und dann eine Klasse dafür schreiben, die genau diese Funktionen implementiert.

In unserem einfachen Beispiel muss die Engine nur die SDL initialisieren und die Bereinigung durchführen, wenn alles erledigt ist. Da wir die Engine im Hauptzyklus verwenden werden, müssen wir auch prüfen, ob sie weiterhin funktioniert, um die Arbeit abzuschließen, die üblichen Ereignisse zu verarbeiten, die Spielwelt zu aktualisieren und eine Folge von Frames zu zeichnen.

Der Teil der Engine, der dem State Manager zugeordnet ist, ist im Wesentlichen sehr einfach. Damit einige Zustände über anderen bestehen, müssen Sie sie in Form eines Stapels anordnen. Ich werde einen solchen Stack mit einem Vektor von STL implementieren. Außerdem benötige ich Methoden zum Ändern von Zuständen sowie zum Auf- und Abbewegen von Zuständen im Stapel.

Die Klasse der Game-Engine hat also die folgende Form:

class CGameEngine
{
public:
  void Init();
  void Cleanup();
  void ChangeState(CGameState* state);
  void PushState(CGameState* state);
  void PopState();
  void HandleEvents();
  void Update();
  void Draw();
  bool Running() { return m_running; }
  void Quit() { m_running = false; }
private:
  // стек состояний
  vector states;
  bool m_running;
};


Das Schreiben einiger dieser Funktionen wird sehr einfach sein. , und - Alle rufen einfach die entsprechende Funktion aus dem Status auf, der sich oben im Stapel befindet. Da dies häufig den Zugriff auf die Daten der Game Engine erfordert, kehre ich zur Klasse der Spielzustände zurück und füge einen Zeiger auf die Klasse der Game Engine als Parameter für jede dieser Mitgliedsfunktionen hinzu. Die letzte Frage ist, wie man zwischen den Zuständen wechselt. Woher weiß der Motor, wann er von einem Zustand in einen anderen wechseln muss? Auf keinen Fall. Nur der aktuelle Status weiß, dass in den nächsten Status gewechselt werden muss. Wir kehren also wieder zur Klasse der Spielzustände zurück und fügen dort eine Funktion zum Übergang zwischen ihnen hinzu.HandleEvents()
Update()
Draw()




In diesem Fall erstellen wir eine abstrakte Basisklasse und machen die meisten ihrer Mitglieder zu rein virtuellen Funktionen. Dadurch wird sichergestellt, dass die geerbte Klasse sie implementiert. Angesichts all dieser Änderungen wird die fertige Klasse von Spielzuständen die folgende Form annehmen:

class CGameState
{
public:
  virtual void Init() = 0;
  virtual void Cleanup() = 0;
  virtual void Pause() = 0;
  virtual void Resume() = 0;
  virtual void HandleEvents(CGameEngine* game) = 0;
  virtual void Update(CGameEngine* game) = 0;
  virtual void Draw(CGameEngine* game) = 0;
  void ChangeState(CGameEngine* game,
                   CGameState* state) {
    game->ChangeState(state);
  }
  protected: CGameState() { }
};


Das Hinzufügen von Status zu unserem Spiel ist jetzt ganz einfach: Wir erben die Basisklasse und definieren sieben reine virtuelle Funktionen. Da wir in jedem Fall nicht mehr als eine Instanz eines bestimmten Staates benötigen, setzen wir sie in Form von Einzelgängern um. Wenn Sie mit dem Muster "Einzelgänger" nicht vertraut sind, erkläre ich Folgendes: Sie können nur sicherstellen, dass das Objekt genau in einer Instanz vorhanden ist. Dazu wird der Konstruktor geschützt und anschließend eine Funktion erstellt, die einen Zeiger auf eine statische Instanz dieser Klasse zurückgibt.

Damit Sie sich vorstellen können, wie diese Methode das gesamte Spiel vereinfachen kann, beachten Sie die folgende Auflistung, in der sich die gesamte Datei befindet :main.cpp


#include "gameengine.h"
#include "introstate.h"
int main ( int argc, char *argv[] )
{
  CGameEngine game;
  // инициализация движка
  game.Init( "Engine Test v1.0" );
  // загрузка заставки
  game.ChangeState( CIntroState::Instance() );
  // основной цикл
  while ( game.Running() )
  {
    game.HandleEvents();
    game.Update();
    game.Draw();
  }
  // очистка движка
  game.Cleanup();
  return 0;
}


Dateien In

diesem Beispiel werden drei Zustände beschrieben: ein Bildschirmschoner, der vor einem schwarzen Hintergrund angezeigt wird, das Gameplay und das Game-Menü. Das Spiel wird für die Dauer des Menüs unterbrochen und nach dem Schließen des Menüs fortgesetzt. Jeder Zustand entspricht einem einfachen Hintergrundbild.

stateman.zip - Quellcode, Grafiken und Projektdateien für Visual C ++
stateman.tar.gz - Quellcode, Grafiken und Projektdateien für Linux.

Der Beispielcode verwendet SDL. Wenn Sie mit SDL noch nicht vertraut sind, lesen Sie mein Tutorial Erste Schritte mit SDL . Wenn Sie SDL nicht auf Ihrem Computer installiert haben, können Sie dieses Beispiel nicht kompilieren und ausführen.

Ressourcen

Wenn Sie gerade erst anfangen, C ++ zu lernen, sollten Sie sich auf jeden Fall mit dem Buch „ Lernen von C ++ durch Spieleprogrammierung “ vertraut machen . Dies ist eine wunderbare Einführung in die Programmiersprache C ++. Der Autor verwendet beispielsweise einfache Spiele. Für mittelschwere Programmierer empfehle ich C ++ für Spielprogrammierer . Dieses Buch wird Ihnen helfen, Ihre Kenntnisse in C ++ zu vertiefen. Um die Muster richtig zu lernen, lesen Sie das Buch Design Patterns, das von The Gang of Four verfasst wurde.

Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte komm rein .

Benötigen Sie ein Buch "C ++ For Game Programmers"?

  • 85.7% Ja, übersetzen Sie, ein solches Buch fehlt schon lange 192
  • 14,2% Nein, veraltet 32

Jetzt auch beliebt: