Tutorial Bomberman2: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (Verwendete Dateiformate: Weiter übertragen)
K (Die Spielfigur (TPlayer): weiter portiert)
Zeile 427: Zeile 427:
  
 
;<nowiki>procedure ProcessInput;</nowiki> : Auch hier gibts nix besonderes. Mittels '''GetAsyncKeyState''' prüfen wir auf Hardwareebene ob eine Taste im gedrückten Zustand ist und führen dann die damit verbundene Aktion aus. Ist die übergebene Taste gedrückt, wird das höchstwertige Bit des zurückgegebenen Wertes gesetzt. <br>Im Falle einer Bewegung prüfen wir natürlich nach, ob die neue Position des Spielers auch gültig ist. <br> Einzige Besonderheit ist hier die Nutzung unseres vorher berechneten Zeitfaktors '''BomberManGame.TimeFactor''' als Faktor für die Bewegungsgeschwindigkeit.
 
;<nowiki>procedure ProcessInput;</nowiki> : Auch hier gibts nix besonderes. Mittels '''GetAsyncKeyState''' prüfen wir auf Hardwareebene ob eine Taste im gedrückten Zustand ist und führen dann die damit verbundene Aktion aus. Ist die übergebene Taste gedrückt, wird das höchstwertige Bit des zurückgegebenen Wertes gesetzt. <br>Im Falle einer Bewegung prüfen wir natürlich nach, ob die neue Position des Spielers auch gültig ist. <br> Einzige Besonderheit ist hier die Nutzung unseres vorher berechneten Zeitfaktors '''BomberManGame.TimeFactor''' als Faktor für die Bewegungsgeschwindigkeit.
 +
 +
 +
====Bomben (TBomb)====
 +
Auch die von den Spielern legbaren Bomben habe ich in einer Klasse gekapselt um deren Verwaltung und Aktualisierung zu erleichtern. Hier gibts eigentlich nix Spektakuläres, weshalb ich direkt zu den Eigenschaften und Prozeduren übergehe :
 +
 +
;<nowiki>Position : TVector3i;</nowiki> : Die Position der Bombe als '''Integer'''-Vektor, da Bomben nur genau in der Feldmitte abgelegt werden (wie beim original Bomberman).
 +
;<nowiki>Owner : ^TPlayer;</nowiki> : Zeiger auf den Besitzer dieser Bombe.Diese Eigenschaft wird benötigt um herauszufinden wie viele Bomben dieser Spieler bereits gelegt hat, und um die Explosionsreichweiter der Bombe (die auch spielerabhängig ist) zu ermitteln.
 +
;<nowiki>CountDown : Single;</nowiki> : Wie der Eigenschaftsname bereits vermuten lässt ist hier der Countdown bis zur Explosion der Bombe gespeichert. Sinkt dieser Wert auf >= 0, so wird die Explosion eingeleteitet und die Bombe aus der dynamischen Liste entfernt.
 +
;<nowiki>AnimPhase : Single;</nowiki> : Die Animationsphase wird als Grundlage für die sinusförmigen Skalierung der Bombe genutzt und lässt so den Eindruck entstehen als würde die Bombe sich strecken und wieder zusammenziehen.
 +
;<nowiki>procedure Draw;</nowiki> : Kümmert sich um das Zeichnen der Bombe sowie deren Schattens.
 +
;<nowiki>procedure Explode;</nowiki> : Startet die lineare Explosion in vier Richtungen. Wie im Original wird die Explosion in die jeweilige Richtung durch einen Block gestoppt. Ist dieser Block sprengbar, so wird er vom Spielfeld entfernt. <br>Trifft die Explosion auf ihrem Weg auf eine andere Bombe, so wird der Countdown dieser Bombe auf einen Wert nahe 0 gesetzt, so daß eine Kettenrekation stattfindet.
 +
 +
 +
====Das Spiel (TBomberManGame)====
 +
Wie bereits erwähnt repräsentiert dieses Objekt unser Spiel und kümmert sich neben der Verwaltung dynamischer Listen und der Spiellogik auch um die Initialisierung wichtiger Listen bzw. OpenGL und implementiert unsere bereits ausgiebig beschriebene Statemachine. Bei Erweiterung solltet ihr hier alles was global geschieht und nicht von den Objekten selbst getan werden kann unterbringen.
 +
 +
<u>'''Private Deklarationen'''</u>
 +
;<nowiki>ShakeScreen : Single;</nowiki> : In dieser Eigenschaft wird die Restdauer gespeichert während der der Bildschirm nach einer Bombenexplosion erschüttert wird.
 +
;<nowiki>procedure DrawScene_GSMainMenu/DrawScene_GSNewGame/DrawScene_GSInGame/DrawScene_GSMatchEnd;</nowiki> :            Wie bereits im Kapitel zur Statemachine angesprochen sind dies die Unterprozeduren die von der Statemachine zum Zeichnen der verschiedenen Spielstadien (sprich Szenen) genutzt werden.
 +
<u>'''Öffentliche Deklarationen'''</u>
 +
;<nowiki>gsMainMenu_Selected : Integer;</nowiki> : Wie am Präfix dieser Variable zu erkenne, gibt sie den im Hauptmenü ausgewählten Menüpunkt wieder.
 +
;<nowiki>gsMainMenu_Rotation : Single;</nowiki> : Die Rotation der im Hintergrund des Menüs dargestellten Karte.
 +
;<nowiki>gsNewGame_Selected : Integer;</nowiki> : Im Menü "Neues Spiel" gewählter Menüpunkt.
 +
;<nowiki>gsNewGame_Players : Integer;</nowiki> : Anzahl der im Menü "Neues Spiel" eingestellten Spieler.
 +
;<nowiki>MapList : TStringList;</nowiki> : In dieser Stringlist werden mit der Prozedur '''CreateMapList''' alle Namen der in einem bestimmten Ordner gefundene Karten abgelegt, so daß diese im Spielmenü bequem ausgewählt werden können.
 +
;<nowiki>ExtremeMode : Boolean;</nowiki> : Der Name lässts wohl bereits vermuten. Hiermit wird dem Spiel mitgeteilt das es sich im Extremmodus befindet, in dem sowohl Anzahl der legbaren Bomben als auch deren Reichweite und die Geschwindigkeit der Spielfigur stark erhöht werden.
 +
;<nowiki>MapListPos : Integer;</nowiki> : Position der gewählten Karte in '''MapList'''
 +
;<nowiki>TimeFactor : Single;</nowiki> : Diese Variable wurde bereits im Kapitel zum Thema "Timebased Movement" besprochen und behinhaltet den Zeitfaktor der für eine gleichmäßige Bewegung/Animation sorgt.
 +
;<nowiki>OwnerForm : ^TForm;</nowiki> : Zeiger auf die Form in der das Spiel dargestellt wird. Dieser Zeiger muß einen Wert besitzen und darf nicht ins Leere zeigen.
 +
