[C++] Tic Tac Toe mit GUI (Borland c++-Builder 5 VCL) [Teil 3 von 4]

Im letzten Teil habe ich versucht, euch die Funktionsweise des Controllers (Teil 2) näher zu bringen.

ttt2Im dritten Teil des „Tic Tac Toe“-Tutorials geht es um das Optionsmenü, bei dem mir meiner Meinung nach die Umsetzung des MVC ziemlich gut gelungen ist. Die View-Schicht kommuniziert mit dem Controller nur insofern, dass sie alle Anfragen (Klicks) an ihn weiterleitet und das, was sie vom Controller befohlen bekommt, ausführt. Zum Model besteht überhaupt keine Verbindung.

Das hat zur Folge, dass die Methoden allesamt ziemlich kurz geraten sind (fast nur 1-Zeiler) und auch nicht viel Erklärung von Nöten ist.

Dafür hat es aber der  Option-Controller in sich. Doch dazu kommen wir dann später noch!

Code der GUI

Den Code der GUI vom Optionsmenü möchte ich euch nicht länger vorenthalten. Im Bild seht ihr, wie das Menü aussieht. Es gibt einige Buttons, die alle einen Auftrag für den Controller bedeuten, wenn sie angeklickt werden.

#include "__headers.h"

__fastcall TForm2::TForm2(TComponent* Owner)    //Konstruktor
        : TForm(Owner)
{
  this->controller = new ControllerOptions(this, Form1->getController());  //Model für Einstellungen erstellen
}

//Setzt die Farbe des ersten Spielers
void __fastcall TForm2::button_farbe_spieler1Click(TObject *Sender)
{
  this->farbauswahl->Execute();                //Farbauswahl anzeigen
  this->farbe_spieler1->Color =  this->farbauswahl->Color;  //gewählte Farbe übernehmen
}

//Setzt die Farbe des zweiten Spielers
void __fastcall TForm2::button_farbe_spieler2Click(TObject *Sender)
{
  this->farbauswahl->Execute();               //Farbauswahl anzeigen
  this->farbe_spieler2->Color =  this->farbauswahl->Color;  //gewählte Farbe übernehmen
}

//Behandelt den Klick des OK-Buttons
void __fastcall TForm2::button_okClick(TObject *Sender)
{
  this->controller->ok();             //an Controller delegieren
}

//Einstellen der Hintergrundfarbe
void __fastcall TForm2::button_farbe_hintergrundClick(TObject *Sender)
{
  this->farbauswahl->Execute();     //Farbauswahl anzeigen
  this->farbe_hintergrund->Color = this->farbauswahl->Color; //gewählte Farbe übernehmen
}

//Alles auf Standarteinstellungen setzen
void __fastcall TForm2::button_resetClick(TObject *Sender)
{
 this->controller->reset();
}

//Einstellungen exportieren
void __fastcall TForm2::button_exportClick(TObject *Sender)
{
   this->controller->exportSettings();
}

//Importieren der Einstellungen
void __fastcall TForm2::button_importClick(TObject *Sender)
{
  this->controller->importSettings();
}

//Formular initialisieren
void __fastcall TForm2::FormCreate(TObject *Sender)
{
   this->controller->init();
}

//Farbe eines leeren Felds ändern
void __fastcall TForm2::button_farbe_leeresfeldClick(TObject *Sender)
{
  this->farbauswahl->Execute();    //Farbdialog anzeigen
  this->farbe_leeresfeld->Color =  this->farbauswahl->Color;  //Gewählte Farbe übernehmen
}

//Wenn der Typ des ersten Spielers geänder wird, wird diese Methode ausgeführt
void __fastcall TForm2::typ_spieler1Click(TObject *Sender)
{
  this->controller->changeTypeOfPlayer(1);
}

//Wenn der Typ des zweiten Spielers geänder wird, wird diese Methode ausgeführt
void __fastcall TForm2::typ_spieler2Click(TObject *Sender)
{
  this->controller->changeTypeOfPlayer(2);
}

