Rahmenunabhängige Bewegung

11

Ich habe hier zwei andere Themen zum Thema Bewegung gelesen: Zeitbasierte Bewegung Vs Bildratenbasierte Bewegung? und Wann sollte ich einen festen oder variablen Zeitschritt verwenden?

Aber ich glaube, mir fehlt ein grundlegendes Verständnis der rahmenunabhängigen Bewegung, weil ich nicht verstehe, wovon einer dieser Threads spricht.

Ich verfolge die SDL-Tutorials von lazyfoo und bin auf die rahmenunabhängige Lektion gestoßen. http://lazyfoo.net/SDL_tutorials/lesson32/index.php

Ich bin nicht sicher, was der Bewegungsteil des Codes zu sagen versucht, aber ich denke, es ist dies (bitte korrigieren Sie mich, wenn ich falsch liege): Um eine rahmenunabhängige Bewegung zu haben, müssen wir herausfinden, wie weit ein Objekt ist ( Beispiel Sprite) bewegt sich innerhalb eines bestimmten Zeitrahmens, zum Beispiel 1 Sekunde. Wenn sich der Punkt mit 200 Pixel pro Sekunde bewegt, muss ich berechnen, wie viel er sich innerhalb dieser Sekunde bewegt, indem ich 200 pps mit 1/1000 Sekunde multipliziere.

Ist das richtig? Die Lektion sagt:

"Geschwindigkeit in Pixel pro Sekunde * Zeit seit dem letzten Bild in Sekunden. Wenn das Programm also mit 200 Bildern pro Sekunde ausgeführt wird: 200 pps * 1/200 Sekunden = 1 Pixel"

Aber ... ich dachte, wir multiplizieren 200 pps mit 1/1000 Sekunde. Was ist das für ein Geschäft mit Bildern pro Sekunde?

Ich würde mich freuen, wenn mir jemand eine etwas detailliertere Erklärung geben könnte, wie rahmenunabhängige Bewegungen funktionieren.

Vielen Dank.

ZUSATZ:

SDL_Rect posRect;
posRect.x = 0;
posRect.y = 0;

float y, yVel;
y = 0;
yVel = 0;

Uint32 startTicks = SDL_GetTicks();

bool quit = false;
SDL_Event gEvent;

while ( quit == false )
{
    while ( SDL_PollEvent( &gEvent ) )
    {
        if ( gEvent.type == SDL_QUIT )
            quit = true;
    }

    if ( y <= 580 )
    {
        yVel += DOT_VEL;
        y += (yVel * (SDL_GetTicks() - startTicks)/1000.f);
        posRect.y = (int)y;
    }

    startTicks = SDL_GetTicks();
    SDL_BlitSurface( bg, NULL, screen, NULL );
    SDL_BlitSurface( dot, NULL, screen, &posRect );
    SDL_Flip( screen );
}

Das ist Code, der einen Punkt auf dem Bildschirm nach unten bewegt. Ich denke, ich habe bisher alles richtig gemacht. Es bewegt sich auf dem Bildschirm nach unten, aber es passiert etwas Seltsames, das ich nicht erklären kann. Der Punkt soll bei y = 580 bleiben, wenn er größer als dieser y-Wert wird. Jedes Mal, wenn ich das Programm starte, landet der Punkt an einer anderen Stelle, was ein wenig bis viel mehr als 580 bedeutet, sodass der Punkt auf halber oder mehr als auf halber Höhe vom Bildschirm entfernt ist (der Punkt ist 20 Pixel, Bildschirm Abmessungen 800x600). Wenn ich so etwas wie die Titelleiste des Programms anklicke und halte und dann loslasse, verschwindet der Punkt vom Bildschirm. Wie kommt es, dass es jedes Mal variabel ist? Was das Problem mit der Titelleiste betrifft, denke ich, dass der Timer noch läuft, wenn ich mich an der Titelleiste festhalte, und die verstrichene Zeit größer wird. Dies führt zu einer größeren Entfernung, um die sich der Punkt im nächsten Frame bewegt. Ist das richtig?

ShrimpCrackers
quelle
Ihre Hinzufügung ist eigentlich eine andere Frage. Sie sollten eine zweite Frage stellen, anstatt sie zu Ihrer vorhandenen Frage hinzuzufügen. Es kann jedoch leicht beantwortet werden: Berechnen Sie einfach die y-Bewegung, z. yMovement = (yVel * (SDL_GetTicks() - startTicks)/1000.f);dann mach:if(y + yMovement <= 580){ y += yMovement; } else { y = 580; }
bummzack

Antworten:

10

HINWEIS: Alle Fraktionen sind als Schwimmer gedacht.

Die rahmenunabhängige Bewegung basiert auf der Bewegung außerhalb der Zeit. Sie erhalten die Zeit, die seit dem letzten Bild aufgewendet wurde (wenn also 60 Bilder in einer Sekunde vorhanden sind, dauert jedes Bild 1,0 / 60,0 Sekunden, wenn alle Bilder dieselbe Zeit in Anspruch genommen haben) und finden heraus, wie viel Bewegung das ist übersetzt ins.

Wenn Sie möchten, dass Ihre Entität für eine bestimmte Zeiteinheit eine bestimmte Menge an Raum verschiebt (wir sagen 100 Pixel pro Sekunde), können Sie herausfinden, wie viele Pixel Sie pro Frame verschieben sollten, indem Sie die Bewegungsmenge pro multiplizieren Sekunde (100 Pixel) um die in Sekunden verstrichene Zeit (1,0 / 60,0), um herauszufinden, wie viel Bewegung im aktuellen Frame stattfinden soll.

Es funktioniert, indem Sie anhand der verstrichenen Zeit und einer Geschwindigkeit, die mit einer bestimmten Zeiteinheit definiert ist (Sekunden oder Millisekunden sind vorzuziehen), herausfinden, wie viel Bewegung Sie pro Frame ausführen sollten. Ihre Berechnung könnte also folgendermaßen aussehen:movementPerSecond * (timeElapsedInMilliseconds / 1000.0)

Ich hoffe, das hat für Sie einen Sinn ergeben.

Ich möchte meinen Mann jede Sekunde um 200 Pixel nach rechts bewegen. Wenn der aktuelle Frame 50 Millisekunden nach dem vorherigen Frame ausgeführt wird, sollte ich meinen Mann um einen Bruchteil der zuvor angegebenen Geschwindigkeit (200 Pixel) bewegen. Ich sollte ihn 50/1000 der Strecke bewegen, weil nur 1/20 (50/1000 = 1/20) der Zeit vergangen ist. Daher wäre es sinnvoll, ihn nur um 10 Pixel zu verschieben (wenn 19 weitere Bilder im Abstand von 50 Millisekunden auftreten würden, würde die Gesamtbewegung in dieser Sekunde 200 Pixel betragen, die von uns gewünschte Menge).

Die Art und Weise, wie eine rahmenunabhängige Bewegung funktioniert, besteht darin, dass Frames normalerweise in variablen Zeitschritten auftreten (zwischen nachfolgenden Frames findet eine unterschiedliche Zeitspanne statt). Wenn wir ein Objekt in jedem Frame ständig um eine konstante Distanz bewegen, basiert die Bewegung auf der Framerate. Wenn zwischen den Frames viel Zeit liegt, scheint sich das Spiel zu langsam zu bewegen, und wenn zwischen den Frames nicht viel Zeit liegt, scheint das Spiel zu schnell zu werden. (zu wenig Zeit zwischen Frames = viele Frames = mehr Bewegung) Um dies zu überwinden, verwenden wir eine zeitliche Geschwindigkeit und verfolgen die Zeit zwischen Frames. Auf diese Weise wissen wir, wie lange es her ist, seit wir die Position das letzte Mal aktualisiert haben, und wie viel weiter wir die Entität verschieben sollten.

Bilder pro Sekunde: Dies ist die Anzahl der Bilder, die pro Sekunde stattfinden. Normalerweise gibt eine Framerate entweder an, wie oft das Spiel pro Sekunde gezeichnet / gerendert wird oder wie oft die Spielschleife pro Sekunde abgeschlossen ist.

Feste Versvariable Zeitschritt: Dies bezieht sich auf die Zeitspanne zwischen Frames. Normalerweise ist die Zeit zwischen den Frames nicht konstant. Bestimmte Systeme / Kerne wie die Physik benötigen eine gewisse Zeiteinheit, um etwas zu simulieren / auszuführen. Normalerweise sind physikalische Systeme stabiler / skalierbarer, wenn der Zeitschritt festgelegt ist. Der Unterschied zwischen festen / variablen Zeitschritten liegt in den Namen. Feste Zeitschritte sind das, wonach sie klingen: Zeitschritte, die mit einer festen Zeitrate auftreten. Variable Zeitschritte sind Zeitschritte, die mit unterschiedlichen / unterschiedlichen Zeitraten auftreten.

Michael Coleman
quelle
In dem Beispiel, das Sie geben, sind 50 Millisekunden die Zeit für jeden Frame, richtig? Und das wurde mit 1000 / FPS berechnet? Die Bewegung, die Sie für jedes Bild benötigen, beträgt also Pixel pro Sekunde * 50/1000?
ShrimpCrackers
hm, mir wurde jedoch klar, dass die Millisekunden für jeden Zeitrahmen wahrscheinlich variabel sein würden, nicht wahr? So etwas wie getTicks () - startTicks wäre immer anders und nicht konstant.
ShrimpCrackers
@Omnion: Wenn Sie den Abstand in "Pixel pro Sekunde" angeben, können Sie keine Millisekunden verwenden. Er sollte 1,0 / 60,0 und nicht 1000/60 betragen, was zu etwas völlig anderem führen würde.
Bummzack
@ ShrimpCrackers ja, die verstrichene Zeit ändert sich. Stellen Sie sich einen älteren PC vor, der keine 60 fps rendern kann. Sie möchten immer noch, dass das Spiel auf einem solchen Computer mit derselben Geschwindigkeit (aber nicht mit denselben Bildern pro Sekunde) ausgeführt wird.
Bummzack
Was bedeutet die 1000 im Lazyfoo-Tutorial in deltaticks / 1000.f? FPS? 1000 Millisekunden? Ich bin gerade ein bisschen verwirrt. Es scheint, dass FPS notwendig ist, um die für jeden Frame erforderliche Zeit zu bestimmen, aber dass es nicht tatsächlich in die Bewegung rechnet.
ShrimpCrackers
7

In der Rahmendynamik würde Ihr Code zum (zum Beispiel) Verschieben einer Entität folgendermaßen aussehen:

x = x + speedPerFrame

Wenn Sie rahmenunabhängig sein möchten, könnte dies folgendermaßen aussehen:

x = x + speedPerSecond * secondsElapsedSinceLastFrame
Wouter Lievens
quelle
Danke, das macht Sinn. Ich habe oben eine andere Frage.
ShrimpCrackers
1

In Bezug auf die zusätzliche Frage.

Ihr Punkt stoppt jedes Mal an verschiedenen Stellen, da Sie beim Verschieben nicht nach der Grenze (y> 580) suchen. Sie beenden die Aktualisierung erst nach 580.

Im letzten Frame, bevor Sie 580 überqueren, könnten Sie bei 579 beginnen oder bei 570 oder bei 100. Sie könnten auch 1 Pixel vorwärts oder 1000 gehen, je nachdem, wie lange die Ausführung des letzten Frames gedauert hat.

Ändern Sie einfach Ihre IF-Bedingung in so etwas und es sollte Ihnen gut gehen.

if ( y <= 580 )
{
    yVel += DOT_VEL;
    y += (yVel * (SDL_GetTicks() - startTicks)/1000.f);
    if( y > 580 )
    {
        y = 580;
    }
    posRect.y = (int)y;
}
Tim O'Neil
quelle