[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.
Im 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.


[…] 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] […]
DAs is doch alles scheiße