//Schwierigkeitsstufe des ersten PCs ändern
void __fastcall TForm2::dif_pc1Click(TObject *Sender)
{
 this->controller->setRestartOnSave(true);          //Neustart, weil Schwierigkeit geändert wurde
}

//Schwierigkeitsstufe des zweiten PCs ändern
void __fastcall TForm2::dif_pc2Click(TObject *Sender)
{
 this->controller->setRestartOnSave(true);          //Neustart, weil Schwierigkeit geändert wurde
}

//Schließen des Optionsmenüs
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
  //Das Schließen des Optionsmenüs soll das gleiche wie ein Klick auf OK bewirken.
  this->controller->ok();
}

Wie beim letzten Teil der Artikelserie wird auch hier der Controller beim Erzeugen des Formulars vom Formular selbst instanziiert. Danach liegt aber die Macht über die Anwendung beim Controller.

Der Options-Controller

Da es zur GUI nicht viel zu sagen gibt, komme ich gleich zum Controller des Optionsmenüs, den ich im restlichen Beitrag der Einfachheit halber einfach nur Controller nennen werde.

/*
   Der Controller für die Optionen schafft eine Verbindung zwischen
   GUI und Model.
   Er gibt die Daten zur Speicherung weiter und gibt die ausgelesenen Daten
   an die GUI zur Anzeige.
*/
class ControllerOptions
{
  private:
    ModelOptions* model;               //Pointer auf Model
    TForm2* gui;                       //Pointer auf GUI
    Controller* main_controller;       //Pointer auf Hauptcontroller
    bool restart_on_save;              //Nach Speichern neu starten?

  public:
    ControllerOptions(TForm2* pgui, Controller* maincontroller)
    {
      this->restart_on_save = false;                //Beim Speichern das Spiel neustarten?
      this->model = new ModelOptions;               //Erstellen des Models
      this->gui   = pgui;                           //GUI zuweisen
      this->main_controller = maincontroller;       //Hauptcontroller zuweisen
    }

    void storeSettings();                           //Einstellungen speichern
    void importSettingsFromFile(AnsiString);        //INI-Datei importieren

    void ok();                                      //Schließen der Optionen
    void reset();                                   //Zurücksetzen der Optionen
    void exportSettings();                          //Exportieren
    void importSettings();                          //Importieren
    void init();                                    //Initialisieren
    void setRestartOnSave(bool);                    //Beim Speichern neu starten?
    void changeTypeOfPlayer(int nr);                //Spielertyp ändern
};

//Wird beim Klick auf OK oder beim Schließen aufgerufen.
void ControllerOptions::ok()
{
  this->storeSettings();             //Einstellungen übernehmen
  this->gui->Visible = false;        //Optionsmenü verstecken
}

//Hier kann eingestellt werden, ob das Spiel nach dem Speichern neu gestartet
//werden muss
void ControllerOptions::setRestartOnSave(bool r)
{
  this->restart_on_save = r;
}

/*
  Wird aufgerufen, wenn das Formular erzeugt wird
  (Also beim Programmstart)

  Hier werden Werte übernommen und Einstellungen aus einer INI-Importiert,
  falls eine existiert.
*/
void ControllerOptions::init()
{
   //Die aktuellen Farben müssen in der GUI angezeigt werden (Optionsmenü)
   this->gui->farbe_spieler1->Color =  this->main_controller->getSpieler(1)->getColor();
   this->gui->farbe_spieler2->Color =  this->main_controller->getSpieler(2)->getColor();
   this->gui->farbe_hintergrund->Color =  this->main_controller->getModel()->getBackgroundColor();
   this->gui->farbe_leeresfeld->Color = this->main_controller->getModel()->getEmptyFieldColor();

   //Die aktuellen Spielernamen anzeigen
   this->gui->spieler1_name->Text    = this->main_controller->getSpieler(1)->getName();
   this->gui->spieler2_name->Text    = this->main_controller->getSpieler(2)->getName();

  //Prüfen, ob Pfad zur INI existiert.
  if(this->model->getINIPath() != "")
  {
     //Importieren der Einstellungen aus der in pathinfo.ttt angegebenen Datei
     this->importSettingsFromFile(this->model->getINIPath());

     //Spiel zurücksetzen
     this->main_controller->reset();
  }
}

