Autor Beitrag
Jörg1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 12.04.06 19:45 
Hallo zusammen,

Ich bin momentan damit beschäftigt, einen Pacman-Clone zu schreiben auf Basis der Windows-GDI, also ohne DirectX usw.
Bis jetzt funktioniert eigentlich alles ganz gut und ich bin recht zufrieden. Das Spiel läuft in einer Spielschleife, in der ich mit dem Performance-Counter die entsprechenden Zeiten auslese und erst beim erreichen der Zeit für das nächste Frame alles wieder neu rendere. Ich möchte da jetzt nicht so sehr ins Detail gehen, der Aufbau dürfte den meisten hier wohl geläufig sein :

1.) Hole aktuelle Zeit
2.) wenn aktuelle Zeit größer nextframe dann berechne nextframe neu(aktuelle Zeit + Abstand) und rendere
3.) wenn 2 nicht zutrifft dann gehe wieder zu 1

Soweit funktioniert bei mir eigentlich alles recht gut. Ich gebe die gewünschte Framerate vor, in meinem Fall 120fps, der Abstand zum nächsten Frame wird berechnet (counterfrequenz div Framerate) und die Schleife(while) läuft anstandslos. Eine Routine zur Berechnung der tatsächlichen, real erreichten Framerate habe ich ebenfalls eingebaut und bis jetzt werden die 120fps auch tatsächlich erreicht. Natürlich ist in der Schleife auch eine Nachrichtenverarbeitung mit PeekMessage integriert.

Wie gesagt, was die reine Funktion des Programms angeht, kann ich im Moment eigentlich nicht meckern. Es gibt jedoch etwas, daß mich sehr stört bzw. was mir ein Dorn im Auge ist. Es ist etwas, was sehr viele hier im Forum beanstanden, nämlich die sehr hohe Auslastung des Prozessors von 100%. Dazu muss man sich erst einmal fragen bzw. überlegen, wie denn diese Auslastung überhaupt zustande kommt. Sind es extrem aufwändige Berechnungen oder etwa extreme Grafikoperationen? In den meisten Fällen, auch in meinem kann man diese Frage mit einem ganz klaren NEIN beantworten. Einige geben der Windows-GDI die Schuld, weil diese ja zum größten Teil über den Prozessor läuft und nicht über den Grafikadapter bzw. Grafikkarte. Dies ist, zumindestens nach meiner Auffassung, was die Auslastung angeht, nicht ganz falsch aber auch nicht ganz richtig. Meiner Meinung nach ist die Tatsache, daß das Programm, während es die Schleife abarbeitet, nicht mehr im Leerlauf ist und so diese hohe Auslastung produziert. Aus diesem Grunde hatte ich mal ein Testprogramm geschrieben mit einer Schleife, in der absolut nichts berechnet, gezeichnet oder sonst irgendetwas abgearbeitet wird. Trotzdem verlangte es vom Prozessor die volle Leistung. Und das ist etwas, womit ich mich eigentlich nicht abfinden möchte, auch wenn das Programm sonst bis jetzt wie eben schon erwähnt einwandfrei funktioniert.
Deswegen war ich mal neugierig und wollte einmal wissen, wie es denn unsere Vorbilder, die großen Entwicklungsstudios machen.
Ich öffnete den Taskmanager von WinXP, wählte den Reiter "Systemleistung" und lies ihn laufen. Anschließend startete ich verschiedene Spiele zum testen (natürlich immer jeweils nur eines !!). Ich blieb dann einige Zeit in den Einstellungsmenüs des jeweiligen Spieles, weil ich wissen wollte , wie stark diese den Prozessor belasten. Als ich das Spiel beendete und auf den Task-Manager schaute, der ja die ganze Zeit im Hintergrund lief, war ich gleichermaßen enttäuscht und auch erstaunt. Die Auslastung betrug 100%, obwohl das Einstellungsmenü weder animiert noch sonstwie aufwändig gestaltet war(Das Spiel war DRIVER, der erste Teil). Da hätte ich von den Profis ganz ehrlich gesagt mehr erwartet. Aber nun gut. Es ist so wie es ist.
Um nun noch einmal zu meinem Fall zurückzukommen, ich hatte mir eine Routine überlegt, um die Prozessorauslastung drastisch zu verringern. Für eine Erklärung muss ich etwas weiter ausholen. Am Anfang der Schleife hole ich ja die aktuelle Zeit vom Counter. Anschließend frage ich ab, ob die Zeit denn für das nächste Frame schon erreicht ist(if aktuelle_zeit > nextframe). Wenn ja, rendere die Szene neu, wenn nicht, hole Zeit neu. Was ich mir nun konkret überlegt habe ist, was man denn machen kann, wenn die Zeit für das nächste Frame eben noch NICHT erreicht ist. Daher fügte ich eine Routine in die Schleife ein, die das Programm für 1 Millisekunde pausieren lassen soll, jedoch nur dann, wenn die aktuelle Zeit noch mindestens 1 Millisekunde von der Zeit für das nächste Frame entfernt ist. Die Routine soll also mehr oder weniger dynamisch eine Pause einfügen wenn die verbleibende Restzeit bis zum nächsten Frame mindestens noch eine Millisekunde oder mehr beträgt. Dazu musste ich natürlich zuerst einmal herausfinden bzw. berechnen, was denn überhaupt einer Millisekunde entspricht. Ich nahm dafür die Counterfrequenz als Maß, die ja stellvertretend für eine Sekunde ist. Wenn man nun diese Frequenz durch 1000 teilt, erhalte ich die Anzahl der Counter-Ticks, die während einer Millisekunde hochgezählt werden. Hier nun der Routinen-Quellcode :

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
if aktuelle_zeit + (frequenz div 1000) < nextframe then begin
sleep(1); // mache 1 Millisekunde Pause
queryperformancecounter(count);
aktuelle_zeit := count; // aktualisiere Zeit
end;


