5

私は現在、物理ベースのゲーム用の FPS に依存しないゲーム ループの次の実装に近いものを持っています。私がテストしたほぼすべてのコンピューターで非常にうまく機能し、フレームレートが低下してもゲーム速度を一定に保ちます. しかし、私は組み込みデバイスに移植する予定であり、ビデオでより苦労する可能性が高く、それでもマスタードをカットできるかどうか疑問に思っています.

編集:

この質問では、msecs() が、プログラムの実行にかかった時間をミリ秒単位で返すと仮定します。ミリ秒の実装は、プラットフォームによって異なります。このループも、異なるプラットフォームでは異なる方法で実行されます。

#define MSECS_PER_STEP 20
int stepCount, stepSize;  // these are not globals in the real source

void loop() {
    int i,j;
    int iterations =0;
    static int accumulator; // the accumulator holds extra msecs
    static int lastMsec;
    int deltatime = msec() - lastMsec;
    lastMsec = msec();

    // deltatime should be the time since the last call to loop
    if (deltatime != 0) {
        // iterations determines the number of steps which are needed
        iterations = deltatime/MSECS_PER_STEP;

        // save any left over millisecs in the accumulator
        accumulator += deltatime%MSECS_PER_STEP;
    }
    // when the accumulator has gained enough msecs for a step...
    while (accumulator >= MSECS_PER_STEP) {
        iterations++;
        accumulator -= MSECS_PER_STEP;
    }
    handleInput(); // gathers user input from an event queue
    for (j=0; j<iterations; j++) {
        // here step count is a way of taking a more granular step 
        // without effecting the overall speed of the simulation (step size)
        for (i=0; i<stepCount; i++) {
            doStep(stepSize/(float) stepCount); // forwards the sim
        }
    }
}
4

1 に答える 1

6

いくつかコメントがあります。1つ目は、コメントが足りないことです。何をしようとしているのかがはっきりしないところもあるので、もっといい方法があるとは言い難いのですが、気になったら指摘していきます。ただし、最初に:

#define MSECS_PER_STEP 20
int stepCount, stepSize;  // these are not globals in the real source

void loop() {
    int i,j;
    int iterations =0;

    static int accumulator; // the accumulator holds extra msecs
    static int lastMsec;

これらは何にも初期化されていません。おそらく 0 になりますが、初期化する必要があります。また、それらを static として宣言するのではなく、loop参照によって渡す構造体に配置することを検討することをお勧めします。

    int deltatime = msec() - lastMsec;

lastMsecではない (初期化され、おそらく 0 である) ため、これはおそらく大きなデルタとして開始されます。

    lastMsec = msec();

この行は、最後の行と同様に、 を呼び出しますmsec。これはおそらく「現在の時刻」を意味し、これらの呼び出しは十分に近いため、両方の呼び出しで返される値はおそらく同じであり、これはおそらく予想どおりですが、それでも関数を 2 回呼び出します。int now = msec(); int deltatime = now - lastMsec; lastMsec = now;この関数を 2 回呼び出すのを避けるために、これらの行を に変更する必要があります。現在の時刻を取得する関数は、多くの場合、考えているよりもはるかにオーバーヘッドが大きくなります。

    if (deltatime != 0) {
        iterations = deltatime/MSECS_PER_STEP;
        accumulator += deltatime%MSECS_PER_STEP;
    }

これが何をするかを説明するコメントをここに置く必要があります。また、変数が何を意味するかを説明する上記のコメントも必要です。

    while (accumulator >= MSECS_PER_STEP) {
        iterations++;
        accumulator -= MSECS_PER_STEP;
    }

このループにはコメントが必要です。また、存在しない必要があります。に置き換えられた可能性があるようiterations += accumulator/MSECS_PER_STEP; accumulator %= MSECS_PER_STEP;です。除算とモジュラスは、ハードウェア除算を備えたマシンのループよりも短く、一貫した時間で実行する必要があります (多くの場合)。

    handleInput(); // gathers user input from an event queue

    for (j=0; j<iterations; j++) {
        for (i=0; i<stepCount; i++) {
            doStep(stepSize/(float) stepCount); // forwards the sim
        }
    }

入力に関係なくループ内でステップを実行すると、ゲームの実行が遅くなり遅れると、ゲームが応答しなくなるという効果があります。少なくとも、ゲームが遅れると、すべての入力が積み重なって一緒に実行され、ゲーム内のすべての時間が 1 つのチャンクで通過するように見えます。これは失敗するための優雅とは言えない方法です。

さらに、jループ (外側のループ) が何を意味するかは推測できますが、内側のループについてはあまり明確ではありません。また、doStep関数に渡される値 - それはどういう意味ですか。

}

これが最後の中括弧です。寂しげに見えると思います。

あなたの関数を呼び出す限り、何が起こるかわかりません。loopそれはあなたの制御外である可能性があり、それがこの関数の動作とその外観を決定する可能性がありますが、そうでない場合は、構造を再検討してください. それを行うためのより良い方法は、繰り返し呼び出される関数を持つことですが、一度に 1 つのイベントのみを使用することです (比較的短い期間で定期的に発行されます)。これらのイベントは、ユーザー入力イベントまたはタイマー イベントのいずれかです。ユーザー入力イベントは、次のタイマー イベントに反応するように設定するだけです。(処理するイベントがない場合は眠ります)

処理が遅れた場合に多少の誤差が生じる可能性がありますが、各タイマー イベントは常に同じ周期で処理されると想定する必要があります。ここで気付くかもしれない主な奇妙な点は、ゲームがタイマーイベントの処理に遅れをとってから再び追いつくと、ゲーム内の時間が遅くなり (リアルタイム以下)、次にスピードアップ (リアルタイムになる) するように見えることです。その後、速度を落とします (リアルタイムに)。

これに対処する方法としては、一度に 1 つのタイマー イベントのみをイベント キューに入れることを許可することが含まれます。これにより、時間が遅くなり (リアルタイムよりも遅く)、その後、スーパー スピード インターバルなしで (リアルタイムに) 速度が戻るように見えます。 .

これを行う別の方法は、あなたが持っているものと機能的に似ていますが、各タイマーイベントを処理する最後のステップを次のタイマーイベントのキューに入れることです (他の誰もタイマーイベントを送信しないことに注意してください {最初のone} これがゲームの実装方法として選択した場合)。これは、タイマーイベント間の定期的な時間間隔をなくすことを意味し、プログラムがスリープする機能も制限します。これは、少なくともイベントキューが検査されるたびに、処理するタイマーイベントがあるためです。

于 2010-10-18T19:44:26.490 に答える