//Einstellungen in eine INI-Datei exportieren
void ControllerOptions::exportSettings()
{
   //Es soll automatisch als erstes der Ordner angezeigt werden, wo das letzte Mal
   //gespeichert wurde
   this->gui->SaveDialog1->InitialDir = ExtractFilePath(this->model->getINIPath());
   //Den Dialog zur Auswahl der INI-Datei anzeigen
   this->gui->SaveDialog1->Execute();

   //Prüfen, ob eine Datei gewählt wurde
   if(this->gui->SaveDialog1->FileName == "")
   {
       ShowMessage("Keine Datei gewählt");
       return;                          //Abbrechen
   }

   //Prüfen, ob gewählte Datei existiert
   if(FileExists(this->gui->SaveDialog1->FileName))
   {
      //Fragen, ob überschrieben werden darf
      if(Application->MessageBox((this->gui->SaveDialog1->FileName +(AnsiString)" ist schon vorhanden").c_str(), "Sicher?", MB_YESNO) == IDNO)
      {
        return;                      //Abbrechen
      }
   }

   //Ein neues Zugriffsobjekt auf die INI-Datei wird erstellt
   TIniFile* ini = new TIniFile(this->gui->SaveDialog1->FileName);

   this->model->setINIPath(this->gui->SaveDialog1->FileName);

   //Spielernamen in Einstellungsdatei schreiben
   ini->WriteString("name", "spieler1", this->gui->spieler1_name->Text);
   ini->WriteString("name", "spieler2", this->gui->spieler2_name->Text);

   //Alle Farben als (int) kodiert in Einstellungsdatei schreiben
   ini->WriteInteger("farben", "hintergrund", (unsigned int)this->gui->farbe_hintergrund->Color);
   ini->WriteInteger("farben", "leer",        (unsigned int)this->gui->farbe_leeresfeld->Color);
   ini->WriteInteger("farben", "spieler1",    (unsigned int)this->gui->farbe_spieler1->Color);
   ini->WriteInteger("farben", "spieler2",    (unsigned int)this->gui->farbe_spieler2->Color);

   //Spielertypen in Einstellungsdatei schreiben
   ini->WriteInteger("spieler", "spieler1",    this->gui->typ_spieler1->ItemIndex);
   ini->WriteInteger("spieler", "spieler2",    this->gui->typ_spieler2->ItemIndex);

   //PC-Schwierigkeit schreiben
   ini->WriteInteger("schwierigkeit", "pc1",  this->gui->dif_pc1->ItemIndex);
   ini->WriteInteger("schwierigkeit", "pc2",  this->gui->dif_pc2->ItemIndex);

   //Erfolgsmeldung anzeigen
   ShowMessage("Die Einstellungen wurden unter "+this->gui->SaveDialog1->FileName+" abgespeichert");

   //Speicher wieder freigeben
   delete ini;
}

//Importieren der Einstellungen
void  ControllerOptions::importSettings()
{
   //Es soll automatisch als erstes der Ordner angezeigt werden, wo das letzte Mal
   //gespeichert wurde
   this->gui->OpenDialog1->InitialDir = ExtractFilePath(this->model->getINIPath());

   //Datei öffnen-Dialog anzeigen
   this->gui->OpenDialog1->Execute();

   //Wurde eine Datei gewählt?
   if(this->gui->OpenDialog1->FileName == "")
   {
     ShowMessage("Es wurde keine Datei gewählt");
     return;                  //Abbruch
   }

   //Diese Methode importiert dann die Einstellungen aus der gewählten Datei
   this->importSettingsFromFile(this->gui->OpenDialog1->FileName);
}

