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.