Timebased Movement: Unterschied zwischen den Versionen
Flo (Diskussion | Beiträge) K (→Links: Link zum Bomberman Tutorial korregiert.) |
(→Absolute Genauigkeit per Hardware) |
||
Zeile 86: | Zeile 86: | ||
=== Absolute Genauigkeit per Hardware === | === Absolute Genauigkeit per Hardware === | ||
− | Man kann auch die Hardware zur Zeitmessung nutzen um so noch genauere Ergebnisse zu | + | Man kann auch die Hardware zur Zeitmessung nutzen um so noch genauere Ergebnisse zu erzielen. Es kann jedoch theoretisch sein, dass diese nicht zur Verfügung steht. Zudem handelt es sich hierbei um reine Windows API-Aufrufe. Bei Verwendung von FreePascal bzw. Lazarus sollte diese Methode also nicht in betracht gezogen werden. |
+ | |||
<pascal> | <pascal> | ||
//Benötigte Variablen | //Benötigte Variablen | ||
Zeile 100: | Zeile 101: | ||
QueryPerformanceCounter(BerechnungsZeit); // Aktuelle Zeit ermitteln | QueryPerformanceCounter(BerechnungsZeit); // Aktuelle Zeit ermitteln | ||
end; | end; | ||
+ | |||
procedure BerechnungsCheck; // Alle nötigen Berechnungen durchführen | procedure BerechnungsCheck; // Alle nötigen Berechnungen durchführen | ||
var | var | ||
Zeile 108: | Zeile 110: | ||
BerechnungsZeit := AktuelleZeit; | BerechnungsZeit := AktuelleZeit; | ||
end; | end; | ||
+ | |||
procedure Berechne(vergangene_Zeit : Double); | procedure Berechne(vergangene_Zeit : Double); | ||
const | const | ||
Zeile 118: | Zeile 121: | ||
</pascal> | </pascal> | ||
− | Man hat nun den | + | Man hat nun den Geschwindigkeitsfaktor, den man mit sämtlichen Bewegungen multipliziert. Wenn der PC viele Frames per Second rendert wird der Geschwindigkeitsfaktor klein. Die Bewegungen werden dadurch naturlich auch kleiner. Je niedriger die Ausführungsgeschwindigkeit der Zeichenprozedur, desto höher wird der Geschwindigkeitsfaktor, die Bewegungsschritte also auch größer. |
== Konstant Beschleunigte Bewegung == | == Konstant Beschleunigte Bewegung == |
Version vom 22. Juni 2008, 14:39 Uhr
Inhaltsverzeichnis
Kurz-Beschreibung
Timebased Movement sorgt dafür, dass die Bewegungsgeschwindigkeiten nicht vom CPU-/Renderspeed abhängen.
Prinzip
Nehmen wir an wir möchten einen Ball darstellen der sich mit konstanter Geschwindigkeit vom linken Bildschirmrand zum rechten Bildschirmrand bewegt.
Das erste Bild wird den Ball ganz links zeigen. Auf dem nächsten Bild wird der Ball ein Stück weiter sein, die Frage ist nur wie weit? Die Länge der Strecke die der Ball zurücklegen muss legen wir nun mal willkürlich auf 1 Längeneinheit (kurz: LE) fest. Die Strecke soll er in 5 Sekunden zurück legen.
Ein Rechner A braucht 1 Sekunde um 1 Bild zu Berechnen. Um das gewünschte Ergebnis zu erreichen müssten wir den Ball nach jedem Bild 0.2 Felder weiterbewegen.
In einer Sekunde hätten wir also folgende Bilder:
- Bild 0: Ball Position: 0.0
- Bild 1: Ball Position: 0.2 (weiterbewegt um 0.2 LE in 1.0 Sekunden)
- Bild 2: Ball Position: 0.4 (weiterbewegt um 0.2 LE in 1.0 Sekunden)
- Bild 3: Ball Position: 0.6 (weiterbewegt um 0.2 LE in 1.0 Sekunden)
- Bild 4: Ball Position: 0.8 (weiterbewegt um 0.2 LE in 1.0 Sekunden)
- Bild 5: Ball Position: 1.0 (weiterbewegt um 0.2 LE in 1.0 Sekunden)
Ein Rechner B braucht nur 0.5 Sekunden um ein Bild zu berechnen.
Ebenfalls in nur einer Sekunde hätten wir folgende Bilder:
- Bild 0: Ball Position: 0.0
- Bild 1: Ball Position: 0.1 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 2: Ball Position: 0.2 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 3: Ball Position: 0.3 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 4: Ball Position: 0.4 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 5: Ball Position: 0.5 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 6: Ball Position: 0.6 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 7: Ball Position: 0.7 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 8: Ball Position: 0.8 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 9: Ball Position: 0.9 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
- Bild 10: Ball Position: 1.0 (weiterbewegt um: 0.1 LE in 0.5 Sekunden)
Bei dem einen Rechner muss der Ball bei jedem Bild um 0.2 LE bewegt werden, beim andern Rechner nur 0.1 LE pro Bild. Tut man dies nicht, bewegt sich der Ball zu schnell oder zu langsam.
Doch woher weiß man nun wie weit man seinen Ball auf den jeweiligen Rechner pro Bild bewegen muss?
In unserem Beispiel bewegt sich der Ball mit der konstanten Geschwindigkeit von 0.2 LE pro Sekunde. Mit folgender Formel können wir bei konstanter Geschwindigkeit die neue Position berechnen:
neue_Position = Geschwindigkeit * vergangene_Zeit + alte_Position
Wir brauchen also nur eine Geschwindigkeit festlegen und messen, wie viel Zeit vergangen ist, um die neue Position zu berechnen zu können.
Wohlgemerkt gilt diese Formel nur wenn sich das Objekt mit konstanter Geschwindigkeit fortbewegt.
Berechnung der vergangen Zeit
Allgemein:
VergangeneZeit := ( Zeit1 - Zeit0 ) / Frequenz;
Millisekunden genau unter Windows
Die wohl einfachste aber auch ungenaueste Möglichkeit unter Windows so etwas zu realisieren besteht darin mit Hilfe von GetTickCount die Zeit (in Millisekunden) seit dem letzen Windows-Start zu ermitteln.
//Benötigte Variablen var BerechnungsZeit : LongWord; Position : Double; procedure BeimStart; // Was hier drinnen steht sollte zum Start ausgeführt werden begin BerechnungsZeit := GetTickCount(); end; procedure BerechnungsCheck; // Alle nötigen Berechnungen durchführen const Frequenz = 1000; // Durch Windows festgelegt var AktuelleZeit : LongWord; begin AktuelleZeit := GetTickCount(); Berechne((AktuelleZeit - BerechnungsZeit) / Frequenz); BerechnungsZeit := AktuelleZeit; end; procedure Berechne(vergangene_Zeit : Double); const Geschwindigkeit = 0.5; begin {Alle Berechnungen stehen hier } // z.B. Position := Geschwindigkeit * vergangene_Zeit + Position; end;
Absolute Genauigkeit per Hardware
Man kann auch die Hardware zur Zeitmessung nutzen um so noch genauere Ergebnisse zu erzielen. Es kann jedoch theoretisch sein, dass diese nicht zur Verfügung steht. Zudem handelt es sich hierbei um reine Windows API-Aufrufe. Bei Verwendung von FreePascal bzw. Lazarus sollte diese Methode also nicht in betracht gezogen werden.
//Benötigte Variablen var BerechnungsZeit : Int64; Frequenz : Int64; Position : Double; procedure BeimStart; // Was hier drinnen steht sollte zum Start ausgeführt werden begin if not QueryPerformanceFrequency(Frequenz) then // Frequenz ermitteln raise Exception.create('Kein Hardware Timer vorhanden'); QueryPerformanceCounter(BerechnungsZeit); // Aktuelle Zeit ermitteln end; procedure BerechnungsCheck; // Alle nötigen Berechnungen durchführen var AktuelleZeit : Int64; begin QueryPerformanceCounter(AktuelleZeit); Berechne((AktuelleZeit - BerechnungsZeit) / Frequenz); BerechnungsZeit := AktuelleZeit; end; procedure Berechne(vergangene_Zeit : Double); const Geschwindigkeit = 0.5; begin {Alle Berechnungen stehen hier } // z.B. Position := Geschwindigkeit * vergangene_Zeit + Position; end;
Man hat nun den Geschwindigkeitsfaktor, den man mit sämtlichen Bewegungen multipliziert. Wenn der PC viele Frames per Second rendert wird der Geschwindigkeitsfaktor klein. Die Bewegungen werden dadurch naturlich auch kleiner. Je niedriger die Ausführungsgeschwindigkeit der Zeichenprozedur, desto höher wird der Geschwindigkeitsfaktor, die Bewegungsschritte also auch größer.
Konstant Beschleunigte Bewegung
Nehmen wir an wir möchten darstellen wie ein Ball zu Boden fällt. Der Ball sei 1 LE über dem Boden und wird dort zum Zeitpunkt 0s losgelassen. Die Kraft welche die Erdanziehung simuliert soll pro Sekunde die Geschwindigkeit des Balles um 1 LE/s erhöhen.
Die neue Geschwindigkeit die sich durch die Beschleunigung ergeben hat, läßt sich noch sehr einfach Berechnen:
neue_Geschwindigkeit := Beschleunigung * vergangene_Zeit + alte_Geschwindigkeit;
Wie sieht es aber mit der Position aus?
Falsch wäre diese Berechnung:
neue_Position := alte_Geschwindigkeit * vergangene_Zeit + alte_Position
Denn so würde sich der Ball von Bild 0 nach Bild 1 gar nicht bewegen.
Genauso falsch wäre diese Berechnung:
neue_Position := neue_Geschwindigkeit * vergangene_Zeit + alte_Position
Denn dann würde sich der Ball sofort mit der vollen Geschwindigkeit bewegen und wäre viel zu schnell ab Boden.
Wer sich ein wenig mit Physik auskennt weiß das man bei konstant Beschleunigter Bewegung so die neue Position ausrechnet:
neue_Position := 0.5 * Beschleunigung * vergangene_Zeit * vergangene_Zeit + Geschwindigkeit * vergangene_Zeit + alte_Position
Beispielcode:
var Position : Double; Geschwindigkeit : Double; procedure Berechne(vergangene_Zeit : Double); const Beschleunigung = 1; begin Position := 0.5*Beschleunigung * vergangene_Zeit * vergangene_Zeit + Geschwindigkeit * vergangene_Zeit + Position // Wichtig: alte Geschwindigkeit erst ändern wenn sie nicht mehr gebraucht wird. Geschwindigkeit := Beschleunigung * vergangene_Zeit + Geschwindigkeit end;
Umsetzung mit einer Delphi Form
var [...] Frequency, StartCount, EndCount: Int64; Speedfactor: Extended; const Scalefactor = 100; //Der Scalefactor ist ein beliebiger Wert um den Speedfactor //zu beeinflussen [...] procedure TForm1.FormCreate(Sender: TObject); begin if not QueryPerformanceFrequency(Frequency) then raise Exception.create('Kein Hardware Timer vorhanden'); QueryPerformanceFrequency(Frequency); //Frequenz des Rechners ermitteln [...] end; procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean); begin QueryPerformanceCounter(StartCount); //Zeit0 Form1.Render; QueryPerformanceCounter(EndCount); //Zeit1 Speedfactor := ((EndCount - StartCount) /Frequency) * Scalefactor //(Zeit1 - Zeit0) / Frequenz [...] end;
Man hat nun den Speedfactor, den man mit sämtlichen Bewegungen multipliziert. Und schon hat man eine einfache Umsetzung von Timebased movement, ohne sich über Physik Gedanken machen zu müssen.
Links
- Tutorial_Bomberman2#Timebased_Movement
- SDL_GetTicks - Methode zur Zeitmessung mit SDL