//Alles auf Standarteinstellungen setzen
void ControllerOptions::reset()
{
 //Zuerst fragen, ob die Einstellungen zurückgesetzt werden sollen
  if(Application->MessageBox("Einstellungen zurücksetzen?", "Sicher?", MB_YESNO) == IDYES)
  {
    //Spielernamen setzen
    this->gui->spieler1_name->Text = CONST__default_spieler1_name;
    this->gui->spieler2_name->Text = CONST__default_spieler2_name;

    //Die Spielertypen-Auswahl setzen
    this->gui->typ_spieler1->ItemIndex = 0;
    this->gui->typ_spieler2->ItemIndex = 1;

    //Die Schwierigkeit einstellen
    this->gui->dif_pc1->ItemIndex     = 0;
    this->gui->dif_pc2->ItemIndex     = 1;

    //Spielerfarben setzen
    this->gui->farbe_spieler1->Color = CONST__default_spieler1_color;
    this->gui->farbe_spieler2->Color = CONST__default_spieler2_color;

    //Andere Farben setzen
    this->gui->farbe_hintergrund->Color = CONST__default_background_color;
    this->gui->farbe_leeresfeld->Color = CONST__default_emptyField_color;
  }
}

//Speichern der Einstellungen
void ControllerOptions::storeSettings()
{
  //Wenn die Spielereinstellungen geändert wurden
  //Dann muss das Spiel neugestartet werden.
  //Davor werden die Spieler so erzeugt, wie eingestellt wurde.
  if(this->restart_on_save)
  {

     //Typ des ersten Spielers setzen
     this->main_controller->setPlayerType(1, this->gui->typ_spieler1->ItemIndex == 0 ? "Mensch" : "Computer");

     //Typ des zweiten Spielers setzen
     this->main_controller->setPlayerType(2, this->gui->typ_spieler2->ItemIndex == 0 ? "Mensch" : "Computer");

     //Falls der erste Spieler ein PC ist, wird noch die Schwierigkeit eingestellt
     if(this->gui->typ_spieler1->ItemIndex == 1)
     {
       //der Cast macht aus Spieler* Computer*
       dynamic_cast(this->main_controller->getSpieler(1))
       ->setSchwierigkeit(this->gui->dif_pc1->ItemIndex == 0 ? 1 : 2);
     }

     //Falls der zweite Spieler ein PC ist, wird noch die Schwierigkeit eingestellt
     if(this->gui->typ_spieler2->ItemIndex == 1)
     {
       //der Cast macht aus Spieler* Computer*
       dynamic_cast(this->main_controller->getSpieler(2))
       ->setSchwierigkeit(this->gui->dif_pc2->ItemIndex == 0 ? 1 : 2);
     }

     //Spiel neustarten
     this->main_controller->reset();
  }

  //Die Spielernamen setzen
  this->main_controller->getSpieler(1)->setName(this->gui->spieler1_name->Text);
  this->main_controller->getSpieler(2)->setName(this->gui->spieler2_name->Text);

  //Die Spielerfarben setzen
  this->main_controller->getSpieler(1)->setColor(this->gui->farbe_spieler1->Color);
  this->main_controller->getSpieler(2)->setColor(this->gui->farbe_spieler2->Color);

  //Die anderen Farben setzen
  this->main_controller->getModel()->setBackgroundColor(this->gui->farbe_hintergrund->Color);
  this->main_controller->getModel()->setEmptyFieldColor(this->gui->farbe_leeresfeld->Color);

  this->main_controller->refreshStatus(); //Status aktualisieren
  this->main_controller->getGUI()->RefreshField();    //Spielfeld neu aufbauen
}

