2014-07-03 164 views
2

我將以下代碼用作簡單Android遊戲的遊戲循環。它工作得很好,但是偶爾會出現一場小小的呃逆。一切都凍結了一秒鐘,然後向前跳躍了一點,比它應該更遠。我在遊戲循環中進行了大量的閱讀,但是我仍然在實現平滑循環時遇到了一些問題。插值解決這個問題?我該如何去平滑運動?平滑Android遊戲循環

private final static int MAX_FPS = 30; 
private final static int MAX_FRAME_SKIPS = 5; 
private final static int FRAME_PERIOD = 1000/MAX_FPS; 

protected Void doInBackground(Void... params) { 
    Log.d("MainTask", "starting game task"); 
    Canvas canvas; 

    long begin;  // the time when the cycle began 
    long delta;  // the time it took for the cycle to execute 
    int sleep;  // ms to sleep if ahead 
    int skipped; // number of frames being skipped 

    sleep = 0; 

    while (running) { 
     canvas = null; 
     try { 
      canvas = surfaceHolder.lockCanvas(); 
      synchronized (surfaceHolder) { 
       begin = System.currentTimeMillis(); 
       skipped = 0; 

       gamePanel.update(); 
       gamePanel.draw(canvas); 

       delta = System.currentTimeMillis() - begin; 
       sleep = (int)(FRAME_PERIOD - delta); 

       if (sleep > 0) { 
        try { 
         Thread.sleep(sleep); 
        } catch(InterruptedException e) {} 
       } 

       while (sleep < 0 && skipped < MAX_FRAME_SKIPS) { 
        gamePanel.update(); 
        sleep += FRAME_PERIOD; 
        skipped++; 
       } 
      } 
     } finally { 
      if (canvas != null) surfaceHolder.unlockCanvasAndPost(canvas); 
     } 
    } 
    Log.d("MainTask", "the task is complete"); 
    return null; 
} 
+0

參見:https://source.android.com/devices/graphics/architecture.html#loops – fadden

回答

3

您實際上已經爲此循環正確完成了大部分工作。有幾個潛在的坑下降,但我可以看到

  • 您不會將幀時間傳遞給您的更新方法。這意味着您的更新方法必須假定一定的幀時間。這可能是你跳躍的原因。如果您在任何地方接近機器的最高性能,您就不會獲得恆定的幀速率,您只是不會。這大致沒有關係。如果你錯過了一個框架,你的大腦填補了缺失的框架,它看起來很好。什麼是絕對重要的是一個變量對象速度。在這種情況下,可變的幀速率會導致對象的速度變化。您的更新方法應該是:

    long previousFrameTime=System.currentTimeMillis(); 
    while (running){ 
        ..... 
        ..... 
        long currentTime=System.currentTimeMillis(); 
        long frameTime=currentTime-previousFrameTime 
        previousFrameTime=currentTime 
        gamePanel.update(frameTime/1000.0); //I personally like my frametimes in seconds, hence /1000.0, but thats up to you 
        ..... 
        ..... 
    } 
    

    而且更新中的所有運動應該採取frametime考慮確定多遠移動

爲了證明這一點的問題是如何,請參閱下面的兩個圖時。一個具有假定的幀速率,另一個具有測量的幀速率。點表示實際的渲染幀,線表示在觀衆大腦中發生的插值。

enter image description here

enter image description here

  • 還持有到畫布上鎖定了很多比nessissary更長。一路通過你的更新方法,並通過睡眠,你有一個鎖在畫布上。 This saps performance.獲取你開始渲染之前鎖定並儘快釋放它,你已經完成

    gamePanel.update(); 
    canvas = surfaceHolder.lockCanvas(); 
    gamePanel.draw(canvas); 
    surfaceHolder.unlockCanvasAndPost(canvas); 
    
+2

即使如果抓住畫布鎖定不會導致呃逆,那絕對值得修復 – zgc7009

+0

Canvas鎖定部分是合理的。通過frameTime,你是指我的三角洲?我將如何使用它?我是否需要重新計算內部while循環?或者繼續使用上面找到的三角洲? –

+0

@ user2694189您的增量似乎是幀的處理時間(忽略任何睡眠) - 這通常不是delta值,通常delta是幀時間。你想記錄剛剛更新之前的時間,然後在更新之前的下一幀再次獲得時間,並且你會得到兩者之間的差異。這是frameTime。內部的更新方法一切都會有一個速度;您將frameTime乘以速度以獲得該幀應該移動的距離 –