Der Code ist meiner Meinung nach vollkommen richtig, dennoch funktioniert es leider doch nicht. Warum es nicht funktionieren kann wurde mir klar, als ich wieder mal ein kleines Testprogramm schrieb, indem ich einfach vor und nach der Ausführung der Anweisung "sleep(1)" die Zeit vom counter geholt habe und anschließend die differenz aus beiden Zeiten bildete(zeitende-zeitanfang). Diese Differenzwerte variierten so extrem stark, das es einfach nicht funktionieren kann. Die Streuung reichte von dreistelligen Werten(168) bis weit über 30000. Kleine Schwankungen hätte mann ausgleichen können, wenn sie annähernd dem errechneten Wert (frequenz div 1000) entsprochen hätten. Damit hatte ich auch gerechnet. Aber mit so einer Ungenauigkeit kann man nicht leben.

Fest steht : Sleep(1) entspricht definitiv leider nun mal nicht genau einer Millisekunde, sondern es ist nicht nur ungenau, sondern schon EXTREM ungenau, was sehr schade ist, denn sonst müsste diese Routine einwandfrei funktionieren und der Prozessor würde wirklich nur dann belastet, wenn die Szene neu gerendert werden muss, sonst nicht. Wahrscheinlich könnte man dann auch bei leistungsstärkeren Prozessoren die Auslastung noch mehr reduzieren, weil sich ja durch die höhere Geschwindigkeit noch größere Restzeiten bilden und dementsprechend noch mehr Pausen gemacht werden können. Aber nun gut, lange Rede, kurzer Sinn, mit Sleep geht es leider auf diese Art und Weise nicht.
Das Optimum wäre eine ähnliche Funktion, bei der ich aber die Counterticks angebe und nicht die Millisekunden. Ich arbeite noch mit Delphi5, ich weiß jetzt nicht, ob es etwas vergleichbares vielleicht bei den neueren Delphi-Versionen gibt. Aus diesem Grunde bitte ich das Forum um Hilfe, welche Alternativen es gibt bzw. ob es überhaupt welche gibt. Habe ich Sleep vielleicht falsch angewendet oder gibt es Möglichkeiten, die Genauigkeit dieser Funktion zu erhöhen? Über Hilfestellung wäre ich wie immer sehr dankbar.

Mit freundlichen Grüßen

Jörg
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 12.04.06 20:22 
Schau mal unter WaitForSingleObject. Die Routine wartet auf ein Ereignis, oder bis eine bestimmte Zeitspanne verstrichen ist. Laut OH eine 'sehr effiziente' Routine, soll wohl heißen, das dein CPU-100% Problem damit jedenfalls nicht auftritt.

_________________
Na denn, dann. Bis dann, denn.
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 17.04.06 02:36 
Hallo,