//Anhand einer Datei, deren Name übergeben wird, importiert diese Methode
//alle Einstellungen aus dieser Datei und weißt sie dem Spiel zu.
void ControllerOptions::importSettingsFromFile(AnsiString filename)
{
   //Wenn die Datei nicht existiert, wird einfach abgebrochen.
   if(!FileExists(filename))
   {
     return;
   }

    this->model->setINIPath(filename);

    //Neues Objekt zum INI-Zugriff
    TIniFile* ini = new TIniFile(filename);

    //Read*() erwartet immer 3 Parameter:
    //-Die [Sektion] in der INI-Datei
    //-Den Namen des Attributs
    //-Den Standartwert, falls das Attribut in der INI nicht gefunden wird

    //Hier werden Farben zugewiesen. (der letzte Paramter ist der "aktuelle" wert, der momentan eingestellt ist)
    this->gui->farbe_hintergrund->Color = (TColor)ini->ReadInteger("farben", "hintergrund", this->gui->farbe_hintergrund->Color);
    this->gui->farbe_leeresfeld->Color = (TColor)ini->ReadInteger("farben", "leer",   this->gui->farbe_leeresfeld->Color);
    this->gui->farbe_spieler1->Color = (TColor)ini->ReadInteger("farben", "spieler1", this->gui->farbe_spieler1->Color);
    this->gui->farbe_spieler2->Color = (TColor)ini->ReadInteger("farben", "spieler2", this->gui->farbe_spieler2->Color);

    //Hier werden die Spielertypen zugewiesen
    this->gui->typ_spieler1->ItemIndex = ini->ReadInteger("spieler", "spieler1", this->gui->typ_spieler1->ItemIndex);
    this->gui->typ_spieler2->ItemIndex = ini->ReadInteger("spieler", "spieler2", this->gui->typ_spieler2->ItemIndex);

    //Die Schwierigkeiten der Bots
    this->gui->dif_pc1->ItemIndex = ini->ReadInteger("schwierigkeit", "pc1", this->gui->dif_pc1->ItemIndex);
    this->gui->dif_pc2->ItemIndex = ini->ReadInteger("schwierigkeit", "pc2", this->gui->dif_pc2->ItemIndex);

    //Die Spielernamen
    this->gui->spieler1_name->Text = ini->ReadString("name", "spieler1", CONST__default_spieler1_name);
    this->gui->spieler2_name->Text = ini->ReadString("name", "spieler2", CONST__default_spieler2_name);

    //Die Einstellungen speichern (Dem Spiel/den Objekten zuweisen)
    this->storeSettings();

    //Speicher freigeben
    delete ini;
}

/*
   Ändert den Spielertyp (speichert nichts, sondern zeigt nur die richtigen Werte an)
*/
void ControllerOptions::changeTypeOfPlayer(int nr)
{

  TRadioGroup* rgrp_type = NULL;                //Radiogroup Typ
  TEdit* edt_name = NULL;                       //Textfeld Name
  TRadioGroup* rgrp_diff = NULL;                //Radiogroup Schwierigkeit

  //Um den richtigen Spieler zu erwischen, werden jetzt ein paar Werte zugewiesen
  if(nr == 1)
  {
      rgrp_type = this->gui->typ_spieler1;
      edt_name = this->gui->spieler1_name;
      rgrp_diff = this->gui->dif_pc1;
  }
  else
  {
      rgrp_type = this->gui->typ_spieler2;
      edt_name = this->gui->spieler2_name;
      rgrp_diff = this->gui->dif_pc2;
  }

  if(rgrp_type->ItemIndex == 0) //Mensch
  {
    edt_name->Text = "Mensch";
    rgrp_diff->Enabled = false;     //Mensch hat keine Schwierigkeitsstufe
  }
  else                                    //PC
  {
    edt_name->Text = "Computer";
    rgrp_diff->Enabled = true;        //PC-Schwierigkeit wird einstellbar
  }

  this->setRestartOnSave(true);//Das Spiel muss neugestartet werden,
                                     //Wenn der Spielertyp geändert wurde
}

Der Controller bietet folgende Möglichkeiten

  • Speichern von Einstellungen
  • Laden von Einstellungen
  • Resetten von Einstellungen
  • Spielertyp ändern

Konstruktor

