質問
プログラムで固定ステップ ループを作成しようとしていますが、何らかの理由で正しく動作できないようです。基本的に必要なのは、次のことを行うループです。
while(!over) {
Update(elapsedtime);
Draw(elapsedtime);
}
または同様のものを .Thread.Sleep を使用してみましたが、実際の固定ステップループが得られたかどうかはわかりません。 これ ループの影響を受けるオブジェクトの状態を保存する方法が見つからないため、問題が発生しました。そのため、その部分を除外する必要があり、ループ内に多くのオブジェクトがある場合に速度が低下しました。
ループ内のすべてのオブジェクトの状態を常に保存せずに、固定ステップ ループを取得するにはどうすればよいですか?
解決
ゲームエンジンで作業している場合、タイマーはおそらくうまく機能しません。タイマーには、固定ステップループを処理して定期的に維持するための十分な精度がありません。
高精度タイマーを使用してこれを実装する必要があります。この場合、 Stopwatchクラスを使用する必要があります。 これにより、経過時間を正確に追跡するのに十分な精度が得られます。
更新する頻度(Ticks)を追跡し、ストップウォッチを使用して各更新にかかる時間を測定する必要があります。その後、潜在的にブロックするためにSleep()呼び出しを使用できますが、ほとんどのシステムでスリープが0〜1ミリ秒しかスリープしない場合でも、スリープの精度は14〜15ミリ秒に過ぎないことを認識してください。これにより、ループが非常に遅くなる可能性があるため、スピンロックなどを実装する必要がある場合があります(while(waiting){何かを行う})。 Sleep()は、可能であればCPUを節約するので便利です。スピンロックは、待機中にCPUサイクルを消費しますが、より正確になります。
他のヒント
基本的に n ティックごとに実行されるループを求めていますか?これは次のように聞こえます タイマー. 。BCL にはいくつかのタイマーがあります。あ サーバー用のタイマー または1つ ウィンドウフォーム用.
これらの線に沿って使用できます。以下は疑似コードであり、これがどのように行われるかを一般的に示すことを目的としています。つまり、コピーして貼り付けるだけではコンパイルできない可能性があります。
public class RepeatingTask
{
public MyObjectState _objectState;
public RepeatingTask(Timespan interval)
{
Timer timer=new Timer(Timer_Tick); //This assumes we pass a delegate. Each timer is different. Refer to MSDN for proper usage
timer.Interval=interval;
timer.Start();
}
private DateTime _lastFire;
private void Timer_Tick()
{
DateTime curTime=DateTime.Now();
DateTime timeSinceLastFire = curTime-lastFireTime;
_lastFire=DateTime.Now(); //Store when we last fired...
accumulatedtime+=timeSinceLastFire
while(accumulatedtime>=physicsInterval)
{
Update(physicsInterval);
accumulated-=physicInterval;
}
}
}
クロージャを使用して、タイマーが定義されているメソッドの状態をラップすることもできます。
編集
私は記事を読み、問題を理解しました。ただし、タイマーを使用することはできますが、彼が示しているように、物理エンジンを設定した間隔ごとにエンジンを呼び出すように関数を設定する必要があります。
WPF を使用していますか? WPF には、アニメーションを安定した速度で起動すると思われるイベントがいくつかあります。
編集
私の解釈を示すためにコードサンプルを更新しましたが、基本的に行うことは物理エンジンの間隔を定義することです。その後、「ループ/タイマー何でも」を通過するたびに行う必要があることは、それから経過した実時間を決定することです。最後の反復。次に、そのデルタをアキュマレータに保存します。これは、最後に呼び出してから失敗したすべての間隔について物理エンジンを呼び出すまでのカウントダウンに使用します。
私が苦労しているのは、ここでタイマーを使用する方が良いのか、専用のスレッドをスリープさせるか、他の実装を使用する方が良いのかということです。
Thread.Sleepは、固定ステップループを提供するのではなく、プログラムを続行させる前に、指定された時間だけ待機します。実際の周波数は、スリープの精度と、各ステップで同じ量の実時間を実行するプログラムの一貫性に依存します。ただし、ある程度のドリフトに対応できる場合は、これで十分であり、確かに最も簡単です。
実際の固定ステップループを取得するには、インターバルタイマー。タイマーが切れるとコールバックを介してコードを実行します。これはもはやループではないことに注意してください。ただし、コードは定期的に実行されます。繰り返しますが、多少のドリフトがある可能性があるため、より正確な情報が必要な場合は次のイベントが適切なタイミングで発生するように、各ステップで間隔を調整する必要があります。完全に正確にすることは実際には不可能です-結局のところ、ハードウェアベースのインターバルタイマーでも精度があります-しかし、うまくいけば目的に応じて十分に正確にすることができます。