Home » Tutorials » Grafik und Spiele » Genesis3D und Delphi

Genesis3D und Delphi

Ein Spieltisch für Genesis3D

Als erstes kümmern wir uns jetzt um das Quartett der Form-Prozeduren, das hier in der Tabelle zusammengefasst ist:

FormCreate Vor dem Erscheinen des Formulars: Einrichten der 3D-Engine, Einbinden der nötigen Grafiktreiber
FormActivate Mit dem Erscheinen des Formulars: Grafiktreiber und -modus initialisieren, Spiel „laufen lassen“
FormDestroy Mit dem Verschwinden des Formulars: Spielelemente wieder „abbauen“
FormClick Reaktion auf Mausklick, um Spiel zu beenden

Alle diese Methoden müssen zunächst über das jeweilige On-Ereignis im Register „Ereignisse“ des Objektinspektors erzeugt und verknüpft werden, ehe wir sie mit unseren Anweisungen füllen.
Es geht nun darum, die Fähigkeiten von Genesis3D in Delphi einzubetten. Dazu ist die Methode TForm1.FormCreate genau die richtige, um die „Maschine“ zu initialisieren:

procedure TForm1.FormCreate(Sender: TObject);
var AppDir: String;
begin
  // Wert von ControlWord speichern, Division durch Null "ausschalten"
  CW8087 := Default8087CW;
  Set8087CW(MCW_EM);
  // Handle/Instanz von Formular/Applikation für 3D-Engine festlegen
  GHandle   := Self.Handle;
  GInstance := hInstance;
  // Pfad der Applikation ermitteln
  AppDir := ExtractFilePath (Application.Exename);
  // 3D-Engine initialisieren
  Engine := geEngine_CreateWithVersion
    (GHandle, GVersion, PChar(AppDir), GE_VERSION);
  if Engine = nil then
    ExitError('3D-Engine kann nicht gestartet werden!');
  // Treiber auswählen (Methode aus Unit G4DDriver)
  SetDriver(GInstance, GHandle, Engine, Driver, DMode);
end;

Wichtig zu wissen ist, dass wir hier einige Kontrollen in die Hände von Genesis3D übergeben (müssen) und das Formular damit nur noch als reiner „Spieltisch“ dient.
Bereits in den ersten beiden Zeilen geht es um einen Kontrollwert für die in den Prozessor eingebaute numerische Einheit – englisch Floating Point Unit genannt (kurz: FPU). Weil unsere 3D-Engine fast nur mit „Kommazahlen“ arbeitet, wird die Kontrolle der Division durch null ausgeschaltet (und von Genesis3D übernommen):

CW8087 := Default8087CW;  // Alten Wert merken
Set8087CW (MCW_EM);       // Neuen Wert setzen

Das ist deshalb nötig, weil die FPU diesen Fehler bereits bei sehr kleinem Divisor meldet, der nahe bei null liegt (aber noch nicht null ist). Ich habe den Quelltext so übernommen, wie er in einer Notiz zu den G4D-Units empfohlen wird. (8087 ist übrigens die alte ursprüngliche Bezeichnung der FPU.)
Als Nächstes geht es um Handles und Instanzen:

GHandle   := Self.Handle;
GInstance := hInstance;

Unter einem Handle versteht man eine Art „Aufhänger“ oder Verknüpfung. Die ordnet Windows jedem Objekt zu, ob es nun z.B. ein Formular oder die Anwendung selbst ist. Unsere 3D-Engine benötigt diese beiden Verknüpfungswerte, um sich dort auch richtig einnisten zu können. GHandle ist die Verbindung zum Formular und GInstance die zur Applikation, also zum Spielprogramm selbst.
Außerdem benötigt die Engine einen Pfad, um auch den richtigen Ordner für die Applikation auszumachen.

AppDir := ExtractFilePath(Application.Exename);

Und dann kann endlich die Initialisierung beginnen:

