Autor |
Beitrag |
rushifell
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Mo 30.10.06 17:01
Hallo,
Ich habe mit Hilfe von SDL ein Spiel in Delphi programmiert. Die Engine ist fast fertig, die Level zum Großteil spielbar. Da dies mein erstes Spiel ist, habe ich mich zuvor noch nie mit dem Geschwindigkeitsproblem bei Spielen beschäftigt. Ich möchte gerne, dass das Spiel auf jedem Rechner mit etwa gleicher Geschwindigkeit läuft. Das Problem ist hierbei nicht, dass das Spiel zu langsam, nein im Gegenteil, dass das Spiel auf manchen PCs einfach zu schnell ist. Das SDL_Delay habe ich komplett entfernt, da es nur zu einem ruckelnden Spielfluss führte. Ohne SDL_Delay läuft das Spiel sauber und ruckelfrei mit konstanter Geschwindigkeit, aber eben je nach PC unterschiedlich schnell.
Ich habe vom Timebased-Movement gelesen, das sehr interessant klingt und das ich auch ausprobieren werde. Hierbei wird die Anzahl der Pixel, um die das Bild verschoben wird, von der Geschwindigkeit abhängig gemacht. Meine Frage, gibt es weitere Methoden, um die Spielgeschwindigkeit zu kontrollieren? Wie handhabt ihr das bei Spieleprogrammierung?
cu
rushifell
|
|
Phobeus
      
