2011-12-18 11 views
0

固定タイムステップシステム(60FPS)用に書かれたSonic physics engineのロジックを可変タイムステップ時代(正確にはSlick2D)にコピーしようとしています。可変タイムステップと重力/フリクション

オリジナルでは、ジャンプボタンを押したときに、プレーヤーのvelocity.yは-6.5に設定され、それぞれの目盛り0.21875は重力をモデル化するためにvelocity.yに追加されます。

論理更新が呼び出されるたびに、何ミリ秒経過したかを指定する時間デルタパラメータが渡されます。私が予想していたよりももっと多くのミリ秒が経過しているなら、ターゲットフレームの '残りの部分'を扱っている場合、更新ロジックを繰り返し、 '内側のデルタ'を1以下にします。

など。フレームが16msかかると予想される場合、に16msかかります。ループは1回反復し、thisMiniTickを1に渡します。デルタが16msではなく40msであれば、ループは3回実行され、そして最後に0.5。

私は間違いなくこれらの内部更新ループのそれぞれでvelocity.y += (gravity * thisMiniTickRelative)を実行できると考えていましたが、これは機能しません。より速いフレームレートでは、十分な重力が適用されずにジャンプが高くなり、フレームレートが遅くなるとジャンプが低くなります(ただし、目立つほど近くにはありません)。

実質的にすべてのフレームレートで動作する方法がありますか、またはdeltaの上限と下限を設定する必要がありますか?

「インナーアップデート」ループ:

float timeRemaining = delta/1000f; 
    while(timeRemaining > 0) 
    { 
     float thisMiniTick = Math.min(timeRemaining, 1f/FRAMES_PER_SECOND); 
     float thisMiniTickRelative = thisMiniTick/(1f/FRAMES_PER_SECOND); 

     updateInput(container, game, thisMiniTickRelative); 
     if (playerAirState) 
     { 
      playerVelocity.y += (GRAVITY * thisMiniTickRelative); 
     } 
     clampPlayerVelocity(); 
     playerPosition.add(playerVelocity); 
     doCollisions(); 
     timeRemaining -= thisMiniTick; 
    } 

答えて

2

は「deltaのためにバインドされた上部および下部の設定に頼る」としてそれについて考えてはいけません。あなたのアプリケーションとスレッドは、あなたのアプリケーションのOSスケジューリング時間、システム上の他のすべての要求、およびあなたが気づいておく必要のあるものがあります。この課題は、シングル・タスク・オペレーティング・システムからマルチ・タスキング・オペレーティング・システムに移行した頃と同じくらい、PCゲームでは古くなっています。

Slickを使用すると、ロジックの更新をレンダリングの更新から切り離すことができます(その結果、deltaの値がアプリケーションに渡されます)。これを行うには.setMinimumLogicUpdateInterval and .setMaximumUpdateIntervalメソッドを使用します。

Slickのプロジェクトも含めて、私が取り組んだプロジェクトでは、毎秒30-60回のロジックアップデート(アップデートの間に30.3ミリ秒から16.6ミリ秒)の何かがうまくいき、必要なスムーズさあなたの動き、物理学、そして衝突計算から。

その手段が第二の範囲あたり30-60ロジックの更新のために、あなたは次のことをやりたい、ある文字通り何

:また、それはtimeRemaing値を計算しようとすることはよくある間違いです

container.setMinimumLogicUpdateInterval(16); // max 60 logic updates per second 
container.setMaximumLogicUpdateInterval(31); // min 30 logic updates per second 

が、あなたはこれをしたくありません。あなたはどれくらいの時間が経過したかによって、どれくらい移動したかを乗算したいだけです。 30ミリ秒が経過した場合、それは約1/33秒ですので、ゲームオブジェクトを1秒間に移動する量の1/33に移動する必要があります。上記のように設定上限/下限で

float timeElapsed = delta/1000f; 

playerVelocity.y += (GRAVITY * timeElapsed); 

は、あなたは timeElapsedは常に 0.030.06の間の値になることを確信しています。ゲームが停滞し、フレームレートが低下した場合でも、ロジックアップデートはこれらの範囲外には進まないでしょう。代わりに何が起こるかは、ゲーム全体が減速するように見えます(スクリーン上に過度にあった古いセガの日のように)。しかし、衝突や物理計算は期待通りに機能します。

+0

詳細な回答をいただきありがとうございます。私が把握できなかった魔法の解決策がないことを確認することは安心です。なぜあなたは時間を差別化アプローチと誤解していますか?私はもともと自分の速度に経過時間を掛けていましたが、衝突コードが大きな書き換えを必要とすることを意味しました。プレイヤーは1つのティックでタイル全体より多くを移動してタイルをスキップすることができました。 –

+0

ええ、私はそれを誇張しているかもしれませんが、私は、時間計算が、AI計算など、他のことをやりなおすためにどれくらいの時間がかかるかを人々がその値を使って調べようとしていることを見てきたと思います。あなたがそれを使うことができないということではなく、この問題に近づく様々な方法を使って、すべてを「delta」値にフックすると、最も信頼性の高い結果が得られます。 – jefflunt

+0

衝突のために動きが速すぎるプレイヤーにとって、その問題にアプローチするにはいくつかの方法があります。 1人は、プレイヤーが横断したスペース全体で衝突計算を行うことになります(プレイヤーが3タイルを移動した場合、それらのいずれかで起こった衝突を把握して戻す必要があります)。 (ロジック更新間隔の最小/最大を調整する)ロジックの更新の粒度を増やすか、プレイヤーの移動を遅くするかのどちらかです。 – jefflunt