Engine := geEngine_CreateWithVersion
  (GHandle, GVersion, PChar(AppDir), GE_VERSION);

CreateWithVersion bedeutet eigentlich das Gleiche wie Create, nur wird hier gleich eine Versionsbezeichnung mit eingebunden, die wir als Konstante vereinbart haben:

GVersion  = 'G4DPlay1';

Sollte die Initialisierung nicht klappen, erhält Engine den Wert nil (sozusagen „nichts“). Und das Programm wird mit einer Fehlermeldung beendet. Weil solche Fälle durchaus immer wieder vorkommen können, ist uns das eine eigene Prozedur wert, die wir dann immer wieder mit dem passenden Text aufrufen können:

procedure TForm1.ExitError (Txt: String);
begin
  // Fehlermeldung anzeigen, Programm abbrechen
  ShowMessage(Txt); halt;
end;

Schließlich müssen Grafiktreiber und Grafikmodus ausgesucht und eingebunden bzw. eingestellt werden:

SetDriver(GInstance, GHandle, Engine, Driver, DMode);

Die Arbeit der FormCreate-Methode ist damit beendet, die weiteren Aufgaben übernimmt die Methode FormActivate. Sie tritt in Aktion, sobald das Formular „aktiv“ ist:

procedure TForm1.FormActivate(Sender: TObject);
begin
  // Grafiktreiber/modus überprüfen
  if (Driver = nil) or (DMode = nil) then
    ExitError ('Kein Grafiktreiber/modus ausgewählt!');
  // Grafiktreiber/modus initialisieren
  if geEngine_SetDriverAndMode(Engine, Driver, DMode) <> OK then
    ExitError ('Grafikinitialisierung fehlgeschlagen!');
  // Anzeigedaten für Engine ausschalten
  geEngine_EnableFrameRateCounter(Engine, GE_FALSE);
  // Spiel initialisieren
  CreateGame;
  // Wiederholungs-Schleife für Spielverlauf
  RunGame;
  // Formular (bei Spielende) schließen
  Close;
end;

Zuerst muss überprüft werden, ob die Treiber zur Verfügung stehen und auch funktionieren. Wenn nicht, gibt’s wieder eine Fehlermeldung und dann ist Schluss:

if (Driver = nil) or (DMode = nil) then
  ExitError ('Kein Grafiktreiber/modus ausgewählt!');

Ansonsten kann die 3D-Engine Treiber und Modus für ihre Zwecke einrichten:

geEngine_SetDriverAndMode(Engine, Driver, DMode)

Genesis3D zeigt in der Standardeinstellung verschiedene Leistungsdaten an, die beim Betrachten der Welt stören können. Deshalb lässt sich diese Anzeige auch abschalten (was wir hiermit tun):

geEngine_EnableFrameRateCounter(Engine, GE_FALSE);  // sonst GE-TRUE

Hat alles geklappt, dann wird das Spiel selbst initialisiert. Anschließend kann gespielt werden. Dafür sind die beiden Methoden CreateGame und RunGame zuständig. Die letzte Prozedur beinhaltet eine Wiederholungsschleife, in der auch Ereignisse von Maus und Tastatur ausgewertet werden (können). Zusätzlich findet hier das gesamte Spiel statt!
Wird diese Schleife dann verlassen, kommt die letzte Anweisung in FormActivate zum Zuge: Mit Close wird der „Spieltisch“ geschlossen und die ganze Applikation beendet.
Weil CreateGame und RunGame nicht Bestandteil des Genesis-Wortschatzes sind, werden wir uns später selbst um ihre Erstellung kümmern.
Vorher jedoch beschäftigen wir uns noch mit der Methode FormDestroy, wo auch der alte Wert für die FPU wiederhergestellt wird.

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // Spiel "freigeben"
  FreeGame;
  // Alten FPU-Kontrollwert wiederherstellen
  Set8087CW (CW8087);
end;

Da ist dann auch die dritte Methode im Bunde – FreeGame. Auch um die werden wir uns noch kümmern.