Home » Tutorials » Grafik und Spiele » Vier gewinnt

Vier gewinnt

Der Einstellungsdialog

Manch einer wird sich auch erinnern, dass wir am Anfang mit mehr oder weniger Mühe einen extra Dialog gebastelt haben, in dem der Spieler Einstellungen vornehmen kann. Den haben wir bisher überhaupt nicht verwendet. Das werden wir jetzt ändern.

Den Dialog als solchen haben wir schon erstellt, jetzt müssen wir den einzelnen Funktionen nur noch Leben einhauchen. Zunächst benötigen wir für alles, was wir einstellen wollen, eine eigene Variable.

private
  FFelderbreite: Integer;
  FSpieler1Farbe: TColor;
  FSpieler2Farbe: TColor;
  FSpieler1Name: string;
  FSpieler2Name: string;

Um später die einzelnen Werte aus dem Dialog auslesen zu können benötigen wir jetzt noch für jede Variable eine sogenannte Property. Diese wird nicht unter private, sondern unter public deklariert:

public
  property Felderbreite: Integer read FFelderbreite;
  property Spieler1Farbe: TColor read FSpieler1Farbe;
  property Spieler2Farbe: TColor read FSpieler2Farbe;
  property Spieler1Name: string read FSpieler1Name;
  property Spieler2Name: string read FSpieler2Name;

Properties dienen dazu, den Zugriff auf Variablen von außerhalb der Klasse zu beschränken bzw. zu steuern. Wird von einer anderen Klasse zum Beispiel die Felderbreite benötigt, dann greift diese nicht direkt auf die Variable FFelderbreite zu, sondern fordert mit „Felderbreite“ diese Variable nur an. Hinter read steht, was die anfragende Klasse erhält. In diesem Fall ist das direkt die Variable. Hier kann auch eine Funktion stehen, welche zum Beispiel eine bestimmte Formatierung oder ähnliches zurückgibt und auch andere Aktionen ausführen kann, wenn die Variable abgefragt wird, wie etwa einen Zähler erhöhen oder anderes.

Im Allgemeinen zählt zu Propertys auch ein Abschnitt write. Die Funktionsweise ist ähnlich wie bei read. Auch hier kann einfach nur die Variable stehen, d.h. die externe Klasse schreibt direkt in die Variable. Häufiger wird hier eine Funktion angegeben, welche zum Beispiel den neuen Wert auf Plausibilität prüft oder umformatiert, … In unserem Fall müssen die Variablen nicht von außen geändert werden, weshalb wir keine write Anweisung brauchen.

Als nächstes müssen wir den Variablen natürlich die Werte geben, die der Benutzer in den Feldern eingetragen bzw. ausgewählt hat. Dabei gibt es eigentlich nur zwei Dinge, auf die ich hier näher eingehen werde, der Rest ist nichts Besonderes.

Zum einen muss im Feld für die Eingabe der Feldbreite verhindert werden, dass der Benutzer Buchstaben oder ähnlichen Blödsinn eingibt. Die Vorgehensweise ist dabei recht einfach. In der KeyDown Routine des Eingabefeldes, also immer dann, wenn eine Taste gedrückt wird, wird überprüft, ob es sich um einen Zahlenwert oder etwas anderes handelt. Zahlen werden „durchgelassen“
und alles andere außer der Backspace Taste wird verworfen:

procedure TFormEinstellungen.edtFelderbreiteKeyPress(Sender: TObject;
var Key: Char);
begin
  if not (Key in ['0'..'9', Char(VK_BACK)]) then Key := #0;
end;

Als nächstes schauen wir uns an, wie wir den Spieler die Farbe aussuchen lassen. Dazu haben wir zwei Panels erstellt, auf denen der jeweilige Spieler steht. Zusätzlich sind sie in der richtigen Farbe eingefärbt.

Wir werden jetzt zwei Dialoge auf unser Form platzieren. Diese finden wir in der Registerkarte „Dialoge“. Dort wählen wir den TColorDialog aus und platzieren ihn auf dem Formular. Wo ist egal, er ist im laufenden Programm nicht sichtbar. Wir nennen ihn
„SpielerFarbDialog“.

Diese Dialoge rufen wir jetzt in der OnClick Routine des Panels auf, dass der Spieler die Farbe auswählen kann:

if SpielerFarbDialog.Execute then
begin
  FSpieler1farbe := SpielerFarbDialog.Color;
  PanelFarbenAktualisieren;
end;

„Execute“ ist eine Funktion, die den Dialog öffnet. Drückt der Benutzer am Ende auf „OK“ gibt sie „true“ zurück, drückt der Spieler auf „Abbrechen“ gibt sie „false“ zurück. Durch die Einbindung in die If-Abfrage wird die Spieler Farbe nur geändert, wenn auch tatsächlich auf OK gedrückt wurde. Dasselbe muss natürlich auch beim zweiten Panel passieren.

Die Prozedur PanelFarbenAktualisieren kapselt nur die Anpassung der Hintergrundfarben der beiden Panels an die Spielerfarbe:

pnlAendernSpieler1.Color := FSpieler1farbe;
pnlAendernSpieler2.Color := FSpieler2farbe;

Nachdem der Nutzer nun überall seine Eingaben machen kann, müssen die natürlich beim Klick auf OK in die Variablen geschrieben werden, dass das Hauptformular sie „abholen“ kann.

Da ist nichts Kompliziertes dabei. Nur bei der Feldgröße müssen wir etwas aufpassen. Zum einen muss da eine Abfrage eingebaut werden, ob der Nutzer eine ungültige Eingabe tätigt. Das haben wir zwar weitestgehend unterbunden, indem wir alle Eingaben außer Zahlen verboten haben, aber Nutzer sind sehr einfallsreich, wenn es darum geht, Fehler zu erzeugen. Deshalb werden wir beim Umwandeln prüfen, ob tatsächlich eine Zahl eingegeben wurde.

if not TryStrToInt(edtFelderbreite.text, temp) then
begin
  MessageDlg('Ungültige Eingabe der Spielfeldbreite!' + sLineBreak +
  'Bitte nur ganze Zahlen zwischen 10 und 40 eingeben!', mtWarning, [mbOK], 0);
  ModalResult := mrNone;
  exit;
end
else
  FFelderbreite := temp;

TryStrToInt versucht den übergebenen String in eine Zahl umzuwandeln, die er in die zweite übergebene Variable schreibt. Sollte das nicht möglich sein gibt die Funktion false zurück.

In dem Fall wird mit MessageDlg ein Standard-Dialog von Windows geöffnet. In diesem steht der übergebene Text. mtWarning ist der Dialogtyp. Der Hauptunterschied zwischen den verschiedenen Dialogen besteht im Symbol links oben und ggf. dem Sound der abgespielt wird. Anschließend wird noch mitgeteilt, welche Buttons der Dialog haben soll, in unserem Fall nur „OK“. Die Null steht für den Hilfe-Kontext. Da wir keine Hilfe haben, steht hier 0.

Hat der Benutzer eine Zahl eingegeben, so wird diese als Feldbreite benutzt.

Um zu vermeiden dass unsinnig große oder kleine Werte z.B. 1 oder auch 1000 eingesetzt werden, setzen wir danach noch eine Sicherheitsabfrage, die Prüft ob der Wert zwischen 10 und 40 liegt und ihn gegebenenfalls anpasst.

Aufgabe: Schreibe die besagte Abfrage, zeige bei übertreten der Grenzen eine Fehlermeldung an und setze den Wert auf Mini- bzw. Maximum.

if FFelderbreite < 10 then
begin
  MessageDlg('Der eingegebene Wert für die Feldbreite ist zu klein. Minimalwert 10 verwendet.',
  mtWarning, [mbOK], 0);
  FFelderbreite := 10;
end;
if FFelderbreite > 40 then
begin
  MessageDlg('Der eingegebene Wert für die Feldbreite ist zu groß. Maximalwert 40 verwendet.',
  mtWarning, [mbOK], 0);
  FFelderbreite := 40;
end;

Zum Abschluss müssen nur noch die Spieler-Namen übernommen werden.

Wer jetzt voller Freude das Spiel gestartet hat und den Einstellungsdialog ausprobieren wollte, ist enttäuscht worden.
Beim Klick auf „Einstellungen“ passiert nichts. Logisch, denn daran haben wir bisher noch nichts gemacht.

Dazu kommen wir jetzt. Wir müssen das zweite Formular öffnen, wissen wann OK geklickt wurde und dann die Einstellungen übernehmen. Hierbei kommt der Modalresult zum Einsatz. Wir erinnern uns, dass wir am Anfang dem OK Button einen modalresult mrOK gegeben haben und dem Abbrechen Button mrCancel.

Wird jetzt das Formular mit showmodal Aufgerufen, wird es beim Klicken einer der beiden Buttons geschlossen und der modalresult zurück gegeben. Dieser kann dann genutzt werden, um zu entscheiden, was weiter getan wird. Im Quelltext sieht das dann so aus:

FormEinstellungen.Position := poMainFormCenter;
if FormEinstellungen.ShowModal = mrOK then
begin
  EinstellungenUebernehmen;
end;
Darstellen;

Die erste Zeile dient dazu, das neue Formular genau in die Mitte des Hauptformulars zu setzen.
Anschließend wird das neue Formular geöffnet. Während es offen ist, kann auf das Hauptformular nicht mehr zugegriffen werden.
Falls der Nutzer auf OK klickt, werden die Einstellungen übernommen. Am Ende wird das Spielfeld noch neu gezeichnet, da sich sowohl Name als auch Farbe der Spieler oder auch die Feldgröße verändert haben können.

FFelderbreite := FormEinstellungen.Felderbreite;
...
GroessenEinstellungenAnpassen;

Die Prozedur EinstellungenUebernehmen macht nichts anderes, als sämtliche Eigenschaften zu übernehmen. Wichtig dabei ist, dass auf die Properties zugegriffen wird.