Erst einmal vielen Dank für deine Antwort. Ich habe mir das mal in der OH angeschaut(leider nur in Englisch). Es scheint nicht so einfach in der Handhabung zu sein und hat auch etwas mit dem Thema Threads zu tun, wovon ich nur sehr wenig Ahnung habe. Hättest du vielleicht ein kleines Code-Beispiel wie ich das am besten machen könnte? Das wäre echt super.

Mit freundlichen Grüßen

Jörg
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Mo 17.04.06 08:13 
Hab mir das jetzt nicht alles durchgelesen, aber so könntest du Sleep ersetzen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  Start: Single;
begin
  Start := GetTickCount;

  while GetTickCount - Start < 1000 do
    Application.ProcessMessages;
end;

So friert deine Anwendung nicht ein.

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 17.04.06 17:43 
Hallo GTA-Place,

Danke für deine Antwort. Ja, natürlch hast du recht, meine Anwendung friert mit deiner Routine nicht mehr ein. Dies wäre jedoch bei diesen verhältnismäßigen kurzen Pausen, die mein Programm eingelegt hätte sofern das mit meiner Sleep-Routine gegangen wäre nicht so schlimm bzw. gravierend gewesen. Obwohl ich deine Routine zu diesem Zeitpunkt noch nicht ausprobiert habe denke ich, das davon die CPU-Auslastung nicht sinken wird, da es sich hierbei im Prinzip ja auch um eine Schleife handelt die durchlaufen wird und das Programm keine "Luft holen" kann. Aber vielleicht irre ich mich auch. Ich werde das auf jeden Fall gleich nachdem ich dies hier geschrieben habe mal testen. GetTickCount werde ich auf jeden Fall ersetzen durch die Abfrage der Counter-Zeiten, weil diese viel genauer sind. Das müsste eigentlich möglich sein.
Jedenfalls nochmals Dankeschön für deine Mühe. Ich werde hier direkt posten wenn ich es getestet habe.

MfG

Jörg
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 17.04.06 19:22 
Hallo Fabian,