ControllerOptions(TForm2* pgui, Controller* maincontroller)
    {
      this->restart_on_save = false;                //Beim Speichern das Spiel neustarten?
      this->model = new ModelOptions;               //Erstellen des Models
      this->gui   = pgui;                           //GUI zuweisen
      this->main_controller = maincontroller;       //Hauptcontroller zuweisen
    }

Im Konstruktor werden Model und GUI zugewiesen und es wird bekanntgegeben, wer der Hauptcontroller ist. (Controller aus dem letzten Artikel, also die Spielsteuerung) Außerdem wird restart_on_save auf false gesetzt. Steht restart_on_save auf true, wird das Spiel neugestartet, wenn die Einstellungen gespeichert werden. Dazu später noch mehr!

Initialisierung

/*
  Wird aufgerufen, wenn das Formular erzeugt wird
  (Also beim Programmstart)

  Hier werden Werte übernommen und Einstellungen aus einer INI-Importiert,
  falls eine existiert.
*/
void ControllerOptions::init()
{
   //Die aktuellen Farben müssen in der GUI angezeigt werden (Optionsmenü)
   this->gui->farbe_spieler1->Color =  this->main_controller->getSpieler(1)->getColor();
   this->gui->farbe_spieler2->Color =  this->main_controller->getSpieler(2)->getColor();
   this->gui->farbe_hintergrund->Color =  this->main_controller->getModel()->getBackgroundColor();
   this->gui->farbe_leeresfeld->Color = this->main_controller->getModel()->getEmptyFieldColor();

   //Die aktuellen Spielernamen anzeigen
   this->gui->spieler1_name->Text    = this->main_controller->getSpieler(1)->getName();
   this->gui->spieler2_name->Text    = this->main_controller->getSpieler(2)->getName();

  //Prüfen, ob Pfad zur INI existiert.
  if(this->model->getINIPath() != "")
  {
     //Importieren der Einstellungen aus der in pathinfo.ttt angegebenen Datei
     this->importSettingsFromFile(this->model->getINIPath());

     //Spiel zurücksetzen
     this->main_controller->reset();
  }
}

Bei der Initialisierung des Controllers, die in der init()-Methode stattfindet, werden die Elemente des Optionsmenüs mit den entsprechenden Daten gefüttert, die sie anzeigen sollen. Falls eine INI-Datei mit Einstellungen existiert, werden die Einstellungen daraus geladen und angezeigt.

Exportieren der Einstellungen

//Einstellungen in eine INI-Datei exportieren
void ControllerOptions::exportSettings()
{
   //Es soll automatisch als erstes der Ordner angezeigt werden, wo das letzte Mal
   //gespeichert wurde
   this->gui->SaveDialog1->InitialDir = ExtractFilePath(this->model->getINIPath());
   //Den Dialog zur Auswahl der INI-Datei anzeigen
   this->gui->SaveDialog1->Execute();

   //Prüfen, ob eine Datei gewählt wurde
   if(this->gui->SaveDialog1->FileName == "")
   {
       ShowMessage("Keine Datei gewählt");
       return;                          //Abbrechen
   }

   //Prüfen, ob gewählte Datei existiert
   if(FileExists(this->gui->SaveDialog1->FileName))
   {
      //Fragen, ob überschrieben werden darf
      if(Application->MessageBox((this->gui->SaveDialog1->FileName +(AnsiString)" ist schon vorhanden").c_str(), "Sicher?", MB_YESNO) == IDNO)
      {
        return;                      //Abbrechen
      }
   }

   //Ein neues Zugriffsobjekt auf die INI-Datei wird erstellt
   TIniFile* ini = new TIniFile(this->gui->SaveDialog1->FileName);

   this->model->setINIPath(this->gui->SaveDialog1->FileName);

   //Spielernamen in Einstellungsdatei schreiben
   ini->WriteString("name", "spieler1", this->gui->spieler1_name->Text);
   ini->WriteString("name", "spieler2", this->gui->spieler2_name->Text);

   //Alle Farben als (int) kodiert in Einstellungsdatei schreiben
   ini->WriteInteger("farben", "hintergrund", (unsigned int)this->gui->farbe_hintergrund->Color);
   ini->WriteInteger("farben", "leer",        (unsigned int)this->gui->farbe_leeresfeld->Color);
   ini->WriteInteger("farben", "spieler1",    (unsigned int)this->gui->farbe_spieler1->Color);
   ini->WriteInteger("farben", "spieler2",    (unsigned int)this->gui->farbe_spieler2->Color);

   //Spielertypen in Einstellungsdatei schreiben
   ini->WriteInteger("spieler", "spieler1",    this->gui->typ_spieler1->ItemIndex);
   ini->WriteInteger("spieler", "spieler2",    this->gui->typ_spieler2->ItemIndex);

   //PC-Schwierigkeit schreiben
   ini->WriteInteger("schwierigkeit", "pc1",  this->gui->dif_pc1->ItemIndex);
   ini->WriteInteger("schwierigkeit", "pc2",  this->gui->dif_pc2->ItemIndex);

   //Erfolgsmeldung anzeigen
   ShowMessage("Die Einstellungen wurden unter "+this->gui->SaveDialog1->FileName+" abgespeichert");

   //Speicher wieder freigeben
   delete ini;
}

