フレームに依存しない動き


11

私はここで、動きに関する他の2つのスレッドを読みました: 時間ベースの動きとフレームレートベースの動き?、および 固定または可変の時間ステップをいつ使用すべきですか?

しかし、どちらのスレッドが何を話しているのか理解できないので、フレームに依存しない動きの基本的な理解が不足していると思います。

私はlazyfooのSDLチュートリアルに従っていて、フレームに依存しないレッスンに出会いました。http://lazyfoo.net/SDL_tutorials/lesson32/index.php

コードの動きの部分が何を言おうとしているのかわかりませんが、これはそうだと思います(私が間違っている場合は修正してください):フレームに依存しない動きをするためには、オブジェクトの距離を知る必要があります(例:スプライト)は、たとえば1秒など、特定の時間枠内で移動します。ドットが200ピクセル/秒で移動する場合、200 ppsに1/1000秒を掛けて、その1秒内に移動する量を計算する必要があります。

そうですか?レッスンは言う:

「秒あたりのピクセル数での速度*秒単位での最後のフレームからの時間。したがって、プログラムが毎秒200フレームで実行される場合:200 pps * 1/200秒= 1ピクセル」

しかし... 200 ppsを1/1000秒で乗算していると思いました。1秒あたりのフレーム数でこのビジネスは何ですか?

フレームに依存しない動きがどのように機能するかについて、もう少し詳しく説明してもらえれば幸いです。

ありがとうございました。

添加:

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 );
}

これは、ドットを画面の下に移動するコードです。これまでのところ、すべてが正しいと思います。画面を下に移動しますが、説明できない奇妙なことが発生します。ドットは、そのy値より大きくなると、y = 580に留まるはずです。ただし、プログラムを実行するたびに、ドットは異なる場所に移動します。つまり、580を少し超える程度です。そのため、ドットは画面の半分または半分以上です(ドットは20ピクセル、画面寸法800x600)。プログラムのタイトルバーをクリックして押したままにして放すと、ドットが画面から消えます。どうして毎回可変なのですか?タイトルバーの問題については、タイトルバーを押し続けると、タイマーがまだ実行中であり、経過時間が長くなるためだと思います。その結果、次のフレームでドットが移動する距離が長くなります。そうですか?


あなたの追加は実際には別の質問です。既存の質問に追加するのではなく、2番目の質問にする必要があります。ただし、簡単に答えることができます。たとえば、y移動を計算するだけです。yMovement = (yVel * (SDL_GetTicks() - startTicks)/1000.f);その後、次のようにしますif(y + yMovement <= 580){ y += yMovement; } else { y = 580; }
。– bummzack

回答:


10

注:すべての分数は浮動小数点数を意味します。

フレームに依存しない動きは、動きを時間に基づかずに動作します。最後のフレーム以降に費やされた時間を取得し(1秒に60フレームある場合、すべてのフレームに同じ時間がかかった場合、各フレームには1.0 / 60.0秒かかります)、その動きの量を確認しますに変換します。

エンティティに特定の時間単位で一定量のスペースを移動させたい場合(1秒ごとに100ピクセルと言います)、フレームごとに移動する必要があるピクセル数を、現在のフレームでどれだけの動きが発生するかを把握するために、秒単位で渡された時間(1.0 / 60.0)で秒(100ピクセル)。

経過時間と時間の単位で定義された速度(秒またはミリ秒が望ましい)を使用して、フレームごとに実行する必要のある動きを把握することで機能します。したがって、計算は次のようになります。movementPerSecond * (timeElapsedInMilliseconds / 1000.0)

それがあなたにとってある種の理にかなっているといいのですが。

私は私の男を毎秒200ピクセル右に移動したいと思います。現在のフレームが前のフレームの50ミリ秒後に実行される場合は、以前に指定した速度(200ピクセル)の何分の1かを移動する必要があります。時間の1/20(50/1000 = 1/20)しか経過していないので、距離の50/1000を彼に移動する必要があります。したがって、彼を10ピクセルだけ移動することは理にかなっています(さらに19のフレームが発生し、互いに50ミリ秒離れている場合、その1秒の移動の合計量は200ピクセル、つまり必要な量です)。

フレームに依存しない動きが機能する方法は、フレームは通常、さまざまなタイムステップで発生することです(後続のフレーム間で発生する時間の量は異なります)。エンティティをフレームごとに一定の距離で絶えず移動する場合、移動はフレームレートに基づきます。フレーム間の時間が長い場合、ゲームの動きが遅すぎるように見え、フレーム間の時間があまりない場合、ゲームは速くなるように見えます。(フレーム間の時間が短すぎる=フレームが多い=動きが多い)これを克服するために、時間の観点から速度を使用し、フレーム間の時間を追跡します。これにより、最後に位置を更新してからの経過時間と、エンティティをさらにどれだけ移動する必要があるかがわかります。

1秒あたりのフレーム数:1秒あたりに発生するフレームの量です。通常、フレームレートは、ゲームが1秒間に描画またはレンダリングされる回数、またはゲームループが1秒間に完了する回数です。

Fixed Verse Variable Time Step:これはフレーム間の時間量を指します。通常、フレーム間の時間は一定ではありません。物理学のような特定のシステム/コアは、何かをシミュレーション/実行するために、時間の単位を必要とします。通常、時間ステップが固定されている場合、物理システムはより安定/拡張可能です。固定/可変時間ステップの違いは名前にあります。固定タイムステップとは、固定時間ステップで発生するタイムステップです。可変時間ステップは、時間の変化する/異なる速度で発生する時間ステップです。


あなたが与える例では、50ミリ秒は各フレームの時間ですよね?そして、それは1000 / FPSによって計算されましたか?そして、各フレームに必要な動きは、1秒あたりのピクセル数* 50/1000ですか?
ShrimpCrackers 2011年

ええと、私は各時間枠のミリ秒がおそらく可変であることに気づきましたね?getTicks()-startTicksのようなものは常に異なり、一定ではありません。
ShrimpCrackers

@オムニオン:「1秒あたりのピクセル数」で距離を指定する場合、ミリ秒は使用できません...まったく異なる結果になる1000/60ではなく、1.0 / 60.0にする必要があります。
bummzack

@ShrimpCrackersはい、経過時間は変化します。60 fpsのレンダリングができない古いPCを想像してみてください。そのようなマシンでは、ゲームを同じ速度(同じfpsではない)で実行する必要があります。
bummzack

では、lazyfooチュートリアルで、deltaticks / 1000.fの1000はどういう意味ですか?FPS?1000ミリ秒?私は今少し混乱しています。FPSは各フレームに必要な時間を決定するために必要であるように見えますが、実際には動きに計算されていません。
ShrimpCrackers

7

フレームダイナミクスでは、(たとえば)エンティティを移動するためのコードは次のようになります。

x = x + speedPerFrame

フレームに依存しない場合は、次のようになります。

x = x + speedPerSecond * secondsElapsedSinceLastFrame

ありがとう、それは理にかなっています。上記で別の質問があります。
ShrimpCrackers

1

追加の質問について。

移動するときに境界(y> 580)をチェックしないため、ポイントは毎回異なる場所で停止します。580を過ぎると、それ以上の更新を停止しません。

580を越える前の最後のフレームでは、579から開始することも、570で開始することも、100で開始することもできます。最後のフレームの実行にかかった時間に応じて、1ピクセルまたは1000ずつ進めることもできます。

IF条件をこのようなものに変更するだけで大​​丈夫です。

if ( y <= 580 )
{
    yVel += DOT_VEL;
    y += (yVel * (SDL_GetTicks() - startTicks)/1000.f);
    if( y > 580 )
    {
        y = 580;
    }
    posRect.y = (int)y;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.