Hi,
so.. nun kommt der 2te Teil
Bewegung im 3D-Raum
Ich zeige euch diesmal wie ihr euch mit den Pfeiltasten durch eine 3D Landschaft bewegen könnt
Als erstes solltet ihr eure scene aus dem ersten Tutorial wieder laden, bzw eine neue leere OpenGL Scene erstellen.
Fangen wir also an..
Kümmern wir uns erstmal um die "Landschaft", und erstellen uns eine Procedure "DrawLandscape":
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| procedure TForm1.DrawLandscape; var j, i: Integer; r, wi, w, x, y: Double; begin wi:=360/40; glColor3f(0.0, 1.0, 0.0); glBegin(GL_LINES); for i:=1 to 40 do begin w:=(PI/180)*(wi*i-(wi/2)); x:=(-5/2)+(5/2+cos(w)*(5+5)/2); y:=(5/2)+(-5/2+sin(w)*(-5-5)/2); glVertex3f(0.0, -0.4, 0.0); glVertex3f(x, -0.4, y); end; glEnd; r:=0; for j:=1 to 11 do begin glBegin(GL_LINE_LOOP); for i:=1 to 40 do begin w:=(PI/180)*(wi*i-(wi/2)); x:=(-r/2)+(r/2+cos(w)*(r+r)/2); y:=(r/2)+(-r/2+sin(w)*(-r-r)/2); glVertex3f(x, -0.4, y); end; glEnd; r:=r+0.5; end; end; |
Ich weiß, das sieht jetzt ziemlich verwirrend aus, aber kopiert sie erstmal nur mit Copy&Paste rein, ruft sie in der DrawScene-Procedur auf, startet das programm und seht euch an wie die Landschaft aussieht...
Ihr solltet jetzt lauter kreise sehen die durch linien vom mittelpunkt zum äussersten Kreis verbunden sind. Und nichts anderes tun wir in der Procedure, das was da jetzt für den ein oder anderen vieleicht verwirrend aussieht ist die berechnung der Eckpunkte des Kreises.
In der ersten For-Schleife werden die Linien vom Mittelpunkt nach aussen gezeichnet (GL_LINES), in der Zweiten die Kreise ansich (GL_LINE_LOOP).
Aber genug dazu.. hier geht es darum wie man sich nun bewegt, nicht wie man Kreiskoordinaten ausrechnet
Ok, überlegen wir uns erstmal wie wir rauskriegen wollen ob eine Taste gedrückt wurde, und wie wir das dann verarbeiten..
Natürlich mit dem OnKeyDown-Event, aber.. jeder der das schonmal benutzt hat um eine Bewegung zu erziehlen wird wissen, das es zwischen dem Drücken der taste, und der dauerhaften bewegung eine verzögerung gibt.
Ein beispiel hierfür wäre z.B. ein Panel das man mit jedem druck auf die Leertaste um 1Pixel nach rechts schiebt. Drückt man nun die Leertaste, und hält sie gedrückt, rutscht das Panel 1 Pixel nach rechts, bleibt dort erstmal einen moment, und fährt dann fließend weiter nach rechts. Tastenverzögerung eben
Nun, da wir aber damit nichts anfangen können, müßen wir uns etwas einfallen lassen... eine Boolean-Variable.
Wir setzen sie True wenn die Taste gedrückt wird, und false wenn sie losgelassen wird. Und damit wir nich für jede Taste eine Variable brauchen, nehmen wir einen Array:
Delphi-Quelltext
1: 2:
| var Keys: Array[0..255] of Boolean; |
0..255 daher, weil es auf der Tastatur 256 Tasten gibt, und wir somit für jede Taste die man drücken kann eine Variable haben
(Ok, wenn das Spiel nur 2 Tasten brauch wäre ne überlegung 2 Variablen zu benutzen nich verkehrt.. *g*)
Aber ok.. lassen wir das ganze erstmal so. Jetzt müßen wir nurnoch im OnKeyDown und OnKeyUp die entsprechende Variable auf True/False setzen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin Keys[Key]:=true; end;
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin Keys[Key]:=false; end; |
Ganz einfach
Denn Key ist ja die Nummer der Taste die gedrückt wurde, und da die höchste Nummer nunmal auch die letzte Taste ist, ist Key maximal 255.
Als nächstes brauchen wir eine Procedur in der wir überprüfen ob eine Taste gedrückt ist, und was passieren soll wenn sie gedrückt ist. Also schreiben wir eine Procedure CheckKeys:
Delphi-Quelltext
1: 2: 3: 4:
| procedure Form1.CheckKeys; begin
end; |
Für den anfang reicht uns erstmal die Rotation, für welche wir nur 2 Tasten benötigen: Die linke + rechte Pfeiltaste. Doch zu allererst brauchen wir eine globale Variable "Rotate" vom Typ Single, in der wir die aktuelle Rotation speichern.
Delphi-Quelltext
In unserer CheckKeys Procedure prüfen wir nun ob die linke (VK_LEFT) oder rechte (VK_RIGHT) Pfeiltaste gedrückt ist, und erhöhen bzw. verringern unseren Rotationswert.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| if Keys[VK_LEFT] then begin Rotate:=Rotate - 0.5; if Rotate<0 then Rotate:=Rotate + 360; end; if Keys[VK_RIGHT] then begin Rotate:=Rotate + 0.5; if Rotate>360 then Rotate:=Rotate - 360; end; |
Wie immer auch hier die überprüfung ob der Winkel >360 oder <0 ist.
Ich denke bis hier sollte alles klar sein, kümmern wir uns nun darum das unsere Scene sich auch dreht, wenn wir die Taste drücken. Hierzu müßen wir als erstes mal die CheckKeys Procedur am anfang der DrawScene Procedur aufrufen, also jedesmal wenn unsere Scene gerendert wird.
Nun noch ein glRotate rein, wo wir unsere Scene um den Winel
Rotate auf der Y-Achse drehen.
Nun sollte die DrawScene-Procedur so aussehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TForm1.DrawScene; begin glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glLoadIdentity;
CheckKeys; glRotate(Rotate,0,1,0); DrawLandscape;
SwapBuffers(h_DC); end; |
Wenn ihr euer Programm nun startet, solltet ihr euch mit den Pfeiltasten nach links/rechts drehen können. Jenachdem was ihr für einen PC habt, kann das nun rasend schnell sein, oder sehr sehr langsam... denn bisher ist unsere Bewegung noch FPS-Abhängig, also jedesmal wenn unsere Scene gezeichnet wird, wird Rotate um 0.5 erhöht (sofern die Taste gedrückt ist), schafft der PC es nun die Scene 500 mal pro Sekunde zu zeichnen, wird Rotate pro sekunde um 250 erhöht... (also fast eine ganze umdrehung), schafft der PC aber z.B. nur 10 FPS (FPS = Frames per second = Bilder pro Sekunde), würde sich Rotate nur um 5 pro Sekunde erhöhen..
Was machen wir also?? Genau, das ganze FPS unabhängig!
Um das zu realisieren müßen wir erstmal rausbekommen wieviel FPS unser PC schafft, bedienen wir uns für den anfang mal eines einfachen TTimer.
Erstmal aber 2 neue Variablen..
RealFPS und
FPS vom Typ Integer. In RealFPS speichern wir die FPS die unser PC schafft, FPS ist ein counter.. er zählt die frames, also wird bei jedem DrawScene aufruf um 1 erhöht.
Delphi-Quelltext
1: 2: 3:
| var RealFPS: Integer = 100; FPS: Integer; |
RealFPS wird mit 100 Initialisiert, da es sonst in der ersten Sekunde ein FPS Rate von 0 gäbe, was später bei der Rechnung doofe nebeneffekte hätte
Klickt euch nun einen TTimer auf euer Form und stellt den Intervall auf 1000. Ein doppelklick auf den Timer bringt euch in das OnTimer-Event wo ihr folgende Zeile reinschreibt:
Delphi-Quelltext
1: 2:
| RealFPS:=FPS; FPS:=0; |
FPS ist also der Counter, der bei jedem DrawScene Aufruf erhöht wird. Jede Sekunde wird nun der Counter zurück auf 0 gesetzt, denn wir wollen ja wissen wieviel FPS wir in einer Sekunde schaffen.
In RealFPS steht nun also die FPS-Zahl die wir brauchen
Wie wenden wir das ganze nun auf unsere Rotation an?
Nehmen wir mal an, unser Programm hätte bei 100 FPS die Optiomal geschwindigkeit.
Bei 100FPS würde Rotate um 50 / Sek erhöht werden.
Bei 200FPS würde Rotate um 100 / Sek erhöht werden...
Damit jetzt bei jeder FPS Zahl Rotate um den gleichen wert / Sekunde erhöht wird benutzen wir FPS als Faktur:
Delphi-Quelltext
1:
| Rotate:=Rotate + (0.5 * (100 / RealFPS)) |
wäre FPS nun 200, würde Rotate um 0,25 erhöht werden... bei 50 FPS würde es um 1 erhöht werden
Das gleiche macht ihr nun noch für die andere Pfeiltaste (einfach anstelle + nen - ) und fertig ist unsere FPS unabhängige drehung
Kümmern wir uns nun darum das wir auch vorwärts und rückwärs laufen können. Hierfür benötigen wir 2 weitere Variablen:
Delphi-Quelltext
1: 2:
| var PosX, PosZ: Single; |
In diesen beiden wird unsere aktuelle X, bzw Z Position gespeichert.
Nun fügen wir in unserer CheckKeys Procedur noch diese paar Zeilen hinzu:
Delphi-Quelltext
1: 2: 3: 4:
| if Keys[VK_UP] then PosZ:=PosZ + 0.1 * (100 / RealFPS); if Keys[VK_DOWN] then PosZ:=PosZ - 0.1 * (100 / RealFPS); |
Wird die Nachoben-Pfeiltaste gedrückt wird PosZ um 0.1 ( * Faktor) erhöht. PosZ daher, weil wir ja in den Bildschirm hineinlaufen wollen und uns somit auf der Z-Achse bewegen.
Noch kann es aber nicht funktionieren, denn uns fehlt noch die Zeile in der wir uns um PosZ bewegen.. nämlich welche...?? Genau, glTranslate
Delphi-Quelltext
1:
| glTranslate(PosX,0,PosZ); |
Die Zeile gehört zwischen glRotate und DrawLandscape. Ich denke mal hierzu brauche ich nichts weiter zu sagen.. wir verschieben unsere Scene um den Wert von PosX auf der X-Achse und um den Wert von PosZ auf der Z-Achse.
Wenn ihr das Programm nun startet, solltet ihr mit den Pfeiltasten etwas rumlaufen können.
Bisher bewegen wir uns aber nur auf der Z-Achse, also egal wie wir uns drehen, wir laufen immer nur auf einer geraden entlang.
Hier kommt nun wieder ein kleinwenig Mathe in's spiel, wir müßen nämlich anhand unserer aktuellen PositionsKoordinaten und dem Winkel in den wir gucken berechnen, in welche richtung wir laufen.
Für vorwärts (VK_UP) sähe das so aus:
Delphi-Quelltext
1: 2:
| PosX:=PosX + Sin(-Rotate * (PI / 180)) * (0,1 * (100 / RealFPS)); PosZ:=PosZ + Cos(-Rotate * (PI / 180)) * (0,1 * (100 / RealFPS)); |
Im grunde eine einfach SinusFunktion.. Das PI/180 brauchen wir, da die Sin() Funktion einen Wert im Bogenmaß erwartet.
Das ganze noch für die Pfeiltaste nach unten, und unsere CheckKeys Procedur sollte so aussehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| procedure TForm1.CheckKeys; begin if Keys[VK_LEFT] then begin Rotate:=Rotate - 2 * (100 / RealFPS); if Rotate<0 then Rotate:=Rotate + 360; end; if Keys[VK_RIGHT] then begin Rotate:=Rotate + 2 * (100 / RealFPS); if Rotate>360 then Rotate:=Rotate - 360; end; if Keys[VK_UP] then begin PosX:=PosX + Sin(-Rotate * (PI / 180)) * (0.1); PosZ:=PosZ + Cos(-Rotate * (PI / 180)) * (0.1); end; if Keys[VK_DOWN] then begin PosX:=PosX - Sin(-Rotate * (PI / 180)) * (0.1); PosZ:=PosZ - Cos(-Rotate * (PI / 180)) * (0.1); end; end; |
Wir ihr seht habe ich den Wert beim Rotate von 0.5 auf 2 erhöht, da mir 0.5 einfach zu langsam war
Wenn ihr nun aber euer Programm startet, solltet ihr euch mit den 4 Pfeiltasten hin und her bewegen können.
Nun bastelt ihr euch eine kleine schöne landschaft drumherum, und fertig ist das erste.. äh... das erste... na, macht einfach mal
Au'revoir,
Aya~~