Es ist möglich, alle Einstellungen in eine INI-Datei zu exportieren. Dazu kann man in einem Dateibrowser den Speicherort auswählen. Sollte die Datei bereits existieren, wird gefragt, ob sie überschrieben werden darf.

Wenn die Einstellungen gespeichert wurden, legt das Programm noch eine pathinfo.ttt an, die den Pfad zur INI enthält. Dadurch ist es möglich, dass beim Starten des Spiels automatisch die zuletzt gespeicherten Einstellungen geladen werden, ohne dass der Benutzer es manuell tun muss.

Zum Speichern sämtlicher Informationen verwende ich übrigens die Klasse TIniFile der VCL, die eine einfache Schnittstelle zu INI-Files bietet.

Importieren der Einstellungen

//Importieren der Einstellungen
void  ControllerOptions::importSettings()
{
   //Es soll automatisch als erstes der Ordner angezeigt werden, wo das letzte Mal
   //gespeichert wurde
   this->gui->OpenDialog1->InitialDir = ExtractFilePath(this->model->getINIPath());

   //Datei öffnen-Dialog anzeigen
   this->gui->OpenDialog1->Execute();

   //Wurde eine Datei gewählt?
   if(this->gui->OpenDialog1->FileName == "")
   {
     ShowMessage("Es wurde keine Datei gewählt");
     return;                  //Abbruch
   }

   //Diese Methode importiert dann die Einstellungen aus der gewählten Datei
   this->importSettingsFromFile(this->gui->OpenDialog1->FileName);
}

Beim Importieren der Einstellungen wird wieder ein Dateibrowser angezeigt. Wurde die Datei gefunden, wird der Dateiname an importSettingsFromFile() übergeben. In dieser Methode wird die INI-Datei geöffnet und alle Werte extrahiert. Diese Werte werden dann den entsprechenden GUI-Elementen zugewiesen. Da dies aber noch nicht ausreicht, wird danach noch storeSettings() ausgeführt. In storeSettings werden die Einstellungen dann letztendlich übernommen. Wie das funktioniert, erfahrt ihr im folgenden Abschnitt.

storeSettings()