Ich habe deine Routine nun getestet. Leider habe ich Recht behalten, die CPU-Auslastung sinkt nicht, leider :( Außerdem scheint das Programm oder der Compiler ein Problem mit Application.Processmessages zu haben wenn es zu schnell hintereinander ausgeführt wird, zumindest in meinem Programm. Beim Schließen des Programmes bekomme ich nämlich eine Fehlermeldung. Wie auch immer, so langsam gehen mir die Ideen aus was man noch machen kann. Ich hatte schonmal daran gedacht eine eigene Sleep-Prozedur zu schreiben, jedoch nicht mit der Auflösung von einer Millisekunde, sondern von einem Counter-Tick, da ich sehr hohe Genauigkeit benötige. Nur dazu müsste ich erstmal wissen, mit welchen API-Befehlen man ein Programm einfrieren kann. Ich habe auch schonmal was von "Delay" hier im Forum gelesen. In der Hilfe von D5 steht diesbezüglich aber gar nichts :( Hast du vielleicht noch eine andere Idee?

MfG

Jörg
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 19.04.06 11:28 
Dein Problem ist, das Du deine Anwendung sequentiell schreibst, die Umgebung (Windows) aber Ereignisgesteuert arbeitet. Daher brauchst Du ein sehr genaues 'Sleep', das genau so viel Millisekunden arbeitet, bis der nächste Frame 'dran ist'.

Probiere Folgendes:
1. Beschäftige Dich mit Threads. Die TThread-Klasse ist sehr einfach zu verstehen.
2. Schreibe einen Thread, der 'auf Anforderung' einen neuen Frame erzeugt.
3. Miss die Zeit, die der Thread dafür benötigt (TF)
4. Implementiere einen Timer, der 1x pro Frame (40fps oder so) ausgelöst wird (TS).
5. Der Timer zeichnet den aktuellen Frame und stößt anschließend den Thread an, den nächsten zu zeichnen.

Du musst nur sicherstellen, das TF<TS ist, also die Erzeugung eines Frames schneller ist, als die Framerate.

_________________
Na denn, dann. Bis dann, denn.
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 19.04.06 15:09 
Titel: Threads
Hallo Alzaimar,

Danke erstmal für dein Schreiben. Leider kann ich nicht von mir behaupten das mir der Umgang mit Threads leicht fällt. Die OH hat zwar sehr viele Informationen über dieses Thema, die für mich aber sehr kompliziert erscheinen. Man sieht praktisch den Wald vor lauter Bäumen nicht mehr. Welchen Timer meinst du denn, den ich implementieren soll? Den normalen Windows-Timer? Wenn ja, reicht dessen Auflösung denn überhaupt aus? Mein Spiel ist eigentlich vorgesehen für 120fps, ich weiß nicht, ob das dann mit einem normalen Timer noch funktionieren kann, wenn du diesen denn überhaupt meinst. Wäre es vielleicht möglich, daß du mir evt. kleine Codebeispiele geben könntest, das wäre echt super. Denn ich weiß nicht, ob ich das mit der OH alleine hinbekomme.

MfG

Jörg
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 19.04.06 15:33 
Das mit dem Windows-Timer ist so eine Sache... Stimmt, sooo genau ist der auch nicht. Muss er aber auch nicht sein... Eventuell kann man da einen besseren Timer (wieder als Thread ;-)) basteln...

Zum Thema Threads solltest du dir die SortDemo von Delphi anschauen, dort steht alles drin.

Eigentlich ist es sehr einfach:
1.Eigene Klasse von TThread ableiten
2.Den Konstruktor überschreiben, damit lokale Variablen initialisiert werden können:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Constructor TMyThread.Create;
Begin
  Inherited Create (True); // Erstmal nicht laufen
  .... // Hier die eigenen Variablen etc. initialisieren
  Resume; // Aufwecken
End;

3. Die Execute-Methode überschreiben
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Procedure TMyThread.Execute;
Begin
  While not terminated do begin
    CreateFrame;
    Bla...
  End;
End;

4. Entweder im Destruktor lokalen Speicher freigeben, oder ein OnTerminate-Ereignis zuweisen.

Wenn Du so einen kleinen Thread mal geschrieben hast, dann kannst du dich mit der 'Synchronisation' beschäftigen, also wie man sich mit einem Thread unterhält. Eine Unsitte ist es, den Thread einfach per 'Suspend' schlafen zu legen, um ihn mit 'Resume' wieder aufzuwecken. Unsitte deshalb, weil man ja gar nicht weiss, was genau der Thread gerade gemacht hat. Wenn man dir sagt, 'Zeit fürs Bett' fällst du ja auch nicht auf der Stelle um, oder?

Dafür kann man z.B. eine Semaphore, ein Mutex, oder ein Event nehmen. Das sind Windows-Objekte, die bestimmte Zustände annehmen können. "Ja und?", denkst du jetzt, aber das Gute an diesen Dingern ist, das garantiert nur EINER diesen Zustand wechseln kann.

Hier würdest Du die Execute-Methode sinngemäß so erweitern:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Procedure TMyThread.Execute;
Begin
  While Not Terminated Do 
    If WaitForSingleObject (fMySyncObject,MYTIMEOUT_VALUE) = WAIT_OBJECT_0 Then
      CreateFrame;
End;

Da hätten wir dann das WaitForSingleObject, das so lange wartet, bis das 'fMySyncObject' angefasst wurde, oder bis die Zeit MYTIMEOUT_VALUE verstrichen ist. Das Hauptprogramm, oder ein anderer Thread zuppelt nun am Synchronisationsobject, wenn es möchte, das der Thread einen Job erledigt ('CreateFrame'). Deshalb heißen diese Threads auch 'WorkerThreads'.

Grundsätzlich würde ich mich ERST mit Threads auseinandersetzen, BEVOR ich mit Realtime-Programmen anfange. Ich hätte damit auch Probleme, ehrlich gesagt...

_________________
Na denn, dann. Bis dann, denn.
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 19.04.06 20:04 
Titel: Thread
Hallo Alzaimar,

vielen Dank für die Beispiele. ich werde mir auf jeden Fall mal dieses Demoprojekt ansehen. Was meinen Fall betrifft habe ich noch eine Frage. Kann ich mir das denn ungefähr so vorstellen, das ich einen Hauptthread habe, indem meine Renderbefehle sind und eine Art "Nebenthread", der permanent im Hintergrund läuft und keine andere Aufgabe hat die Zeit abzufragen und dann bei erreichen einer bestimmten Zeit den Haupthread aufzurufen. Wenn das wirklich so ist, dann würde es doch bedeuten, daß der Nebenthread doch mit einer sehr hohen Priorität laufen muss, weil er ja auf keinen Fall unterbrochen werden darf um wirklich die Zeit sehr genau messen zu können über den Performance-Counter. Würde es dann nicht auf das gleiche herauskommen bezüglich der CPU-Auslastung wenn dieser Nebenthread permanent mit höchster Priorität läuft, also ohne Unterbrechung? oder bin ich absolut auf dem Holzweg hier und hab vollkommen die falsche Vorstellung von der Sache?

MfG

Jörg
Balmung der blaue Gott
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 52

WinXP

BeitragVerfasst: Mi 19.04.06 20:42 
Titel: Re: Thread
user profile iconJörg1 hat folgendes geschrieben:
Kann ich mir das denn ungefähr so vorstellen, das ich einen Hauptthread habe, indem meine Renderbefehle sind und eine Art "Nebenthread", der permanent im Hintergrund läuft und keine andere Aufgabe hat die Zeit abzufragen und dann bei erreichen einer bestimmten Zeit den Haupthread aufzurufen.


Ich glaube es war so gemeint (ohne ThreadTimer): Du hast einen Hauptthread und einen Nebenthread. Der Hauptthread läuft (das haben Hauptthreads so an sich) bis der Timer den Nebenthread (der tatsächlich wartet und nix tut) zum Zeichnen des Frames auffordert. Falls dieser Fall eintritt, zeichnet der Nebenthread und der Hauptthread arbeitet weiter. Wenn der Nebenthread nun fertig ist, legt er sich wieder schlafen, bis er von HauptthreadTimer wieder aufgerufen wird.
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 19.04.06 21:09 
Titel: Thread
Hallo blauer Gott,

danke für dein Schreiben. ich weiß nicht ob du dich mit Zeitmessung auf dem PC ein bischen auskennst. Wenn das wirklich so ist mit dem Haupt- und Nebenthread, muss der Thread, in dem die Zeit abgefragt wird, auf jeden Fall permanent laufen, ohne Pause. Aber habe ich dann nicht im Endeffekt wieder die gleiche hohe Systemauslastung als wenn ich eine Endlosschleife laufen lasse? Wenn ich das nämlich vorher schon weiß, dann kann ich mir den Einsatz von Threads nämlich sparen :)

