Ruckeln von Ping-Pong Game trotz hoher FPS

Vielen Danke für die technische Erläuterung. Ich habe es jetzt mal so implementiert, nun kann ich die Frames bis unters Dach drehen und es ändert sich nichts am Spielgeschehen. :D
Im Falle des Ruckelns auf meinem Laptop lässt sich dieses nur durch eine erhöhte Framerate verringern, ich denke ich werde das ganze mal auf Basis von LWJGL implementieren, vielleicht ist Swing in der Konstellation wirklich ungeeignet, natürlich mit der "Deltageschichte".
 
Für die Deltageschichte nicht, aber das ist leider nicht alles :) Nächstes Problem ist zB., dass sleep unzuverlässig ist.
sleep(16) kan genausogut 20 oder 25ms warten, und das kann man (mit sinnvollem Aufwand) nicht ändern.
Wenn man sich darauf verlassen will, dass man rechtzeitig zum nächsten Bildschirmzeichnen die Schleife
ein weiteres Mal ausgeführt wurde, statt dass alles noch am Stand vom vorigen Frame ist,
muss man kürzere Intervalle nehmen.
 
Wie meinst du das mit den kürzeren Intervallen. Im Bezug auf fps also statt 60fps vielleicht 200fps, denn dann werden ja die Intervalle kürzer, oder meinst du etwas anderes ?
 
Hallo hunter19441,
Wie meinst du das mit den kürzeren Intervallen. Im Bezug auf fps also statt 60fps vielleicht 200fps, denn dann werden ja die Intervalle kürzer, oder meinst du etwas anderes ?
WAAAAIIIIITTT!! 200 Frames pro Sekunde ist viel zu viel und wäre ein Designfehler. Ich habe oben eigentlich nur versucht dir klar zu machen, weshalb du mit Frameeinbrüchen zu Rechnen hast, das heißt nicht, dass man mit meinem Code die perfekte Hauptschleife für eine Game Engine hat! (es würde mich wahrscheinlich Wochen kosten alle Ansätze und Methoden einer solchen hier zu posten :D)
Stell' dir doch mal diese Frage: Wenn dein Spiel auf einem Computer nicht flüssig läuft, ist es dann eine sinnvolle Lösung die Rechenlast (= Framerate) zu erhöhen? ;)

Der Grund warum das Pong auf deinem Laptop ruckelt liegt wie sheel schon geschrieben hat daran,
dass sleep unzuverlässig ist.

Der Bereich Engine-Design fällt in die Rubrik Real-Time-Applications. Dort ist die Methode mit dem Sleep-Befehl wie du sie jetzt implementiert hast durchaus bekannt, wenn auch nicht sehr beliebt. Ich vermute mal ohne weiteres "Tuning" kann es dir passieren (oder passiert ja sogar auf'm Laptop), dass dein Spiel einfach mal 0,5 Sekunden auf die Reaktivierung durch das Betriebssystem wartet (obwohl es "nur" ein Sleep(16); ausführt). Ein bekanntes Hilfsmittel um die Genauigkeit von Sleep zu erhöhen ist es, die Priorität des Prozesses (und aller Threads!) im Betriebssystem mindestens auf "Real Time" - vielleicht sogar auf das Systemmaximum - zu setzen. Ich garantiere dir, dadurch verbessert sich die "Stabilität" deines Spiels enorm.

Allerdings wird im Gaming-Bereich lieber ein anderes Design gewählt: Eine timergestützte Hauptschleife.
Da du ja Swing ohnehin schon benutzt, kannst du hier direkt auf einen Swing Timer zurückgreifen.
Im Prinzip haben sich aber schon andere Menschen die Arbeit gemacht, deshalb gibt es sogar ganze Module die für Animations- und Gaminggeschichten ausgelegt sind (und auf Swing aufbauen). Z.B. SwingWorker.
Das Gute an solchen timerbasierten Designs ist, dass du dir nach dem Einrichten des Timers keinen Kopf mehr bezüglich des Timings machen musst, weil er alles für dich übernimmt (da er für dich im Idealfall alle FPStel Sekunde deine Schleife aufruft, und das mit einer "angenehmen" Präzision).
Übrigens kannst du auch hier über die Prozesspriorität ein ziemlich genaues Timing erreichen.

Trotzdem solltest du natürlich bei dieser dt-Geschichte bleiben :p.

*Klugscheißmodus an*
Übrigens: Du solltest deine Funktionen die von dt abhängen immer so aufbauen, dass mit ganzen Sekunden gerechnet wird. Also wenn du möchtest, dass der Ball 4 mal pro Sekunde das Spielfeld passiert, dann sollte deine Funktion
C++:
void Ball::move(float dt) { // erwarte dt in der Einheit "Sekunde"
    this->position += this->dv * dt; // dv hat die Einheit "Pixel pro Sekunde"
}
// in diesem Beispiel sind Ball.position und Ball.dv Vektoren
lauten und nicht
C++:
// Schlechtes Beispiel!

void Ball::move(float dt) { // erwarte dt in der Einheit "Nanosekunde"
    this->position += this->dv * dt; // dv hat die Einheit "Pixel pro Nanosekunde"
}
// "Pixel pro Nanosekunde" ist äquivalent zu "Gigapixel pro Sekunde"
// keine Einheit die man benutzen würde, gell :P
. Der Grund ist, dass System.nanoTime(); zwar eine Auflösung von einer Nanosekunde hat, aber intern wird dabei "bloß" der QueryPerformanceCounter benutzt (sheel als alter C++ler kennt den bestimmt), wodurch die Präzision auf etwa 640 Nanosekunden begrenzt wird. Dein dt ist also immer ein ganzzahliges Vielfaches davon (= diskret) und es macht hin und wieder Sprünge. Deshalb gibt man in der Praxis alles so an, dass dt die Einheit "Sekunde" trägt.

Und bezüglich der Framerate: Alles zwischen 24 und 30 FPS reicht locker aus. Das Entscheidende ist nur, dass die Frames regelmäßig gezeichnet werden, sonst hast du Ruckler. Angeblich können eingefleischte Gamer es sehen, wenn die FPS unter 60 fallen. Das ist zwar biologisch unmöglich aber, lassen wir sie in dem Glauben :cool:...
200 FPS ist natürlich viel zu hoch, du würdest über 3 Bilder zeichnen in der Zeit, in der dein Bildschirm eines darstellen kann...
Wie gesagt: 24-30 ist okay.
*Klugscheißmodus aus*

Hoffe ich konnte mal wieder etwas weiterhelfen.
Grüße Technipion
 
Zurück