//Speichern der Einstellungen
void ControllerOptions::storeSettings()
{
  //Wenn die Spielereinstellungen geändert wurden
  //Dann muss das Spiel neugestartet werden.
  //Davor werden die Spieler so erzeugt, wie eingestellt wurde.
  if(this->restart_on_save)
  {

     //Typ des ersten Spielers setzen
     this->main_controller->setPlayerType(1, this->gui->typ_spieler1->ItemIndex == 0 ? "Mensch" : "Computer");

     //Typ des zweiten Spielers setzen
     this->main_controller->setPlayerType(2, this->gui->typ_spieler2->ItemIndex == 0 ? "Mensch" : "Computer");

     //Falls der erste Spieler ein PC ist, wird noch die Schwierigkeit eingestellt
     if(this->gui->typ_spieler1->ItemIndex == 1)
     {
       //der Cast macht aus Spieler* Computer*
       dynamic_cast(this->main_controller->getSpieler(1))
       ->setSchwierigkeit(this->gui->dif_pc1->ItemIndex == 0 ? 1 : 2);
     }

     //Falls der zweite Spieler ein PC ist, wird noch die Schwierigkeit eingestellt
     if(this->gui->typ_spieler2->ItemIndex == 1)
     {
       //der Cast macht aus Spieler* Computer*
       dynamic_cast(this->main_controller->getSpieler(2))
       ->setSchwierigkeit(this->gui->dif_pc2->ItemIndex == 0 ? 1 : 2);
     }

     //Spiel neustarten
     this->main_controller->reset();
  }

  //Die Spielernamen setzen
  this->main_controller->getSpieler(1)->setName(this->gui->spieler1_name->Text);
  this->main_controller->getSpieler(2)->setName(this->gui->spieler2_name->Text);

  //Die Spielerfarben setzen
  this->main_controller->getSpieler(1)->setColor(this->gui->farbe_spieler1->Color);
  this->main_controller->getSpieler(2)->setColor(this->gui->farbe_spieler2->Color);

  //Die anderen Farben setzen
  this->main_controller->getModel()->setBackgroundColor(this->gui->farbe_hintergrund->Color);
  this->main_controller->getModel()->setEmptyFieldColor(this->gui->farbe_leeresfeld->Color);

  this->main_controller->refreshStatus(); //Status aktualisieren
  this->main_controller->getGUI()->RefreshField();    //Spielfeld neu aufbauen
}

Manche Änderungen an den Optionen erfodern einen Neustart des Spiels. z.B. wenn der Spielertyp geändert wurde. Wenn eine solche Einstellung geändert wurde, wird intern restartOnSave auf true gesetzt. Ist dieses Attribut true, dann werden die Spielertypen geändert. Danach wird der Hauptcontroller damit beauftragt, das Spiel neuzustarten.

In jedem Fall aber (auch wenn der Spielertyp nicht geändert wurde), werden die Namen und die Farben gespeichert. Diese Änderungen erfordern keinen Neustart, aber einen Neuaufbau des Spielfelds.

Es gibt hier übrigens einen Artikel zum Speichern von Farben, der die unterschiedlichen Speichermöglichkeiten vergleicht.

Wer sich fragt, wozu die dynamic_casts in dieser Methode nötig sind, wird im letzten Teil dieses Tutorials eine Erklärung finden. Soviel kann ich schonmal verraten: Es hat was mit der Vererbung zu tun!

Weiter gehts!

Da jetzt das Wichtigste geklärt wurde, können jetzt ein paar Worte zum ganzen Rest des Spiels (Teil 4) folgen.

1 Star2 Stars3 Stars4 Stars5 Stars (Wurde noch nicht bewertet)
Loading...


2 Kommentare zu “[C++] Tic Tac Toe mit GUI (Borland c++-Builder 5 VCL) [Teil 3 von 4]”

  1. […] Tipps und Tricks für Webmaster PHP (OOP, MVC), HTML, MySQL, Javascript, AJAX und vieles mehr! « [C++] Tic Tac Toe mit GUI (Borland c++-Builder 5 VCL) [Teil 3 von 4] […]

  2. DAs is doch alles scheiße

Hinterlasse einen Kommentar!

Time limit is exhausted. Please reload the CAPTCHA.

»Informationen zum Artikel

Autor: Simon
Datum: 18.07.2009
Zeit: 15:00 Uhr
Kategorien: C/C++
Gelesen: 6083x heute: 2x

Kommentare: RSS 2.0.
Diesen Artikel kommentieren oder einen Trackback senden.

»Meta