MfG

Jörg
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 19.04.06 21:45 
Nee, Windows sollte das für Dich übernehmen. Vermutlich gibt es Timer-Komponenten o.ä., die dieses Problem für Dich übernehmen, schließlich bist Du nicht der Erste, der dieses Problem hat. Ich meine, bei einer Graphikroutinensammlung für Delphi, DelphiX (oder wie des heißt) sowas mal gesehen zu haben. Such doch mal danach.

_________________
Na denn, dann. Bis dann, denn.
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 19.04.06 23:55 
Titel: DelphiX
Hallo Alzaimar,

Ja, selbstverständlich gibt es spezielle Bibliotheken und Header, die speziell für die Spieleprogrammierung gemacht sind. So zb DelphiX , wo es einen sogenannten DXTimer gibt, der wesentlich genauer und schneller arbeitet als der hauseigene Windows-Timer. Damit wäre in der Tat höchstwahrscheinlich mein Problem gelöst. :) Ich hatte mir jedoch vorgenommen, zunächst mal mit Bordmitteln etwas zu entwickeln, also ohne zusätzliche Komponenten, bevor ich einen Schritt weitergehe und mich mit speziellen Grafikbibliotheken beschäftigen möchte. Und ich finde, so schlecht, wie das Windows GDI teilweise gemacht wird, ist es eigentlich nicht. Natürlich reicht es für 3D und extrem schnelle Spiele nicht aus, aber das macht nichts. Für den Anfang reicht es. Und was die 100%-CPU Auslastung angeht, so scheinen es selbst die Profis teilweise nicht nötig zu haben, sich daran zu stören(Siehe mein erstes Schreiben am Anfang). Und wenn die das nicht brauchen, dann brauche ich das als Anfänger erst recht nicht, oder :)? Naja, ich werde mal schauen was so in den nächsten Tagen, Wochen und Monaten hier geposted wird, vielleicht ergibt sich ja mal was :) Jedenfalls trotzdem dankeschön für die Unterstützung. Bis später dann.

MfG

Jörg
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Do 20.04.06 00:32 
Es liegt daran, dass Sleep überhaupt nicht schneller sein kann. Du wirst die 1ms nicht einhalten können, das wird bei 16-20 msek liegen.

