2

私は物理学に重点を置いたゲームを構築しています。したがって、非常に特定の間隔でゲームを実行する必要があります。現在のコード:

public double period = .02; //this is the run interval in seconds

//main gameLoop
public void gameLoop(){
    long startTime;
    long sleep;

    while(running){
        startTime = System.nanoTime();

        Graphics2D g = s.getGraphics();
        operateEntities(g);
        g.dispose();
        s.update();
        //figure out how long it must sleep to take .02s altogether
        sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);
        try{
            if(sleep > 0){
                Thread.sleep(sleep);
            }else{
                System.err.println("Warning: program runtime exceeded period");
            }
        }catch(Exception ex){}

        gameTime += period;
    }
}

これは期待どおりに機能していません。現在、メインスレッドはまったくスリープせずに実行されており、「警告:プログラムの実行時間が超過しました」という警告が表示されます。

以前はSystem.currentTimeMillis()を使用していましたが、目的に対して十分に正確ではなかったため、System.nanoTime()に切り替えました。

期間を長くすると、実際にはプログラムの速度が上がり、短くすると遅くなります。

単純な論理的な問題はありますか?System.nanoTime()についての私の理解はオフですか?または、メソッドoperateEntitiesを実行し、特定の間隔で破棄し、更新するためのより良い方法はありますか?

編集:記録のために、プログラムは完了するのに.02秒以上かかりません。テスト済みです

4

2 に答える 2

8

ナノ秒はミリ秒よりも小さいため、ナノ秒->ミリ秒を変換するには、100000で除算する必要があります。複数で除算する必要はありません。

    //figure out how long it must sleep to take .02s altogether
    sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);

に変更する必要があります

    //figure out how long it must sleep to take .02s altogether
    sleep = ((int)(period*1000) - (System.nanoTime() - startTime)/100000);

現在のコードは、200ミリ秒から大きな数を引いてスリープしようとしており、スリープが負になり、「警告:プログラムの実行時間が期間を超えました」という出力が表示されます。

于 2012-08-06T19:36:48.343 に答える
2

コードを分解すると、いくつかの問題があります。

//Multiplies by 100,000 rather than divides.
sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);
//Note that sleep here is a very small number minus a very large number: probably negative.

try{
  if(sleep > 0){//If positive, sleep
    Thread.sleep(sleep);
  } else{//throws an error in all other cases.
    System.err.println("Warning: program runtime exceeded period");
  }
}catch(Exception ex){}//Empty exception handling poorly handles the thread.sleep() Exception requirement.

period値を大幅に大きくしない限り、このコードは常にエラーになります。ただし、それを超えても、正確なタイミングという目的の結果が得られない可能性があります。コアループとは:

  • 0.02秒間の物理を計算します。
  • 寝る。
  • 何時か確認してください。
  • 特定の期間(0.02秒)が経過した場合は続行し、それ以外の場合は再度スリープします。
  • 繰り返す。

十分に小さいタイムスライスを使用すると、これは正確になります。ただし、スレッドはそのようには機能しません。スレッドがいつウェイクアップするかは保証できません。それは決してあり得ない。それは3秒である可能性があります。それは即座に可能性があります。おそらく、あなたはあなたの期間が何であれオーバーシュートするでしょう、そしてあなたは事実上それを完全に打ち負かすことは決してないでしょう。

特定の増分期間に依存するのではなく、毎回一貫して経過する特定の期間に依存するのではなく、実際に経過した期間ですべての物理をスケーリングする必要があります。

  • 寝る。
  • どれくらいの時間が経過したかを調べます。
  • その期間の物理学を計算します。
  • 繰り返す。

それでも小さなタイムスライスをスリープ状態にする必要がありますが、このようにして、スレッドスケジューラによって発生するエラーを排除します。

于 2012-08-06T19:43:59.213 に答える