Beiträge: 1280
Linux (FC6), WinXP Pro (Box)
D6 Pers, D7 Pro, FPC 2.x
|
Verfasst: Mi 01.11.06 08:14
TimeBasedMovement ist eigentliche das Maß aller Dinge um sinnvoll und effizient frameunabhängige Bewegungen zu ermöglichen. Das Prinzip hat sich bereits seid Jahrzehnten bewert und tut es ebenfalls immer noch. Andere sinnvolle Methoden jenseits des Voodoos sind mir nicht bekannt.
Aber: Die Berechnung sollte nicht in Form von Pixeln erfolgen, sondern mit Hilfe eines Floats. "Pixel" sind exakt so genau, wie das menschliche Auge die Information verarbeitet, was einen bei der Geschwindigkeit doch arg einschränkt. Mindestengeschwindigkeit von einem Pixel könnte schon für viele Sachen "recht schnell" sein. Verwende daher bei SPielen stets für die Berechnung Floats, um diese dann erst beim Rendern auf eine pixelbasis zu bekommen. Auf diese Weise kannst Du wesentlich feinere Bewegungen berechnen als eine rein auf pixel basierende Lösung.
Und: Gute Wahl mit SDL 
_________________ "Menschen sterben nicht wenn man sie zu Grabe trägt, sondern wenn sie ihre Träume verlieren..."
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Mi 01.11.06 21:46
Genau das wollte ich wissen. Danke für Deine Antwort! Dann werd ich mal versuchen, ein Timebased Movement zu implementieren. Sollte wohl nicht allzuschwer sein.
Bin mit SDL auch mehr als zufrieden
Gruß
rushifell
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Fr 05.02.10 06:58
Hallo,
ich grabe mal diesen alten Thread aus, da es sich um die gleiche Problematik handelt. Nachdem ich die Pixel-Variante ausprobiert hatte, habe ich mich letztendlich doch für die Timer-Variante entschieden. Mein Spiel hat sowieso eine geringe Bildschirm-Auflösung und mit einem Timer ist es einfach wesentlich leichter umzusetzten.
Nun habe ich mich in den letzten Tagen daran gemacht, mein Spiel zu tunen, also so wenig ruckeln wie nur möglich. Bei maximaler Geschwindigkeit läuft das Spiel schön flüssig, aber eben auf manchen PC's leider zu schnell. Deshalb muss ich das Spiel auf schnelleren PC's drosseln, was aber leider zu diesem nervigen Ruckeln führt.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| procedure Init_Query; begin QueryLogTime:=0; if not QueryPerformanceFrequency(Frequenz) then raise Exception.create('Kein Hardware Timer vorhanden'); QueryPerformanceCounter(QueryLogTime); end; |
Alte Variante:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure Wait_Query(ms:Integer); var AktuelleZeit : Int64; PositionX:Double; begin Repeat QueryPerformanceCounter(AktuelleZeit); Positionx:=((AktuelleZeit-QueryLogTime)/Frequenz)*1000; Until PositionX>=Ms;
QueryLogTime := AktuelleZeit; end; |
Neue Variante:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure Wait_Query(ms:Integer); var AktuelleZeit : Int64; begin Repeat QueryPerformanceCounter(AktuelleZeit); Until (AktuelleZeit-QueryLogTime)*1000 >= Ms*Frequenz;
QueryLogTime:=Round(Ms*Frequenz/1000)+QueryLogTime; end; |
Hier die Schleife im Spiel:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Init_Query Repeat Wait_Query(17); UpdateScreen; Until Quit; |
Der wesentliche Unterschied zwischen der alten und neuen Variante besteht in folgendem:
In der alten Variante hatte ich Wait_Query(17) noch nach UpdateScreen (also nach dem Zeichnen) stehen. In Wait_Query hatte ich immer mit der aktuellen Zeit weiter gerechnet, in der neuen Variante immer ausgehend von der Zeit, die in Init_Query berechnet wurde. Ich erhoffe mit dadurch ein "konstanteres" Zeichnen der Bildschirmausgabe.
Was denkt ihr darüber? Gibt es noch bessere Möglichkeiten? Würde mich freuen, wenn ihr mir noch Tipps geben könntet  Danke schonmal.
rushifell
Moderiert von Narses: Code- durch Delphi-Tags ersetzt
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Sa 06.02.10 15:41
Ja. Time Based Movement verwenden
Ferner: Die Zeitbasis wird nicht von der Warteroutine festgelegt, sondern von der Hauptschleife. Damit wird das um einiges Regelmäßiger.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Sa 06.02.10 15:59
Hi, danke für Deine Antwort:
Wird das mit TimeBased Movement wirklich flüssiger? Ich habe nur eine Auflösung von 320*240 Pixel.
Zitat: |
Die Zeitbasis wird nicht von der Warteroutine festgelegt, sondern von der Hauptschleife.
|
Ich verstehe nicht ganz, wie Du das genau meinst? Könntest Du das bitte noch genauer erläutern? Danke.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Sa 06.02.10 16:06
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Sa 06.02.10 16:58
Auf meinem Rechner läuft das Spiel mit maximaler Geschwindigkeit und damit auch schön flüssig. Auf meinem PC ist die Zeitverzögerung also nicht notwendig. Erst wenn ich 18 oder 19 Millisekunden eingebe, wird das Spiel auch auf meinem Rechner langsamer.
Auf einem anderen PC ist das Spiel wesentlich schneller. Ich habe das Delay von 17 Millisekunden eingefügt, damit das Spiel annährend genauso schnell läuft, wie auf meinem Rechner. Bis diese 17 Millisekunden abgelaufen sind, brauche ich keine neue Berechnung mehr.
Wenn ich das TimebasedMovement richtig verstanden habe, sollte ich das dann besser mit einem Dreisatz lösen.
Quelltext 1: 2:
| x = y Millisekunden //x Pixel Bewegung pro y Millisekunden 2 = 17 Millisekunden //2 Pixel Bewegung pro 17 Millisekunden |
Ich gehe einfach mal von der Hypothese aus, dass der schnellere Rechner, für einen Schleifendurchlauf y Ms gebraucht hat.
Quelltext 1: 2:
| x/2 = y/17 x = Round(y*2/17); |
Wenn also z.B. y=17ms verstrichen wären, würde sich das Objekt um zwei Pixel weiterbewegen. Wenn es y=9 ms wären,
9*2/17=18/17=1.05 => 1 Pixel? So ähnlich zumindest.
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Sa 06.02.10 17:14
Ja, so in etwa. Du hast eine Geschwindigkeit v und ermittelst bei jedem Schleifendurchlauf den Zeitabstand zum letzten Durchlauf, Δt. Dann kannst du Δx = v * Δt zur aktuellen Position des Objektes addieren.
Wie gesagt solltest du aber ausschließlich mit Floats rechnen; gerundet wird nur beim Anzeigen.
_________________ >λ=
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Sa 06.02.10 17:28
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: So 07.02.10 11:43
Wow
Hammergeil
Vielen Dank nochmal an Euch alle. Den Wiki-Link habe ich eigentlich schon gekannt, war bei mir aber in Vergessenheit geraten. Es ist wirklich, kein Vergleich zu vorher. Ich hätte nicht gedacht, dass die Bewegung so flüssig sein können. Ich habe extra die alte und neue Variante nochmal im Window- und Fullscreen-Mode verglichen.
Ich habe konsequent mit Variablen vom Typ Double gerechnet. Das Ergebnis gerundet und auf dem Bildschirm ausgegeben. Wenn ich mir das im Nachhinein überlege, wundert es mich nicht. Schließlich hab ich immer mit gerundeteten Werten weitergerechnet. Aber dass das Ergebnis so gut ist, hätte ich wirklich nicht erwartet.
Jetzt muss ich das ganze nur noch in mein Spiel einbauen, und wenn das Ergebnis genauso ist, dann lohnt sich's auch.
Würdet ihr immer Zeichnen, auch wenn sich nichts bewegt, also die Pixeldifferenz gerundet 0 ergibt.
Danke 
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 07.02.10 13:48
Jep. Würde ich machen, weil es die Logik im Programm vereinfacht und das Zeichnen i.d.R. wenig ausmacht. Zudem wollen insbesondere OpenGL und SDL ein halbwegs regelmäßiges Refresh haben.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: So 07.02.10 17:13
Danke 
|
|
|