4

あらかじめ決められた時間の経過後にいくつかのイベントが発生するゲームを開発しています。そのためには、ゲーム開始からの経過時間を計算する必要があります。これを行うには、メイン ループでスレッドを起動し、 System.nanoTime()で経過時間を計算します。

私は自分のスマートフォンでコードをテストしましたが、スリープ ボタンを押して電話をスリープ状態にしない限り (アクティビティのonpause()メソッドをトリガーします)、かなりうまく動作します。しかし、スリープボタンを押すと、非常に奇妙なことが時々あります

System.nanoTime()の代わりにSystem.currentTimeMillis()を使用してみましたが、状況はさらに悪化しています (マルチコア デバイスを搭載した Windows システムではSystem.nanoTime()に問題があったことはわかっています。ちなみに私のスマートフォンもマルチコアです)。

コードは次のとおりです。

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity implements Runnable {

   private Thread gameThread = null;
   private volatile boolean running = false;

   private long startTime;
   private long totalTime;

   private boolean[] afterNSeconds = new boolean[11];
   private static long oneBillion = 1000000000;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      TextView tv = new TextView(getApplicationContext());
      tv.setText("Start");
      setContentView(tv);
   }

   @Override
   public void onResume() {
      super.onResume();
      running = true;
      gameThread = new Thread(this);
      gameThread.start();
   }

   @Override
   public void onPause() {
      super.onPause();

      totalTime = System.nanoTime() - startTime;

      running = false;
      while (true) {
         try {
            gameThread.join();
            return;
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   @Override
   public void run() {
      startTime = System.nanoTime();

      while (running) {

         long elapsedTime = totalTime + System.nanoTime()
               - startTime;

         for (int j = 1; j <= 10; j++) {

            if (elapsedTime >= j * oneBillion
                  && !afterNSeconds[j]) {
               Log.d("MainActivity", j + ": elapsedTime = "
                     + elapsedTime );
               afterNSeconds[j] = true;
            }
         }
      }
   }
}

スリープボタンを押さないときの通常の出力は次のとおりです。

01-07 23:56:40.722: D/MainActivity(26579):  1: elapsedTime =  1021247230 
01-07 23:56:41.708: D/MainActivity(26579):  2: elapsedTime =  2007078538 
01-07 23:56:42.701: D/MainActivity(26579):  3: elapsedTime =  3000023615 
01-07 23:56:43.701: D/MainActivity(26579):  4: elapsedTime =  4000000538 
01-07 23:56:44.701: D/MainActivity(26579):  5: elapsedTime =  5000000846 
01-07 23:56:45.701: D/MainActivity(26579):  6: elapsedTime =  6000001230 
01-07 23:56:46.701: D/MainActivity(26579):  7: elapsedTime =  7000001384 
01-07 23:56:47.701: D/MainActivity(26579):  8: elapsedTime =  8000001923 
01-07 23:56:48.701: D/MainActivity(26579):  9: elapsedTime =  9000000769 
01-07 23:56:49.701: D/MainActivity(26579): 10: elapsedTime = 10000002000 

そして、3秒後にスリープボタンを押し、数秒後にもう一度押したときの異常な出力です。(異常な動作は 4 ~ 6 です) ご覧のとおり、4 秒未満経過した後、System.nanoTime()は代わりに 6 秒を報告します。

01-08 00:02:48.645: D/MainActivity(26579):  1: elapsedTime =  1001408231 
01-08 00:02:49.644: D/MainActivity(26579):  2: elapsedTime =  2000000615
01-08 00:02:50.644: D/MainActivity(27265):  3: elapsedTime =  3000000539 

01-08 00:02:50.802: D/MainActivity(27265):  4: elapsedTime =  6000000462
01-08 00:02:50.802: D/MainActivity(27265):  5: elapsedTime =  6000000462
01-08 00:02:50.802: D/MainActivity(27265):  6: elapsedTime =  6000000462

01-08 00:03:21.358: D/MainActivity(27265):  7: elapsedTime =  7000000770
01-08 00:03:22.358: D/MainActivity(27265):  8: elapsedTime =  8000000539
01-08 00:03:23.358: D/MainActivity(27265):  9: elapsedTime =  9000000154
01-08 00:03:23.730: D/MainActivity(27265): 10: elapsedTime = 10000003847

この動作を回避するためにコードを変更するにはどうすればよいですか?

4

1 に答える 1