Du musst halt eine andere Idee dahinter verfolgen.
Und zwar gibst du die für dich maximale Zeit ab (meinetwegen 1 msek)

danach schaust die wielange es wirklich gedauert hat und im verhältnis dazu bewegst du deine Figuren.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
told := GetTickCount;
while true do
begin
  Sleep(1);
  tduration := GetTickCount-told;
  told := GetTickCount;

  pacman.left := pacman.left + round(tduration * speed)
end;


dadurch wird auch die Zeit zum anzeigen des bildes und der bereichnungen mit einberechnet

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Do 20.04.06 11:38 
Hallo Daniel,

Ebenfalls vielen Dank für dein Schreiben. Ja, natürlich hast du recht, sleep(1) entspricht leider keiner Millisekunde sondern dauert länger, ich weiß :( Was deinen Quellcode angeht so muss ich gestehen, daß er doch ganz schön ausgeklügelt ist und raffiniert, da wäre ich so nicht drauf gekommen. Dennoch glaube ich nicht, daß er bezogen auf mein Spiel ein zufriedenstellendes Ergebnis liefern kann. Dies hat folgenden Grund : Bei einem Spiel das mit 120fps laufen soll, darf die maximale Zeit für einen Frame höchstens 8,3-periode Millisekunden betragen, weil 1000 / 120 = 8,3- periode. Nun liegt es natürlich am Rechner und auch am Spiel selbst ob diese 8,3 Millisekunden vollständig gebraucht werden oder ob sich Restzeiten bilden. Sobald ich jetzt nun ein Sleep(1) in die Schleife einbauen würde, hätte ich plötzlich Pausen von beispielsweise 16 Millisekunden. Das heißt auf die Sekunde bezogen könnte die gewünschte Framerate nicht mehr erreicht werden. Wenn ich nun deinen Code richtig interpretiert habe gleichst du das durch variable Schrittweiten der zu bewegenden Objekte aus. Und ohne deinen Code irgendwie mal ausprobiert zu haben könnte ich mir sehr gut vorstellen, das sich dadurch unregelmäßige Bewegungen ergeben, was optisch bzw. grafisch nicht so toll rüber käme dann. Vor allem würden sich dadurch mehrere Folgeprobleme ergeben weil die Bewegungen bzw. exakten Positionen nicht mehr direkt vorhersehbar sind. Was die CPU-Auslastung angeht wäre deine Idee natürlich toll, ich hätte damit bestimmt nur noch 15% statt 100% Auslastung. Aber wie auch immer, trotzdem dankeschön für deine Mühe :)

MfG

Jörg
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Do 20.04.06 16:02 
120 Frames pro Sekunde? Wenn ich mit DelphiX (und dem DelphiX-Timer) was mache, sind es so 60 Frames pro Sekunde (nur Bild ohne Bewegung, etc.). Die Frage ist, ob du 120 Frames brauchst.

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Do 20.04.06 18:25 
@Jörgi:

Das ist richtig, dass du dann die 120 Frames pro Sekunde nicht mehr hinbekommst. Rate aber mal warum die meisten Spiele 100% CPU Auslastung haben? Weil die eben eine Endloschleife haben in der nur in einem bestimmten Zeitinterval etwas ausgeführt wird.
Zusätzlich haben auch diese Spiele so eine Berechnung für die Geschwindigkeit drin. D.h. Sollte das Zeitinterval extrem viel größer sein, so muss die Spielfigur sich auch mehr bewegt haben. Sonst würde man unter umständen bei Pcs die eben locker 200 Frames schaffen auch viel schneller laufen.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Sa 22.04.06 01:35 
Hallo Fabian,

