Wie kann ich Framerate-unabhängige Updates implementieren?

7

Bearbeiten: Ich habe das Problem behoben, indem ich die Sprunggeschwindigkeit so eingestellt habe, dass sie geändert und hinzugefügt wird, und die Geschwindigkeit gleich der Sprunggeschwindigkeit * t ist. Das habe ich gerade:

if (GUI->Space && grounded){
    jumpvelocity = -185.f * 2  / gravity;
    grounded = false;

}

if(!grounded ){
    if(jumpvelocity < terminaly){
        jumpvelocity += 185.f * t * 4  / gravity;
        yvelocity = jumpvelocity * t;
    }
    else{
        jumpvelocity = terminaly;
        yvelocity = jumpvelocity * t / gravity;
    }
}
else{
    yvelocity = 0.f;
    jumpvelocity = 0.f;
}

Danke für die Hilfe.

Ich versuche meine Schwerkraft zum Arbeiten zu bringen. Der Code, den ich bisher habe, ist

if (jumpvelocity < 0){
   yvelocity = jumpvelocity * t *2/gravity;
    jumpvelocity += 185.f*t * 2 / gravity;
}

else if(!grounded ){
    if(yvelocity < terminaly)
        yvelocity += t / gravity;
    else
        yvelocity = terminaly;
}

Die Schwerkraft steigt nach oben, je höher das Fallen und Springen langsamer wird. Der Standardwert ist 1. Die Sprunggeschwindigkeit ist auf 185 eingestellt, wenn der Spieler springen möchte. Mein Problem ist, dass Sie bei einer langsameren Framerate langsamer fallen, aber mit der gleichen Geschwindigkeit springen, als wäre es eine höhere Framerate. Wie würde ich es rahmenunabhängig machen? T ist die Deltazeit.

user975989
quelle
Können Sie den Code anzeigen, in dem Velocity zum Ändern der Position verwendet wird?
Jordaan Mylonas
Also, re dein Kommentar ... was ist das Problem? Für x und y funktioniert es genauso.
Ingenieur
Ich benutze SFML, also bist es nur du. Bewegen (xvelocty, yvelocity).
user975989
Ihr aktualisierter Code weist immer noch alle Probleme auf, auf die ich hingewiesen habe. jumpvelocityBeim Aktualisieren yvelocitywird kein Mittelwert mit dem vorherigen Wert erstellt , was zu einem Genauigkeitsfehler führt, der sich bei niedrigerer Framerate verschlechtert. Was Sie nennen, yvelocitysollte eigentlich sein, ymovementda es sich um eine Geschwindigkeit multipliziert mit der Zeit handelt. Sie aktualisieren, jumpvelocitywenn der Charakter den Boden nicht mehr berührt, was keine physische Bedeutung hat. Ihre Überprüfung terminalyerfolgt vor dem Update, was jumpvelocitydazu führt , dass die terminalyFrameraten größer als und bei niedrigeren Frameraten schlechter werden.
Sam Hocevar

Antworten:

13

Bei jeder Hauptschleifeniteration:

  1. Überprüfen Sie Ihren System-Timer und speichern Sie ihn als currentTime(z. B. SDL_GetTicks ())
  2. timeDelta = currentTime - lastTime
  3. In timeDeltaSekunden konvertieren (normalerweise in ms oder ns), speichern alstimeDeltaSeconds
  4. For each entity in entities: entity.position = velocityInUnitsPerSecond * timeDeltaSeconds
  5. lastTime = currentTime (könnte auch vor Schritt 1 statt hier erfolgen)

... Sie müssen Ihre aktuellen Zahlen so einstellen, dass sie in Einheiten pro Sekunde angegeben sind, dh dass sie bei 1 FPS sinnvoll sind. Wenn Sie beispielsweise erwartet haben, dass die horizontale Geschwindigkeit Ihres Charakters 2 Pixel pro Zehntelsekunde beträgt, müssen Sie sie jetzt auf 20 Pixel pro Sekunde einstellen. Auf diese Weise funktioniert das oben Genannte korrekt.

Für einige Anwendungen ist es besser, den Zeitschritt auf einen bestimmten Wert festzulegen. In diesem Fall timestep != timeDelta. Letzteres ist die tatsächlich verstrichene Systemzeit. Ersteres ist eine quantisierte Menge, die Sie jedes Mal, wenn Sie logische Aktualisierungen in Ihrem Spiel anwenden, von Ihrem Zeitspeicher verbrauchen möchten. Dies unterstützt die Integration, wenn Sie iterative Löser verwenden, wie sie in den bekannten Physik-Engines zu finden sind, da die Zeitschritte bei der Berechnung gleich sein müssen. In diesem Fall bleibt nur ein Bruchteil der verbleibenden Zeit übrig , da Sie nicht einfach die gesamte verbleibende Zeit für jedes Tick verbrauchen, und Sie müssen diese für die Verwendung in späteren Ticks akkumulieren. Weitere Informationen hierzu finden Sie unter Fix Your Timestep von Glenn Fiedler.

Ingenieur
quelle
Entschuldigung, ich bin mir nicht sicher, wie ich auf eine Antwort antworten soll, aber ich habe bereits einen Zeitschritt festgelegt, der je nach zurückgegebenem Wert 1/60 Sekunde oder weniger beträgt. Meine xvelocity funktioniert gut und ist rahmenunabhängig (200.f * t), meine yvelocity jedoch nicht.
user975989
Sie möchten nicht velocityInUnitsPerSecondnur die Durchschnittsgeschwindigkeit während des verstrichenen Zeitrahmens. Wenn die Geschwindigkeit konstant ist, ist Ihr Code korrekt. Wenn sich die Geschwindigkeit linear ändert, wie dies bei der Schwerkraft der Fall ist, ist dieser Wert 0.5 * (OldVelocity + NewVelocity). Andernfalls müssen Sie möglicherweise über einen kleineren Zeitschritt integrieren, um Genauigkeitsprobleme zu vermeiden.
Sam Hocevar
0

Es scheint überhaupt nicht jumpvelocitynötig zu sein. Springen bedeutet, die Geschwindigkeit sofort auf einen bestimmten Wert einzustellen: Sobald die Füße den Boden nicht mehr berühren, kann die Geschwindigkeit nur durch die Schwerkraft (und den Luftwiderstand, den Sie mit der Endgeschwindigkeit implementieren) geändert werden.

Eine weitere Bemerkung: Gleitkommadivisionen sind extrem langsam. Ich schlage vor, Sie speichern 1.0f / gravitystatt gravity.

Schließlich besteht die Möglichkeit, dass für einen Frame yvelocitygrößer sein kann als terminaly.

Mein Vorschlag:

if (GUI->Space && grounded) {
    yvelocity = 185.f;
    grounded = false;
} else if (!grounded ) {
    yvelocity += t * inv_gravity;
    if (yvelocity > terminaly)
        yvelocity = terminaly;
}

Und denken Sie daran, dass Folgendes nicht korrekt ist:

ypos += t * yvelocity;

Sie müssen tun:

ypos += t * 0.5f * (old_yvelocity + yvelocity);
Sam Hocevar
quelle