20

この質問は、物理的および視覚的コンポーネント (ゲームなど) を使用したリアクティブ バナナおよびリアルタイム シミュレーションに固有のものです。

Fix Your Timestepによると!ゲーム ループをセットアップする理想的な方法 (再現性が必要な物理現象を想定) では、フレーム間に固定のタイムステップが必要です。いくつかの実際の複雑さを検討した後、著者は次のゲーム ループに到達します。

double t = 0.0;
const double dt = 0.01;

double currentTime = hires_time_in_seconds();
double accumulator = 0.0;

State previous;
State current;

while ( !quit )
{
     double newTime = time();
     double frameTime = newTime - currentTime;
     if ( frameTime > 0.25 )
          frameTime = 0.25;   // note: max frame time to avoid spiral of death
     currentTime = newTime;

     accumulator += frameTime;

     while ( accumulator >= dt )
     {
          previousState = currentState;
          integrate( currentState, t, dt );
          t += dt;
          accumulator -= dt;
     }

     const double alpha = accumulator / dt;

     State state = currentState*alpha + previousState * ( 1.0 - alpha );

     render( state );
}

dt概要は、数値安定性のために、物理シミュレーションには常に同じ時間増分 ( ) が供給されるということです。そのための調整では、物理とビジュアルが異なる頻度で更新される可能性があることを考慮する必要があり、遅れすぎないようにする必要があります。

たとえば、20hz の頻度で更新したいが、60hz のフレームレートで視覚的な更新が必要な場合があります。このループは物理の線形補間を行い、物理の更新とグラフィックの更新の違いを補います。

dtさらに、フレーム間の時間の差が のチャンクでの更新のステップ実行を処理するためのループよりもはるかに大きい場合dt。死のスパイラルに関するメモは、物理計算が更新の望ましい頻度に追いつかない場合に言及しているだけなので、いくつかの更新をスキップできるようにします。

この議論で私が最も興味を持っている部分は、物理エンジンへの呼び出し ( への呼び出しintegrate) が常に によってステップ実行されるように調整することdtです。反応バナナは、ユーザーがこのスタイルのループを書くことを許可しますか? もしそうなら、どのように?おそらく、リアルタイムの物理シミュレーションを実行する例が用意されている (または既に存在する) のでしょうか?

4

1 に答える 1

19

この議論で私が最も興味を持っている部分は、物理エンジンへの呼び出し (integrate の呼び出し) が常に dt によってステップされるように調整することです。反応バナナは、ユーザーがこのスタイルのループを書くことを許可しますか?

はい、反応バナナはそれを行うことができます。

アイデアは、カスタム イベント ループを作成し、reactive-banana をそれにフックすることです。ライブラリは、イベントをどこから取得するかについて何の仮定も行いません。それは、既存のイベントに関して新しいイベントを適切に記述するという問題を「のみ」解決します。特に、この関数を使用newAddHandlerして、イベント ループの適切な場所で呼び出される 2 つのコールバック関数を作成できます。基本的に、reactive-banana は、状態を維持する通常のコールバック関数を作成するための、気が遠くなるような方法です。これらの関数をいつどのように呼び出すかは、あなた次第です。

一般的な概要は次のとおりです。

-- set up callback functions
(renderEvent, render) <- newAddHandler
(stateUpdateEvent, stateUpdate) <- newAddHandler

-- make the callback functions do something interesting
let networkDescription = do
    eRender      <- fromAddHandler renderEvent
    eStateUpdate <- fromAddHandler stateUpdateEvent
    ...
    -- functionality here

actuate =<< compile networkDescription

-- event loop
while (! quit)
{
    ...
    while (accumulator >= dt)
    {
        stateUpdate (t,dt)      -- call one callback
        t += dt
        accumulator -= dt
    }
    ...
    render ()                   -- call another callback
}

実際、私は古いバージョンのリアクティブ バナナ用にこのスタイルでゲーム ループの例を書いたことがありますが、それを磨き上げてハックで公開するまでには至っていません。私が完成させたい重要なことは次のとおりです。

  • インストールが簡単で、GHCi で動作するグラフィック エンジンを選択します。コンセプトはSDLですが、GHCiからは使えないのでかなり厄介です。OpenGL + GLFW のようなものがいいでしょう。
  • 補間フェーズを簡単に記述できるように、小さな抽象化を提供します。おそらくeTimer :: Event t ()、定期的な物理の更新を表すイベントとbSinceLastTimer :: Behavior t TimeDiff、最後の物理の更新からの時間を測定する動作 (補間を行うために使用できる) の 2 つだけです。(これはイベントではなくビヘイビアであるため、内部の「draw this!」の更新は透過的です。)

Reactive-banana を使用したAndreas Bernstein のブラックアウト クローンは、このスタイルで実装する優れた例かもしれません。

于 2012-10-02T17:14:22.863 に答える