;<nowiki>CurrentState : Word;</nowiki> : Aktueller Status der Statemachine.
 +
;<nowiki>procedure SetState(pNewState : Word);</nowiki> : Versetzt die Statemachine in einen neuen Status.
 +
;<nowiki>procedure CreateMapList(pMapDir : String);</nowiki> : Erstellt eine Liste aller in ''pMapDir''' befindlichen Karten, so daß auf diese einfach und schnell zugegriffen werden kann.
 +
;<nowiki>procedure Create3DSList(p3DSDir : String);</nowiki> : Erstellt eine Liste aller in '''p3DSDir''' befindlichen 3DS-Modelle und generiert auch gleichzeitig deren Displaylisten.
 +
;<nowiki>procedure InitOpenGL;</nowiki> : Erstellt den OpenGL-Rendercontext und initialisiert einige OpenGL-Parameter.
 +
;<nowiki>procedure DrawScene;</nowiki> : Diese Prozedur verzweigt je nach Zustand der Statemachine in die entsprechende Zeichenprozedur.
 +
;<nowiki>procedure UpDateBombs;</nowiki> : Aktualisiert die dynamische Bombenliste und entfernt ggf. explodierte Bomben. Diese Aktion muß außerhalb des TBomb-Objektes getätigt werden, da sich ein Objekt nicht selbst aus einer dynamischen Liste entfernen kann.
 +
;<nowiki>procedure StartNewMatch(pMapName : String);</nowiki> : Wie der Prozedurname vermuten lässt starte diese eine neues Match auf der in '''pMapName''' angegeben Karte. Dabei wird die Bombenliste geleert und es werden alle Spieler wieder auf die Grundwerte zurückgesetzt.
 +
 +
Soviel also zur Deklaration unserer Objekte bzw. Klassen. Wie ihr sehen könnt habe ich hier eine sehr saubere Namensgebung verwendet, durch die viele Eigenschaften bzw. Prozeduren selbsterklärend sind.
 +
 +
Wenden wir uns nun einigen interessanten Stellen im Quellcode zu, die eine genauere Erklärung notwendig machen.
 +
 +
 +
===Interessante Codestellen===

Version vom 26. Dezember 2005, 23:56 Uhr

Bomberman.gif

Wir programmieren einen Bombermanklon!

Teil 1 : Die Codebasis und der Editor

Vorwort

Ave, und willkommen zum zweiten Tutorial der Bombermanserie! Nachdem wir uns im ersten Tutorial mit der Programmierung eines Editor und der grundlegenden Codebasis beschäftigt haben, gehts nun ans Eingemachte, nämlich den 3D-Bombermanklon.

Da der Quellcode unseres Klons recht umfangreich ist, und in diesem Tutorial viele neue Techniken behandelt werden, wirds vielleicht etwas langatmig. Aber keine Angst, denn zum einen versuche ich alles recht ausführlich und auch für Neulinge verständlich zu erklären, und zum anderen werdet ihr am Ende dieses Tutorials einen kompletten Bombermanklon programmiert haben der euch (und eure Bekannten) mit stundenlangem Spielspaß belohnt!

Deshalb gibts zum Ansporn erstmal zwei nette Screenshots direkt aus dem Spiel :

Tutorial Bomberman2 nbomber1.jpgTutorial Bomberman2 nbomber2.jpg


Voraussetzungen

Wie schon im ersten Tutorial vorausgesetzt, solltet ihr recht gute Kenntnisse in Object Pascal und OpenGL mitbringen. Des Weiteren werde ich in unserem Quellcode starken Gebrauch von dynamischen Arrays machen, deren Nutzung und Verwaltung besonders für Personen die sich damit noch nicht beschäftigt haben recht verwirrend sein kann. Deshalb sind gute Kenntnisse zum Thema dynamische Arrays unentbehrlich. Aber keine Angst, denn alles was man zu diesem Thema braucht, findet sich in der Delphi-Hilfe.

Neben der programmiertechnischen Seite, erfordert das Erschaffen eines Spiels natürlich auch den Umgang mit diversen Anwendungen. Einige davon sind kleine Tools, die sich mit einem Klick bedienen lassen während andere Programme eine nicht unerheblich Einarbeitungszeit voraussetzen. Die von mir genutzten Anwendungen und Tools im Überblick :

Komerzielle Anwendungen
Tutorial Bomberman2 app 3dsmax.jpg 3D Studio MAX

Dieses 3D-Modellierungs und Renderprogramm dürfte wohl jedem ein Begriff sein, ist es doch eines der meistgenutzten Programme in der Spielebranche, und man wird wohl kaum ohne grundlegende Kenntnisse in diesem Programm auskommen.

Wenn man sich erstmal (mittels des sehr gute Handbuches/mitgelieferten Tuts) eingearbeitet hat, dann geht das Erstellen von 3D-Modellen sehr leicht von der Hand. Den Hauptaktor unseres Bombermanklons hab ich in knapp 5 Min. erstellt.

Alternativen :
Als Alternative kommen eigentlich alle Programme in Frage, die in der Lage sind in das 3DS-Format zu exportieren, wie z.B. :
Blender, Milksphape3D, Maya

Tutorial Bomberman2 app photoshop.jpg Adobe Photoshop

Zu diesem Bildbearbeitungsprogramm muß ich wohl auch kaum was erzählen. Für alle die es nicht kennen : Das momentan wohl mächtigste Bildbearbeitungsprogramm. Die Einarbeitung ist nicht gerade einfach, doch wenn man sich mit diesem Programm erstmal auskennt, ist das Erstellen von guten Grafiken und Texturen schnell erledigt.

Alternativen :
Micrografx Picture Publisher, Gimp, JASC Paintshop Pro

Kostenlose Tools
Tutorial Bomberman2 tools explosion generator.jpg
Explosion Generator

Dieses sehr praktische Tool rendert eine komplette Explosion in eine Textur, so daß man diese als Grundlage für eine Explosionsanimation ins einer Anwendung nutzen kann. Nachteil der Sache ist, das sich das Erscheinungsbild der Explosion nur manuell über eine Textdatei anpassen lässt.

Link : www.positech.co.uk

Tutorial Bomberman2 tools bitmapfont builder.jpg Bitmapfont Builder

Mit diesem Programm lassen sich in Windeseile die für Bitmapfonts benötigten Fonttexturen auf Basis der in Windows verfügbaren Schriftarten erstellen.

Link : www.lmnopc.com

Soviel also zu den (von mir) genutzten Anwendungen und Tools. Natürlich steht euch die Wahl frei, und ihr könnt die Tools und Programme eurer Wahl nutzen.


Ziel

Wie schon kurz im Vorwort erwähnt, werden wir in diesem Tutorial einen kompletten Bombermanklon programmieren. Ziel dabei ist es, eine saubere Codebasis zu schreiben und euch grundlegende Techniken der Spieleprogrammierung beizubringen.

Außerdem möchte ich euch einen recht sauberen Programmierstil beibringen, der bei vielen missen lässt und Programmcodes für andere oft unnutzbar macht.

Folgendes wird euch im Tutorial erwarten, und euch auch bei zukünftigen Ausflügen in die Welt der Spieleprogrammierung von Nutzen sein :


Tutorial Bomberman2 superbomberman help.gif
  • Timebased Movement (Frameunabhängige Bewegungen)
  • Texturenfonts
  • Statemachine
  • Kollisionsabfrage
  • Sound (via FMOD)


Projektstruktur

Widmen wir uns nun kurz der Projektstruktur. Gemeint sind damit die genutzten Units, deren Funktion und Inhalt. Was genau wo gemacht wird, darüber werden wir uns später unterhalten.

Bei eurer Projektstruktur solltet ihr vor allem darauf achten, nicht alles in eine Unit zu packen. Zum einen kann das später nämlich eingige Probleme machen (ich sage nur Kreuzbezüge) und zum anderen schadet es der Portabilität. Damit sind z.B. wiederverwendbare Programmteile wie der Texturenmanager oder das Soundsystem gemeint, die wenn sie in eine externe Unit ausgelagert werden, später ohne großen Aufwand in andere Projekte eingebunden werden können. Dadurch spart man sich über die Zeit sehr viel Arbeit und baut sich nach und nach mit jedem Projekt eine größere Codebasis auf.

Wichtig ist natürlich (wie auch bei euren Variablen) die Namensgebung. Es scheint zwar verlockend, Objekt- oder Unitnamen so kurz wie möglich zu halten (besonders dann wenn man schreibfaul ist), aber das wird sich früher oder später rächen. Wenn man dann nämlich dutzende Units mit kryptischen Namen in denen dutzende Objekte mit noch kryptischeren Namen verteilt sind quer auf der Platte verteilt hat, fällt die Wiederverwendung und Zuordnung recht schwer!

Projektunits

Nachfolgend gebe ich einen kurzen Überblick über alle dem Projekt zugehörigen Units und deren Inhalt.

Icon unit.gifNapalmBomber3D_1.pas

In dieser Unit befindet sich das Hauptformular unseres Bombermanklons. Hier findet neben der Initialisierung einiger wichtiger Variablen und Objekte nichts wirklich Interessantes statt.

Icon unit.gifBombermanGlobal.pas

Dies ist wie der Name schon vermuten lässt der Sitz unserer Codebasis und somit auch die wichtigste Unit unseres Projektes. Im letzten Tutorial hieß sie noch global.pas, wurde jedoch aufgrund der besseren Übersicht umbenannt.

Icon unit.gifBombermanTextureManager.pas

Naja, ich glaube es ist ein Einfaches anhand der Namensgebung herauszufinden was sich hinter dieser Unit versteckt. Im letzten Tutorial befand sich unser Texturenmanager noch in der globalen Unit. Allerdings will unser 3DS-Lader diesen Texturenmanager auch nutzen, und um Kreuzbezüge zu vermeiden habe ich ihn deshalb in eine extra Unit ausgelagert.
Vorteil ist, neben der Tatsache das er nun von mehrern Units genutzt werden kann, auch noch die praktische Tatsache das man diese Unit unverändert für seine anderen Projekte nutzen kann. Also quasi ein Texturenmanager mit Zukunft.

Icon unit.gifBombermanSoundSystem.pas

Auch hier sollte die Unitfunktion anhand der Namensgebung recht einfach zu erkennen sein. Hier befindet sich das (momentan noch recht einfache) Soundsystem. Vorteil der Auslagerung in eine estra Unit ist auch hier die Portabilität.

Icon unit.gifBombermanFont.pas

In dieser Unit befindet sich unser Fontsystem zur Ausgabe von Bitmapfonts.Mehr dazu später.

Icon unit.gifBombermanLoad3DS.pas

Diese Unit bietet einige einfache Methode um 3D-Modelle im 3DS-Format zu laden und in eine unter OpenGL darstellbare DisplayListe zu wandeln.
Die Unit basiert ursprünglich auf dem 3DS-Loader von Mike Lischke, und wurde von mir vereinfacht. Deshalb wirds hierzu keine genaue Erklärung geben.


Fremdunits

Nachdem ich vorher alle dem Projekt zugehörigen Units aufgezählt habe, gibts hier noch einen ganz kurzen Überblick über die Fremdunits, die vom Projekt benötigt werden.

Icon unit.gifUtils3DS.pas,Const3DS.pas,File3DS.pas,Types3DS.pas

Diese Units werden von unserem 3DS-Lader benötigt und stammen auch aus Mike Lischkes 3DS-Packet.

Icon unit.giffmod.pas,fmodtypes.pas,fmoddyn.pas,fmoderros.pas,fmodpresets.pas

Auch hier lässt die Namensgebung recht schnell auf die Units für die FMOD-Library schliessen, die als Grundlage für unser Soundsystem dient.

Icon unit.gifGLBmp.pas

Zu dieser Unit muß ich wohl kaum noch viele Worte verlieren, haben wir diese doch schon im Editor zum Laden der Texturen genutzt.
Quelle : http://delphigl.cfxweb.net

Icon unit.gifOpenGL12.pas

Auch zu dieser Unit gibts nicht viel zu sagen. Sie beinhaltet alle nötigen Deklarationen und Funktionen zur Nutzung von OpenGL (1.2).


Verwendete Techniken

Bevor wir uns also in den Quellcode stürzen, werde ich hier kurz die verwendeten Techniken, deren Nutzen und deren Umsetzung erklären. Alle hier erwähnten Dinge werdet ihr in weiteren Projekten verwenden können und sind für das Gelingen größerer Projekte fast unerlässlich, also lest aufmerksam!


Tutorial Bomberman2 bombermanbomb.gif

Timebased Movement

Bisher werden viele von euch (wenn sie denn schonmal ein kleines Spiel programmiert haben) die Ereignisse wie z.B. Spielerbewegung oder das aktualisieren einer Animation am Ende eines jeweiligen Frames abgearbeitet haben. Das ist zwar eine einfache Lösung, hat jedoch den riesigen Nachteil, dass Bewegungen und Animationen je nach Rendergeschwindigkeit (sprich Anzahl der pro Sekunde berechneten Bilder) verschieden sind. Daraus resultiert z.B., das sich eine Spielfigur auf einem schnellen Rechner unkontrollierbar schnell nach vorne bewegt, während sie auf einem langsamen Rechner kaum von der Stelle kommt. Besonders schlecht ist sowas in einem Netzwerkspiel, da der Spieler am langsamen Rechner durch die langsamere Bewegung einen großen Nachteil hat. Abhilfe schafft hier das sog. "Timebased Movement" (zu Deutsch : Zeitbasierte Bewegung), welches für gleichmäßige Bewegungen bzw. Animationen sorgt, in dem es (im Gegensatz zum "Framebased Movement") diese mittels Zeitmessung auf einen festen Faktor angleicht.

Die Implementation dieser Technik ist zum Glück recht einfach, bietet uns Windows doch netterweise (über einen im Chipsatz integrierten Timer) einen Hochpräzisionstimer dessen Auflösung um ein Tausendfaches höher ist als die eines Systemtimers. Damit wird es uns möglich die oft nur sehr geringen Zeitabstände zwischen dem Rendern der Frames zu berechnen. Ein kleines Beispiel : Ein moderner Rechner schafft heute in einer nicht besonders Aufwendigen 3D-Szene ohne Probleme an die 300-400 Bilder pro Sekunde. Dies bedeutet, das er zum Berechnen und Ausgeben eines Bildes 2,5ms (=1000ms/400fps) benötigt. Ein normaler Systemtimer würde uns eine solche Messung mit seiner "Genauigkeit" von ~55ms gänzlich unmöglich machen.

Windows bietet uns nun zwei Funktionen zur Nutzung dieses Hochpräzisionstimer an :

  • function QueryPerformanceFrequency(var lpFrequency : TLargeInteger): Bool;
Diese Funktion liefert uns in lpPerformanceCount die Anzahl der Takte pro Sekunde zurück, also die Auflösung des Timers. Dieser Wert variiert von Chipsatz zu Chipsatz und muß deshalb unbedingt beim Programmstart abgefragt und in einer Variable abgelegt werden.
Meine Hauptplatine auf Basis des nVidia nForce2-Chipsatz liefert z.B. den Wert 3579545, d.h. dass der Hochpräzisionstimer genau 3.579.545 mal pro Sekunde taktet.
  • function QueryPerformanceCounter(var lpPerformanceCount : TLargeInteger): Bool;
Obige Funktion liefert uns in lpPerformanceCount den aktuellen Stand des Hochpräzisionscounter, gemessen in Systemtakten zurück, und wird von uns zur genauen Zeitmessung genutzt.


Ermitteln der Counterfrequenz

Wie schon in der Funktionsbeschreibung erwähnt müssen wir vor der Nutzung des Hochpräzisionscounters erstmal dessen Frequenz ermitteln. Dies tun wir direkt beim Start unseres Programms im OnCreate-Ereignis des Hauptfensters :

procedure TBomberManForm.FormCreate(Sender: TObject);
begin
QueryPerformanceFrequency(PerfCounterFreq);
[...]
end;


Die Variable PerfCounterFreq wird in der Unit BombermanGlobal.pas deklariert und ist vom Typ Int64. In dieser ist von nun an die Frequenz unseres Hochpräzisionstimers hinterlegt. Sofern dieser Wert < 0 ist, läuft alles nach Plan.

Die Zeitmessung

Nach der recht langen Präambel widmen wir uns nun dem eigentlich Kern des Timebased Movements, nämlich der Zeitmessung. Diese wird in in der DrawScene-Prozedur unseres TBombermanGame-Objektes (BombermanGlobal.pas) umgesetzt :

procedure TBomberManGame.DrawScene;
var
 QPCStartCount, QPCEndCount : Int64;
begin
QueryPerformanceCounter(QPCStartCount);
[...]
// Szene zeichnen
[...]
QueryPerformanceCounter(QPCEndCount);


Der Quellcode spricht eigentlich für sich. Wir ermitteln insgesamt zwei Zeitwerte, zum einen vor dem Zeichnen der Szene und zum anderen nach dem Zeichnen der Szene, und speichern diese ab. Danach berechnen wir über eine recht einfache Formel unseren Zeitfaktor :

BomberManGame.TimeFactor := (QPCEndCount-QPCStartCount)/PerfCounterFreq*100;
end;


Dieser Zeitfaktor entspricht dann der zum Rendern benötigten Zeit in ms/10 und kann dann beim aktualisieren der Bewegungen bzw. Animationen als Faktor genutzt werden.

Kleine Rechenkunde

Um denn Sinn und die Funktion des Timebased Movement besser verstehen zu können, gibts hier ein paar kleine Rechenbeispiele, die darlegen dass der Code nun frameunabhängig Funktioniert :

Rechner 1 braucht 7 ms um die Szene zu Rendern

Zeitfaktor = 0.7
Bewegung des Spieler = 0.05*0.7 = 0,035 Einheiten
Bewegungen werden 142 mal pro Sekunde aktualisiert (142fps)
Gesamtbewegung/s = 142*0,035 = 4,97 Einheiten
Ohne Timebased Movement : Gesamtbewegung = 142*0,05 = 7,1 Einheiten

Rechner 2 braucht 11 ms um die Szene zu Rendern

Zeitfaktor = 1.1
Bewegung des Spieler = 0.05*1.1 = 0,055 Einheiten
Bewegungen werden 90 mal pro Sekunde aktualisiert (90fps)
Gesamtbewegung/s = 90*0,055 = 4,95 Einheiten
Ohne Timebased Movement : Gesamtbewegung = 90*0,05 = 4,5 Einheiten

Ich hoffe mal, dass diese beiden Beispiele verdeutlichen, das sich der Spieler unter Zuhilfenahme des Timebased Movements auf verschieden schnellen Rechner (und in verschieden komplexen Szenen) mit gleicher Geschwindigkeit bewegt. Die Diskrepanz vin 0,02 Einheiten bei obigen Werten ist darauf zurückzuführen, dass ich mit abgerundeten Werten gerechnet habe.

Auch wenn dieser Teil des Tuts etwas theoretisch und trocken war, so ist das Timebased Movement, wie ihr sicher schon aus dem Text herauslesen konntet unerlässlich für moderne Spieletitel, und zwar ganz besonders dann wenn diese später auch im Netzwerk bzw. über das Internet spielbar sein sollen. Allerdings wird sich Bomberman drüber freuen, das er sich jetzt auf jedem System gleichschnell fortbewegen kann... Bomberman wait.gif


Tutorial Bomberman2 bombermanbomb.gif

Statemachine

Auch diesen Begriff dürften einige von euch schonmal irgendwo aufgeschnappt haben. Vermutlich war das aber in einem anderen und weitaus komplexerem Zusammenhang als dies bei unserer (recht primitiven) Statemachine der Fall ist.

Spiele bestehen ja bekannterweise aus verschiedenen Szene (=States) wie z.B. dem Hauptmenü und der 3D-Ansicht. Die Statemachine macht in unserem Falle nichts anderes als den in TBombermanGame.CurrentState gesetzten Status abzufragen und dann in die entsprechende Zeichenroutine zu verzweigen. So hat man die für die einzelnen Szenen erforderlichen Zeichneroutinen in verschiedene Prozeduren separiert und kann später bequem und ohne großen Aufwand neue Szenen hinzufügen.

Für jede Szene deklarieren wir in der Konstantensektion der globalen Unit eine eigene Konstanten, anhand derer sie eindeutig identifiziert werden kann :

      const
       GSMainMenu  = $00;
       GSNewGame   = $01;
       GSInGame    = $02;
       GSMatchEnd  = $03;


Die Statemachine prüft dann in der Zeichenprozedur TBombermanGame.DrawScene in welchem Status sie sich befindet und verzweigt dann mittels einer einfachen Case-Anweisung zur entsprechenden Zeichneroutine :

      procedure TBomberManGame.DrawScene;
      [...]
      case CurrentState of
       GSMainMenu : DrawScene_GSMainMenu;
       GSNewGame  : DrawScene_GSNewGame;
       GSInGame   : DrawScene_GSInGame;
       GSMatchEnd : DrawScene_GSMatchEnd;
      else
       DrawScene_GSMainMenu;
      end;
      [...]


Den Status unserer Statemachine setzen wir dann mittels der Prozedur TBombermanGame.SetState. Aber warum tun wir das nicht direkt über die Variable TBombermanGame.CurrentState? Dafür gibts einen ganz einfachen Grund : Es ist möglich, dass beim Wechsel zwischen zwei Zuständen verschiedene Variablen geändert oder gesetzt werden müssen. Wechseln wir den Status also über oben genannte Prozedur, so können wir vor dem finalen Wechsel noch prüfen in welchen Status gewechselt wird, und eventuell noch benötigte Variablen setzen oder verändern.

Auch wenn oben beschriebenes recht banal erscheinen mag, so ist eine Statemachine (egal wie primitiv) ein einfaches Mittel zur Verwaltung des Spielzustandes, die sich dazu noch recht leicht erweitern lässt.


Tutorial Bomberman2 bombermanbomb.gif

Texturenfonts

Was vielen ein Dorn im Auge ist, und sich hoffentlich bald ändert, ist das Fehlen von Routinen zur Fontdarstellung in OpenGL. Aber zum Glück haben sich einige findige Programmierer zu diesem Thema Gedanken gemacht und Methoden zur Schriftdarstellung mittels OpenGL entwickelt die recht einfach zu nutzen und gleichzeitig schnell sind. Eine davon sind Texturenfonts, die wie deren Name schon vermuten lässt, ihre Schriftzeichen aus einer Textur herausholen.

Ein Tool das diese Texturen erstellen kann, wurde von mir bereits am Anfang dieses Tutorials vorgestellt, nämlich der Bitmap Font Builder. Dieser erstellt auf der Basis einer Windowsschriftart eine Textur die alle nötigen Schriftzeichen enthält, und z.B. so aussehen könnte :

Tutorial Bomberman2 texfont.jpg

Das Erstellen und Ausgeben dieser Schrift erledigt für uns die Klasse TTexFont in der Unit TBombermanFont.pas. Dort finden sich am Anfang in der Konstantesektion die vorgenerierten Texturenkoordinaten für die einzelnen Buchstaben. Da grundlegende Kenntnisse zum Thema Texturemapping eine Voraussetzung für dieses Tut sind, gehe ich davon aus das ihr wisst was diese zu bedeuten haben und wie sie funktionieren.

Unsere TTexFont-Klasse tut jetzt bei der Initialiserung nichts anderes, als für jedes in der Textur hinterlegte Schriftzeichen anhand der in der Konstantesektion festgelegten Texturkoordinate eine Displayliste zu erstellen, die beim Ausgeben des Textes dann nur noch aufgerufen wird.


Tutorial Bomberman2 bombermanbomb.gif

Animierte Texturen

In unserem Bombermanklon nutzen wir für die Explosionen animierte Texturen. Diese animierten Explosionen lassen sich auf zwei Arten realisieren :

Verschiedene Texturen

Wir laden für jede Explosionsphase eine eigene Textur. Dazu benötigen wir also 16 Texturen, die auf der Festplatte recht viel Speicher beanschlagen. Je nach Explosionsphase binden wir nun vor dem Zeichnen des Explosionsquad die zur aktuellen Explosionsphare gehöhrende Textur.

Diese Methode ist zwar recht einfach zu implementieren, hat jedoch einige Nachteile : Da jede Animationsphase in einer eigenen Textur liegt wird sowohl auf der Festplatte als auch im Grafikspeicher recht viel Overhead erzeugt.


Eine Textur

Dies ist die Lösung die wir verwenden werden, da sie recht viele Vorteile beherbergt. Wir erstellen uns mittels eines Tools (z.b. den am Anfang des Tuts erwähnten ExplosionsGenerator) eine Textur auf der in Reihen und Spalten alle Animationsphasen der Explosion dargestellt sind.

Nun binden wir für jedes Explosionsquad dieselbe Textur und verändern entsprechend der Animationsphase nur unsere Texturkoordinaten, die wir in einem speziellen Array abgelegt haben :

ExpTexCoord : array[0..15, 0..3] of Single =
  ((0, 0,    0.25, 0.25), (0.25, 0,     0.5, 0.25), (0.5, 0,    0.75, 0.25), (0.75, 0,    1, 0.25),
   (0, 0.25, 0.25, 0.5),  (0.25, 0.25,  0.5, 0.5),  (0.5, 0.25, 0.75, 0.5),  (0.75, 0.25, 1, 0.5),
   (0, 0.5,  0.25, 0.75), (0.25, 0.5,   0.5, 0.75), (0.5, 0.5,  0.75, 0.75), (0.75, 0.5,  1, 0.75),
   (0, 0.75, 0.25, 1),    (0.25, 0.75,  0.5, 1),    (0.5, 0.75, 0.75, 1),    (0.75, 0.75, 1, 1));


Wenn man sich dieses Array genauer betrachtet, dann kann man schon erahnen das unsere Explosionstextur in 4x4 Zellen aufgeteilt ist, und von links nach rechts verläuft. Links seht ihr die vom Explosionsgenerator erstellte Textur, und rechts selbige mit ein paar von mir eingetragenen Texturkoordinaten, die euch das Verständnis dieser Technik ein wenig erleichtern sollten :

Tutorial Bomberman2 explosion.jpgExplosion det.jpg

Wer also weiß wie man mit Texturkoordinaten umgeht dürfte hier absolut keine Verständnisprobleme haben. Unser Quad zeichnen wir dann wie folgt (siehe TMap.Draw) :

glBegin(GL_QUADS);
 glTexCoord2f(ExpTexCoord[Round(ExplosionPhase)][0], -ExpTexCoord[Round(ExplosionPhase)][3]);  glVertex3f(0, 0, 0);
 glTexCoord2f(ExpTexCoord[Round(ExplosionPhase)][2], -ExpTexCoord[Round(ExplosionPhase)][3]);  glVertex3f(2, 0, 0);
 glTexCoord2f(ExpTexCoord[Round(ExplosionPhase)][2], -ExpTexCoord[Round(ExplosionPhase)][1]);  glVertex3f(2, 2, 0);
 glTexCoord2f(ExpTexCoord[Round(ExplosionPhase)][0], -ExpTexCoord[Round(ExplosionPhase)][1]);  glVertex3f(0, 2, 0);
glEnd;


Wie leicht zu erkennen nehmen wir unsere Texturkoordinaten anhand der aktuellen Explosionsphase aus unserem vorher in der Konstantensektion deklariertem Koordinatenarray. Bei ExplosionPhase=0 sieht unser Quad also so aus :

glBegin(GL_QUADS);
 glTexCoord2f(0,    -0.75); glVertex3f(0, 0, 0);
 glTexCoord2f(0.25, -0.75); glVertex3f(2, 0, 0);
 glTexCoord2f(0.25,  0);    glVertex3f(2, 2, 0);
 glTexCoord2f(0,     0);    glVertex3f(0, 2, 0);
glEnd;


Der offensichtliche Vorteil dieser Methode ist die Tatsache, das die komplette Animation in einer Textur abgelegt ist, und somit nur eine Textur auf der Festplatte sowie im Grafikkartenspeicher Platz verbraucht.


Tutorial Bomberman2 bombermanbomb.gif

Soundsystem

Zu einer intensiven Spielumgebung gehören natürlich nicht nur aufwendige Grafiken, sondern auch Soundeffekte die dem Spieler das Gefühl vermitteln, er bewege sich inmitten einer dreidimensionalen Umgebung.


Hier springt unser Soundsystem ein (BombermanSoundSystem.pas), welches einfache Routinen zur Initialiserung und Ausgabe von Soundateien mitbringt.

Auf dem Markt gibt es inzwischen einige sehr gute Soundlibraries, die vor allem für Freewareprogramme kostenlos nutzbar sind. Deshalb machen wir von einer dieser Soundbibliotheken gebrauch, nämlich fmod, dessen Vorteile ein riesiger Funktionsumfang und die Nutzung aller gängigen Soundformate ist.

Unser Soundsystem tut deshalb nichts anderes als diese (sowieso schon einfach nutzbare) Bibiliothek zu kapseln. So kann man später einfach auf eine andere Bibliothek umsteigen, oder sein Soundsystem komplett selbst z.B. unter Verwendung von DirectSound programmieren.


Auf die Funktionsweise bzw. Implementation von fmod werde ich hier jedoch nicht genauer eingehen. Die mit dieser Bibliothek gelieferten Hilfedateien und Beispiele sind so ausführlich, dass jede weitere Dokumentation Zeitverschwendung wäre.

Unsere Implementation der FMOD-Library ins Soundsystem werden wir später aber noch genauer betrachten.


So viel also zu den verwendeten Techniken. Fehlen tut in der Liste zwar der Texturenmanager, diesen haben wir allerdings schon im letzten Tutorial besprochen, das ihr ja alle bestimmt aufmerksam gelesen habt ;-)


Verwendete Dateiformate

Natürlich sollte man sich bei der Programmierung eines Spiels Gedanken über die verwendeten Dateiformate machen. Hier braucht man das Rad nicht neu zu erfinden und sollte wenn möglich auf bereits bestehende Formate und Editoren zurückgreifen. Ich werde jetzt alle in unserem Bombermanklon verwendeten Formate auflisten, kurz begründen warum wir diese verwenden und auch Alternativen nennen.


3D-Modelle

Für unsere 3D-Modelle (Spieler, Bomben) nutzen wir das sehr weit verbreitet 3DS-Format, in das fast alle gängigen 3D-Modellierprogramme exportieren können. Ein weiterer Vorteil neben der breiten Unterstützung durch gängige Programme ist die Existenz diverser 3DS-Laderoutinen in allen möglichen Programmiersprachen. Großer Nachteil dieses Formates sind Animationen, die auf Keyframe-Basis gesichert werden und deshalb recht schlecht zu implementieren sind, weshalb der genutzte 3DS-Loader diese auch nicht lädt. Als Alternative (vielleicht gibts dazu etwas im nächsten Tutorial) schlage ich deshalb das Milkshapeformat vor, zu dem es auf DGL übrigens eine praktische Ladebibliothek gibt.

Weitere Infos zum 3DS-Format gibts auf [www.wotsit.org www.wotsit.org].

Auf der Suche nach 3D-Modellen sollten vor allem folgende Seiten gute Anlaufstellen sein :


Sounds

Soundformate exisitieren inzwischen reichlich, und dank der FMOD-Library sind wir in dieser Hinsicht auch sehr flexibel. Da wir unser Spiel aber im Netz vertreiben wollen, nutzen wir für größere Sounds (also z.B. die Hintergrundmusik) das OGG-Vorbis-Format. Nicht nur das es ggü. dem MP3-Format bessere Qualität bei gleicher Komprimierung bietet, nein es ist sogar ein komplett gebührenfreies Format und kann deshalb selbst in kommerziellen Anwendungen ohne Copyrightprobleme genutzt werden. Für kleinere Sounds wie z.B. die Bombenexplosion nutzen wir weiterhin das altbewährte Wave-Format.

Sounds finden sich im Netz leider weitaus schwerer als 3D-Modelle und Texturen, ein guter Anfang bildet allerdings folgende Page : http://www.findsounds.com


Texturen

Auch hier sind wir bei der Wahl des Formates dank glBMP.pas recht flexibel. Allerdings wollen wir auch Alphamasking unterstützen, eine Technik bei der man vorher im Bildbearbeitungsprogramm markierte Bereiche durchsichtig machen kann. Deshalb benötigen wir ein Format das die Speicherung des Alphakanals unterstützt.Dazu kämen entweder TGA oder PNG in Frage. Da PNG jedoch recht neu ist und es für Delphi kaum Loader gibt, verwenden wir für unsere Texturen das TGA-Format.

Texturen für Spiele in Hülle und Fülle, und dazu noch in der Public Domain gibts übrigens beim WadFather.


Karten

Natürlich müssen wir auch ein Format für unsere selbsterstellten Karten wählen. Dieses Kapitel haben wir ja schon hinreichend im ersten Tutorial durchgekaut, weshalb ich hier nicht weiter drauf eingehen werde.


Der Quellcode

So Leute, jetzt gehts ans Eingemachte. Während der Quellcode zum Editor noch leicht überschaubar, recht kurz und die Anzahl der Objekte/Klassen sehr gering war, gehts jetzt etwas heftiger zur Sache, da unser Projekt bereits auf weit über 1000 Zeilen angewachsen ist.

Ich werde aber mein Bestes geben und versuchen alle wichtigen Codestellen, Variablen und Prozeduren möglichst detailliert und leicht nachvollziehbar zu erklären. Wenns doch noch irgendwo happert und selbst die Kommentare im Quellcode keine Lösung bringen, dann gibts ja immer noch das DGL-Forum in dem ich dann auf eure Fragen gerne eingehe... [Bild:Bomberman Right.gif]


Grundgedanken

Bevor ihr wild drauflos programmiert, solltet ihr euch natürlich über die in eurem Spiel benötigten Klassen, Objekte und Variablen Gedanken machen. Bei einem bereits vorgegebenem Spielprinzip wie Bomberman ist das natürlich eine recht einfache Sache. Hier brauchen wir Klassen (bzw. Objekte) die unser Spielfeld, die Spieler und gelegte Bomben repräsentieren. Des weiteren gibts noch die Klasse TBombermanGame, welche das eigentliche Spiel kapselt und neben Routinen zur Initialisierung auch zur Verwaltung der globalen Spiellogik dient.

Wie ihr seht, ist es also essentiell sich über seine Objekte und Klassen Gedanken zu machen, denn besonders in der OOP sind dies mächtige Werkzeuge um den Code leichter nutzbar und übersichtlicher zu machen. In größeren Projekten spielt diese Planung natürlich eine weit wichtiger Rolle, kann man sich doch viel Arbeit mittels Vererbung sparen.

Wenn ihr nun z.B. KI-gesteuerte Monster in unseren Bombermanklon integrieren wollt, so könntet ihr euch eine TActor-Klasse schreiben, die sich um Kollsionsabfrage und das Zeichnen der Figur kümmert und davon sowohl die menschlich gesteuerten Figuren (TPlayer) als auch die KI-Aktoren (TKIActor) ableiten.


Auslagern von Klassen/Objekten

Ich hab es im Verlaufe dieses Tuts bereits kurz angeschnitten (bei der Unitauflistung). Klassen bzw. Objekte die nicht spezifisch auf das Spiel zugeschnitten sind (wie z.B. der Texturenmanager oder das Soundsystem) sollten am besten in eine externe Unit ausgelagert werden.

Dies hat den Vorteil, das man sich so nach und nach eine große Codebasis schafft die man dann später in anderen Projekten nutzen kann. So werdet ihr dann nur noch einen Texturenmanager programmieren müssen, den ihr von nun an in jedes andere Projekt einbinden und sofort nutzen könnt. Ein weiterer Vorteil dieser Methode ist die Tatsache, dass eine Erweiterung des Texturenmanagers z.B. direkt in jedem eurer Projekte verfügbar ist. Deshalb wäre es keine schlechte Idee, wenn ihr euch für solche Units einen eigenen Ordner erstellt und in euren Projekten direkt auf die dort befindlichen Units linkt.


BombermanGlobal.pas Zu aller erst kümmen wir uns um unsere stark erweiterte Codebasis, zu der sich einige neue Objekte und Klassen gesellen. Im Folgenden werde ich euch zu jedem Objekt bzw. jeder Klasse die Deklaration liefern und wichtige Codestellen und Prozeduren detailliert erläutern :


Das Spielfeld (TMap)

Dieses Objekt haben wir ja bereits im ersten Tutorial besprochen. Hier ist nichts dazugekommen, weshalb wir uns mit diesem nicht weiter beschäftigen werden.


Die Spielfigur (TPlayer)

Dieses Objekt repräsentiert die Spieler und kümmert sich um deren Verwaltung. Die Liste der Spieler wird in dem dynamischen Array Player verwaltet, so daß es in der Anzahl der Spieler rein theoretisch keine Grenzen gibt. Einige der Eigenschaften dieses Objektes wie z.B. Size verändern sich im Verlaufe des Spielgeschehens nicht. Der Grund warum ich diese dennoch implementiert habe liegt darin, das unser Klon später mit aufsammelbaren Extras erweitert wird, die einige Eigenschaften des Spielers ähnlich dem Original beinflussen. Außerdem lässt sich über diese Eigenschaften das Gameplay recht einfach anpassen, ohne das man diverse Stellen im Quellcode nachträglich verändern muß.

Das Objekt TPlayer bringt folgende Eigenschaften und Funktionen mit :

Position : TVector3f; 
Die aktuelle Spielerposition auf dem Spielfeld .Hierzu verwenden wir einen Fließkommavektor, da sich der Spieler frei und nicht nur Feld für Feld bewegen soll.
ExplosionRadius : Integer; 
Gibt den Explosionsradius der vom Spieler gelegten Bomben an.
MaxBombs : Integer; 
Die Anzahl der von diesem Spieler maximal gleichzeitig platzierbaren Bomben.
Direction : Integer; 
Die Blickrichtung des Spielers.Diese Variable wird nur genutzt um das 3D-Modell in die korrekte Richtung zu drehen.
Wins : Integer; 
Anzahl der von diesem Spieler gewonnenen Spiele.
IsDead : Boolean; 
Wie fast alle anderen Variablen ist auch diese selbstredend. Wenn diese True ist, dann hats den Spieler erwischt und er kann nicht mehr am Spiel teilnehmen.
Speed : Single; 
Bewegungsgeschwindigkeit des Spielers.
Size : Single; 
Die Ausmaße der Spielfigur.
Model3DS : String; 
Der Name des zu diesem Spieler gehörenden 3D-Modells.
KeyUp, KeyDown, KeyLeft, KeyRight, KeyFire : Word 
Die Tastenbelegung des Spielers.
function IsPositionValid(pNewPosX, pNewPosZ : Single) : Boolean; 
Diese Funktion implementiert die (zum Glück recht einfache) Kollisionsabfrage, und prüft ob die Position an die sich der Spieler bewegen will auch begehbar ist. Dabei wird geprüft, ob das Feld unter der neuen Position begehbar ist, ob Kollision mit anderen Spielern stattfindet und ob Bomben im Weg liegen.
Wenn die neue Position gültig ist, dann liefert diese Funktion den Wert TRUE zurück.
procedure UpDate; 
In dieser Version unseres Bombermanklons hat diese Prozedur nur den einzigen Zweck zu prüfen ob sich der Spieler in einer Explosion befindet. Ist dies der Fall, so stirbt er und es wird ein entsprechender Sound abgespielt.
procedure Draw; 
Wie der Name bereits vermuten lässt kümmert sich diese Prozedur um das Zeichnen der Spielfigur und deren Schattens. Die hier verwendeten Schatten sind jedoch von der Sorte "Ganzbillig" und sind nichts anderes als auf den Boden geblendete Texturen. Für unseren Bombermanklon sind sie jedoch ausreichend und geben der Szene etwas mehr an Realismus.
procedure DropBomb; 
Auch dieser Prozedurnamen lässt bereits auf ihre Funktion schliessen. Sie prüft, ob die Zahl der vom Spieler gelegten Bomben nicht die maximale Anzahl an legbaren Bomben überschreitet und legt platziert eine Bombe auf dem Spielfeld.
procedure ProcessInput; 
Auch hier gibts nix besonderes. Mittels GetAsyncKeyState prüfen wir auf Hardwareebene ob eine Taste im gedrückten Zustand ist und führen dann die damit verbundene Aktion aus. Ist die übergebene Taste gedrückt, wird das höchstwertige Bit des zurückgegebenen Wertes gesetzt.
Im Falle einer Bewegung prüfen wir natürlich nach, ob die neue Position des Spielers auch gültig ist.
Einzige Besonderheit ist hier die Nutzung unseres vorher berechneten Zeitfaktors BomberManGame.TimeFactor als Faktor für die Bewegungsgeschwindigkeit.


Bomben (TBomb)

Auch die von den Spielern legbaren Bomben habe ich in einer Klasse gekapselt um deren Verwaltung und Aktualisierung zu erleichtern. Hier gibts eigentlich nix Spektakuläres, weshalb ich direkt zu den Eigenschaften und Prozeduren übergehe :

Position : TVector3i; 
Die Position der Bombe als Integer-Vektor, da Bomben nur genau in der Feldmitte abgelegt werden (wie beim original Bomberman).
Owner : ^TPlayer; 
Zeiger auf den Besitzer dieser Bombe.Diese Eigenschaft wird benötigt um herauszufinden wie viele Bomben dieser Spieler bereits gelegt hat, und um die Explosionsreichweiter der Bombe (die auch spielerabhängig ist) zu ermitteln.
CountDown : Single; 
Wie der Eigenschaftsname bereits vermuten lässt ist hier der Countdown bis zur Explosion der Bombe gespeichert. Sinkt dieser Wert auf >= 0, so wird die Explosion eingeleteitet und die Bombe aus der dynamischen Liste entfernt.
AnimPhase : Single; 
Die Animationsphase wird als Grundlage für die sinusförmigen Skalierung der Bombe genutzt und lässt so den Eindruck entstehen als würde die Bombe sich strecken und wieder zusammenziehen.
procedure Draw; 
Kümmert sich um das Zeichnen der Bombe sowie deren Schattens.
procedure Explode; 
Startet die lineare Explosion in vier Richtungen. Wie im Original wird die Explosion in die jeweilige Richtung durch einen Block gestoppt. Ist dieser Block sprengbar, so wird er vom Spielfeld entfernt.
Trifft die Explosion auf ihrem Weg auf eine andere Bombe, so wird der Countdown dieser Bombe auf einen Wert nahe 0 gesetzt, so daß eine Kettenrekation stattfindet.


Das Spiel (TBomberManGame)

Wie bereits erwähnt repräsentiert dieses Objekt unser Spiel und kümmert sich neben der Verwaltung dynamischer Listen und der Spiellogik auch um die Initialisierung wichtiger Listen bzw. OpenGL und implementiert unsere bereits ausgiebig beschriebene Statemachine. Bei Erweiterung solltet ihr hier alles was global geschieht und nicht von den Objekten selbst getan werden kann unterbringen.

Private Deklarationen

ShakeScreen : Single; 
In dieser Eigenschaft wird die Restdauer gespeichert während der der Bildschirm nach einer Bombenexplosion erschüttert wird.
procedure DrawScene_GSMainMenu/DrawScene_GSNewGame/DrawScene_GSInGame/DrawScene_GSMatchEnd; 
Wie bereits im Kapitel zur Statemachine angesprochen sind dies die Unterprozeduren die von der Statemachine zum Zeichnen der verschiedenen Spielstadien (sprich Szenen) genutzt werden.

Öffentliche Deklarationen

gsMainMenu_Selected : Integer; 
Wie am Präfix dieser Variable zu erkenne, gibt sie den im Hauptmenü ausgewählten Menüpunkt wieder.
gsMainMenu_Rotation : Single; 
Die Rotation der im Hintergrund des Menüs dargestellten Karte.
gsNewGame_Selected : Integer; 
Im Menü "Neues Spiel" gewählter Menüpunkt.
gsNewGame_Players : Integer; 
Anzahl der im Menü "Neues Spiel" eingestellten Spieler.
MapList : TStringList; 
In dieser Stringlist werden mit der Prozedur CreateMapList alle Namen der in einem bestimmten Ordner gefundene Karten abgelegt, so daß diese im Spielmenü bequem ausgewählt werden können.
ExtremeMode : Boolean; 
Der Name lässts wohl bereits vermuten. Hiermit wird dem Spiel mitgeteilt das es sich im Extremmodus befindet, in dem sowohl Anzahl der legbaren Bomben als auch deren Reichweite und die Geschwindigkeit der Spielfigur stark erhöht werden.
MapListPos : Integer; 
Position der gewählten Karte in MapList
TimeFactor : Single; 
Diese Variable wurde bereits im Kapitel zum Thema "Timebased Movement" besprochen und behinhaltet den Zeitfaktor der für eine gleichmäßige Bewegung/Animation sorgt.
OwnerForm : ^TForm; 
Zeiger auf die Form in der das Spiel dargestellt wird. Dieser Zeiger muß einen Wert besitzen und darf nicht ins Leere zeigen.
CurrentState : Word; 
Aktueller Status der Statemachine.
procedure SetState(pNewState : Word); 
Versetzt die Statemachine in einen neuen Status.
procedure CreateMapList(pMapDir : String); 
Erstellt eine Liste aller in pMapDir' befindlichen Karten, so daß auf diese einfach und schnell zugegriffen werden kann.
procedure Create3DSList(p3DSDir : String); 
Erstellt eine Liste aller in p3DSDir befindlichen 3DS-Modelle und generiert auch gleichzeitig deren Displaylisten.
procedure InitOpenGL; 
Erstellt den OpenGL-Rendercontext und initialisiert einige OpenGL-Parameter.
procedure DrawScene; 
Diese Prozedur verzweigt je nach Zustand der Statemachine in die entsprechende Zeichenprozedur.
procedure UpDateBombs; 
Aktualisiert die dynamische Bombenliste und entfernt ggf. explodierte Bomben. Diese Aktion muß außerhalb des TBomb-Objektes getätigt werden, da sich ein Objekt nicht selbst aus einer dynamischen Liste entfernen kann.
procedure StartNewMatch(pMapName : String); 
Wie der Prozedurname vermuten lässt starte diese eine neues Match auf der in pMapName angegeben Karte. Dabei wird die Bombenliste geleert und es werden alle Spieler wieder auf die Grundwerte zurückgesetzt.

Soviel also zur Deklaration unserer Objekte bzw. Klassen. Wie ihr sehen könnt habe ich hier eine sehr saubere Namensgebung verwendet, durch die viele Eigenschaften bzw. Prozeduren selbsterklärend sind.

Wenden wir uns nun einigen interessanten Stellen im Quellcode zu, die eine genauere Erklärung notwendig machen.


Interessante Codestellen