Wie du schon richtig bemerkt hast, ist eine Framerate von 120fps nicht gerade im unteren Bereich angesiedelt und ich möchte dir gerne sagen warum ich diese Framerate gewählt habe. Schon einmal vorweg gesagt, es hat einfach optische Gründe :) Da mein Spiel ja auf der Windows-GDI basiert, kann ich natürlich nicht die Flip-Funktion, so wie es sie in DirectX, in DelphiX aber glaube ich auch gibt, nutzen. Was macht die Flip-Funktion genau? Sie tauscht blitzschnell über spezielle Zeiger den Backbuffer(offscreen) mit der primären Oberfläche aus. Dieser Vorgang wird noch zusätzlich mit einem Signal, was die Grafikkarte bei einem Strahlrücklauf des Monitors abgibt, syncronisiert. So ist gewährleistet, das sämtliche Animationen und Bewegungen auf dem Bildschirm absolut flackerfrei und ohne Verzerrungen realisiert werden können. Nätürlich habe ich auch bei der GDI die Möglichkeit alles in einem Offscreen zu zeichnen und dann alles komplett auf die Form zu bringen, was ich auch in meinem Spiel anwende. Aber vor allem wenn man größere Objekte oder eine große Anzahl von Sprites über den Schirm bewegen muss wird man sehr schnell merken, das die Anzeigequalität doch nicht auf dem Niveau eines Spieles ist was auf spezielle Grafikschnittstellen wie DirectX usw. aufgebaut ist. Selbst bei einigermaßen effizienter Programmierung ist ein leichtes Flackern oder gar Verzerrungen nicht ganz zu vermeiden. Und dies liegt meiner Meinung nach weniger an einem vielleicht etwas betagten PC, sondern an dieser fehlenden Flip-Funktion. Und ich habe für mich persönlich die Erfahrung gemacht, das es bei GDI-Spielen besser ist bzw. optisch besser aussieht, wenn man die zu bewegenden Objekte pro Frame immer nur um 1 Pixel bewegt, weil dann die Verzerrungen, die durch den Strahlrücklauf des Monitors produziert werden, nicht ganz so schlimm sind. Somit bin ich mehr oder weniger gezwungen mit einer hohen Framerate zu arbeiten wenn ich möchte, das meine Sprites sich auch mit der gewünschten Geschwindigkeit über den Bildschirm bewegen. Ich würde auch ganz ehrlich gesagt lieber mit 60fps arbeiten und die Sprites mit 2 Pixeln pro Frame bewegen. Nur die dadurch entstehenden stärkeren Verzerrungen sehen einfach nicht mehr schön aus. Dies ist der Grund, warum ich so eine hohe Framerate gewählt habe. Bei einem DirectX-Spiel gibt es diese Problematik einfach nicht. Man fährt mit 60fps und kann sehr hohe Schrittweiten verwenden von 3,4,5 oder mehr Pixeln. Trotzdem ist die Anzeige immer perfekt und sauber. Bei der GDI muss man da Abstriche machen, leider :)

Zu dir Daniel :), ja natürlich, die 100%-Auslastung wird durch eine Endlosschleife produziert, da hast du vollkommen recht und dessen bin ich mir auch voll bewusst. Es gibt jedoch eine Ausnahme. Wenn in der Endlosschleife hauptsächlich Befehle vorhanden sind, die die Grafikkarte betreffen. In dieser Zeit kann dann der Prozessor pausieren und die CPU-Last sinkt. Dies ist bei mir natürlich nicht der Fall, weil mein Spiel ja ohne Hardwarebeschleunigung auskommen muss. Um nochmal zu deiner Routine zurückzukommen, die vom Ansatz her bzw. von der Überlegung nicht schlecht ist, das wollte ich dir nochmal sagen :) Trotzdem bin ich der Meinung, das bedingt durch die wirklich extrem ungenaue Sleep-Funktion sich die Routine nur bedingt in Spielen einsetzen lässt. Durch die extrem hohen zeitlichen Schwankungen von Sleep ergeben sich unvorhersehbare Zustände, die sich garantiert auch optisch bemerkbar machen, gerade bei GDI-Spielen. Mal bewegt sich der Sprite um 1 Pixel, dann plötzlich um 3, dann vielleicht um 2. Das wird man auf jeden Fall sehen. Und auch sonst wird es weitere Probleme dadurch geben, z.B. bei diversen Kollisionsabfragen oder ähnlichem.

MfG

Jörg
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mo 24.04.06 16:40 
Nimm das Sleep raus, dann haste eine Schleife wie sie wohl in den Spielen benutzt wird. Du wirst ausserdem nichts genaueres als die 16msek hinbekommen.

Zumal die Engine eben nicht darauf ausgelegt werden darf, dass sie wirklich 120fps schafft. Ansonsten ist es eine schlechte Engine, und dafür gibt es ja eben den Programmierer, dass es das Problem löst.
Nicht um sonst gibt es genug Algorithmen, und z.b. einfache Laufrichtung mit Zeitinterval und einer gerade kann gut für Kollisionen bei einem 2D Spiel benutzt werden. Da musst du dich halt drum kümmern.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit