外挿は衝突検出を中断します


10

スプライトの動きに外挿を適用する前は、衝突は完全に機能していました。ただし、(物事を滑らかにするために)スプライトの動きに外挿を適用すると、衝突が機能しなくなります。

これが、外挿前の動作です。

ここに画像の説明を入力してください

しかし、外挿を実装した後、衝突ルーチンが壊れます。これは、(レンダーコールにある)外挿ルーチンによって生成された新しい座標に作用しているためと考えています。

外挿を適用した後

ここに画像の説明を入力してください

この動作を修正するには?

外挿の直後に追加の衝突チェックを試みました-これは多くの問題を解決するようですが、レンダリングにロジックを入れることは問題外なので、これを除外しました。

私はまた、spritesXの位置のコピーを作成し、それを外挿して、オリジナルではなくそれを使用して描画してみたので、元のロジックをそのままにして、ロジックがピックアップできるようにしました。壁にぶつかったとき。これもこれに対処する正しい方法ではないと確信しています。

ここで似たような質問をいくつか見つけましたが、答えは役に立ちませんでした。

これは私の外挿コードです:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

外挿の適用

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

編集する

上で述べたように、私は特に描画するためにスプライトの座標のコピーを作ってみました...これにはそれ自身の問題があります。

まず、コピーに関係なく、スプライトが動いているときは非常に滑らかで、停止しているときは、時間に基づいて位置を推定しているため、左/右にわずかに揺れています。これは正常な動作ですか?スプライトが停止したときに「オフ」にできますか?

左/右のフラグを設定して、これらのいずれかが有効になっている場合にのみ外挿してみました。最後の位置と現在の位置をコピーして、違いがないか確認しました。ただし、衝突に関する限り、これらは役に立ちません。

ユーザーが言う、右ボタンとスプライトが右に移動している場合、壁にぶつかったときにユーザーが右ボタンを押し続けると、スプライトは壁に止められている間、右に動き続けます(したがって、実際には移動していません)。ただし、正しいフラグが設定されたままであり、衝突ルーチンが常にスプライトを壁の外に移動しているため、スプライトがまだ移動していることが(プレーヤーではなく)コードに表示されます。外挿が続行されます。だからプレーヤーが見るのは、スプライトの「静的」(はい、それはアニメーションですが、実際には画面上を移動していません)であり、外挿がそれを実行しようとすると、時々激しく揺れます... ..この助けを願っています


1
これは完全に理解するために消化し、いくつかの(「補間」は、一見ダース異なる意味を持っており、それはあなたがここで意味だけで何一見に完全に明確ではありません)かかりますが、私の最初の本能は、「あなたはやってはいけませんされて何かをあなたに影響を与えるためにレンダリングルーチンにおけるオブジェクトの位置」。レンダラーはオブジェクトの指定された位置にオブジェクトを描画する必要があります。本来、レンダラーとゲームの物理を結び付けているため、問題を解決するためのレシピがあります。理想的な世界では、レンダラーはゲームオブジェクトへのconstポインターを取得できる必要があります。
Steven Stadnicki 2014年

こんにちは@StevenStadnicki、コメントありがとうございます。レンダラーに渡される補間値を示す多数の例があります。これを参照してください:mysecretroom.com/www/programming-and-software/…これは私が私の適応した場所からのものですコード。私の限られた理解は、これが最後の更新からの時間に基づいて、最後の更新と次の更新の間の位置を補間することです-私はそれが少し悪夢であることに同意します!私がより扱いやすくなることを提案し、代替することができれば幸いです-乾杯:-)
BungleBonce 2014年

6
まあ、私は「何かのコードを見つけることができるからといってそれがベストプラクティスにならない」と言います。:-)ただし、この場合、リンク先のページは補間値を使用してオブジェクトの表示場所を特定しているようですが、実際にはオブジェクトの位置は更新されていません。フレームごとに計算される描画固有の位置を持つだけで、その位置をオブジェクトの実際の「物理的」位置から分離することで、それを行うこともできます。
Steven Stadnicki 2014年

こんにちは@StevenStadnickiです。私の質問(「コピーも作ってみました」で始まる段落)で概説していますが、実際には「描画のみ」の位置を使用しようとしました:-)補間の方法を提案できますか?レンダールーチン内でスプライトの位置を調整する必要はありませんか?ありがとう!
BungleBonce 2014年

コードを見ると、内挿ではなく外挿を行っているように見えます。
Durza007 2014年

回答:


1

コメントはまだ投稿できませんので、回答として投稿します。

問題を正しく理解すると、次のようになります。

  1. 最初に衝突があります
  2. 次に、オブジェクトの位置が修正されます(おそらく衝突検出ルーチンによって)
  3. オブジェクトの更新された位置がレンダリング関数に送信されます
  4. 次に、render関数は、外挿を使用してオブジェクトの位置を更新します
  5. 外挿されたオブジェクトの位置は、衝突検出ルーチンを壊します

考えられる解決策は3つあります。私は、少なくとも私見にとって最も望ましい順序でそれらをリストします。

  1. 外挿をレンダー関数の外に移動します。オブジェクトの位置を推定し、衝突をテストします。
  2. または、render関数で外挿を保持したい場合は、フラグを設定して、衝突が発生したことを示します。衝突検出でオブジェクトの位置を修正しますが、外挿値を計算する前に、まず衝突フラグを確認してください。オブジェクトはすでに必要な場所にあるため、移動する必要はありません。
  3. 最後の可能性は、修正よりも回避策の方が多いですが、衝突検出で過剰補償することです。衝突後、オブジェクトを壁から離し、外挿後、オブジェクトが壁に戻るようにします。

#2のサンプルコード。

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

#1の方が論理的に正確なソリューションのようですが、おそらく#2が最も早く簡単に実行できます。デルタ時間ソリューションの処理方法によっては、#1が大きなデルタによってほぼ同じ方法で壊れる可能性があります。その場合、#1と#2の両方を一緒に使用する必要があります。

編集:私はあなたのコードを以前に誤解しました。ループは、可能な限り速くレンダリングし、設定された間隔で更新することを目的としています。これが、更新以上の描画を行う場合に対処するために、スプライトの位置を補間する理由です。ただし、ループが遅れると、追いつくか、フレーム描画の最大数をスキップするまで、更新をポーリングします。

そうは言っても、唯一の問題は、オブジェクトが衝突後に動いていることです。衝突がある場合、オブジェクトはその方​​向への移動を停止する必要があります。したがって、衝突がある場合は、速度を0に設定します。これにより、レンダー機能がオブジェクトをそれ以上移動できなくなります。


こんにちは@Aholioオプション2を試してみましたが、機能しますが、いくつかの不具合が発生する可能性があります。しかし、私はこれを行う方法についての情報を見つけることができませんが、オプション1に非常に興味があります。ロジックルーチンの発言をどのように推定できますか?ちなみに私のデルタは固定されているので、1/60または0.01667です(むしろ、これは私が積分に使用する数値ですが、ゲームの各反復にかかる時間は、何が起こっているかによって明らかに異なりますが、実際には0.01667以上になるべきではありません。 )ですので、それに対するどんな助けでも大いに感謝します。
BungleBonce 2014年

多分私はあなたが何をしているか完全に理解していません。私にとって少し奇妙に思われるのは、ドロー関数で位置の移動を行っているだけではないということです。時間の計算も行っています。これはすべてのオブジェクトに対して行われますか?正しい順序は次のとおりです。時間計算はゲームループコードで行われます。ゲームループはデルタをupdate(dt)関数に渡します。Update(dt)は、すべてのゲームオブジェクトを更新します。動き、外挿、および衝突検出を処理する必要があります。update()がゲームループに戻った後、新しく更新されたすべてのゲームオブジェクトを描画するrender()関数を呼び出します。
Aholio 2014年

うーん現在、私の更新機能はすべてを行います。動きはありますか(動きとは、スプライトの新しい位置を計算することを意味します。これは、デルタ時間から計算されます)。(実際の描画は別として)レンダー関数で実行している唯一のことは、「新しい」位置を推定することですが、もちろん、これは最後のロジックの更新からレンダーコールまでの時間を考慮する必要があるため、時間を使用します。とにかくそれが私の理解です:-)どのようにこれを行いますか?ロジックの更新だけで外挿を使用する方法がわかりません。ありがとう!
BungleBonce 2014年

私の答えを更新しました。お
役に立てば幸い

ありがとう、元のQの「停止すると、少し左/右にぐらついている」を参照してください。スプライトを停止しても、外挿によってスプライトが「動いている」(ぐらつく)ままになります。これは、スプライトの古い位置と現在の位置が外挿を計算します。したがって、スプライトが静的であっても、フレームの反復ごとに計算される「外挿」値に基づいて、このウォブリング効果があります。私は「古いものと現在の位置が同じである場合、外挿しないでください」と言っても試しましたが、これはまだ機能していないようです。戻ってもう一度見てみましょう!
BungleBonce 2014

0

物理のレンダリングと更新を完全に分離する必要があるようです。通常、基礎となるシミュレーションは離散時間ステップで実行され、周波​​数は変化しません。たとえば、1/60秒ごとにボールの動きをシミュレートできます。それだけです。

可変フレームレートを可能にするために、レンダリングコードは可変周波数で動作する必要がありますが、シミュレーションは固定タイムステップでなければなりません。これにより、グラフィックはシミュレーションからメモリを読み取り専用として読み取り、外挿ではなく内挿を設定できます。

外挿は将来の値がどこにあるかを予測しようとしているため、動きの突然の変化はあなたに巨大な外挿エラーを与える可能性があります。代わりに、シミュレーションの背後にあるフレームについてシーンをレンダリングし、既知の離散位置間を補間することをお勧めします。

実装の詳細を確認したい場合は、このトピックに関する短いセクションを記事にまとめました。「タイムステッピング」というセクションをご覧ください。

記事の重要な疑似コードは次のとおりです。

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

このRenderGame機能は最も重要です。アイデアは、離散シミュレーション位置間の補間を使用することです。レンダリングコードは、シミュレーションの読み取り専用データの独自のコピーを作成し、一時的な補間値を使用してレンダリングできます。これはあなたが持っているように見えるような愚かな問題なしであなたに非常に滑らかな動きを与えます!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.