2012-08-15 29 views
1

我正在寫一個基於LunarLander示例項目的小程序。我想實現的是固定間隔之間更新我SurfaceView(重新定位一些Drawable旗下主要)像這樣的:如何在Android中更新固定間隔的SurfaceView?

private static final int UPDATE_INTERVAL = 1000/20; // 20 FPS 

我運行一個Thread這是在其run()方法這樣做:

@Override 
    public void run() { 
     while (isRunning) { 
      Canvas c = null; 
      try { 
       c = view.getSurfaceHolder().lockCanvas(null); 
       synchronized (view.getSurfaceHolder()) { 
        if (GameState.RUNNING.equals(state)) { 
         long now = System.currentTimeMillis(); 
         if (now - lastUpdate > UPDATE_INTERVAL) { 
          lastUpdate = now; 
          updater.updatePieces(); 
         } 
         doDraw(c); 
        } 
       } 
      } finally { 
       if (c != null) { 
        view.getSurfaceHolder().unlockCanvasAndPost(c); 
       } 
      } 
     } 
    } 

我的問題是,有可能這Thread正忙於等待(now - lastUpdate > UPDATE_INTERVAL)條件爲true。但是如果重繪需要超過1000/20秒,還有另外一種情況。這意味着動畫並不總是無縫的。我在我的模擬器中試過了,它通常是正常的,但有時我的Drawable正在放慢速度,然後加速大約半秒鐘。這只是模擬器還是其他的東西?

我不會打擾你的細節,updatePieces()只是從上到下移動一個小圓圈。我沒有創建任何新的對象。我總覺得這段代碼不對。這是一個很好的練習動畫Drawable或我失去了明顯的東西?我在這個話題上有點新鮮。

我將觸摸和按鍵事件從UI線程傳播到這個,所以我不想在Thread中調用sleep(),因爲它可能會導致UI線程無響應。

回答

1

想想物體在現實中的反應。一個物體移動一定的距離。它在一定時間內移動的距離取決於速度。速度取決於最後的加速度。距離,速度和加速度都取決於時間。

在你的代碼中,你根本不處理時間,所有的,因此沒有真正的世界解釋你當前的代碼。放在一起你的物理是這樣的:

  1. 實施一個三角洲系統,如歐拉集成。這個系統應該包含關於繪製前一幀需要多長時間的信息。 注意:當加速度恆定時(如重力),而不是加速度改變時,歐拉積分是準確的。但這更像是一個優化主題,現在不考慮這個問題。
  2. 當你知道前一幀花了多長時間,你也知道你的物體完全沒有移動的時間。
  3. 請考慮下一次更新的時間。

例如:

public void updatePieces(float timeDelta) { 
    // The amount of an acceleration depends on a force 
    // in a specified direction. You can skip this, and just 
    // give your object a velocity. But to consider acceleration 
    // later on, you have to assign forces and resolve them in 
    // their respective direction. 
    mAccelerationX = fResX/weight;    
    mAccelerationY = fResY/weight; 

    // Consider the previous velocity and add the change 
    // in velocity. 
    mVelocityX = mVelocityX + (mAccelerationX * timeDelta); 
    mVelocityY = mVelocityY + (mAccelerationY * timeDelta); 

    mPositionX = mPositionX + (mVelocityX * timeDelta); 
    mPositionY = mPositionY + (mVelocityY * timeDelta); 
} 

稍微瞭解物理學有助於理解爲什麼這是有道理的:

mAccelerationX = fResX/weight; 

基本上是Newton's second lawF = ma

mVelocityX = mVelocityX + (mAccelerationX * timeDelta); 

作品,因爲我們正在做的是按時間s乘以加速度m/s^2。這會給你自上一幀以來速度改變了多少。簡單的取消給了我們(m/s^2)*s = m*s/s^2 = m/s。添加以前的速度和完成。

同樣的邏輯也適用於:

mPositionX = mPositionX + (mVelocityX * timeDelta); 
// (m/s)*s = m*s/s = m 

注意,你可能需要添加的比例因子,以使物理現實到你的屏幕與繁衍 - 例如,有多少像素等於五計,或類似的東西。

有大量的文章描述了這一點,例如Glenn Fiedler的Integration Basics。搜索,你會發現更多。

+0

這是有道理的。無論如何,只要不降低太多,我並不關心幀速率。我現在只有速度,沒有加速度(所以它是1)。 – 2012-08-15 12:15:25

+0

感謝您的詳細解釋,+1! – 2012-08-15 12:17:11

0

對於任何有其他工作要做的系統,根據時間做任何事情都不是一個好習慣:您的代碼儘可能多地佔用CPU時間。你編碼的東西叫做「繁忙循環」。這意味着當你沒有工作要做時,你仍然很忙。

更好的選擇包括使用某種類型的定時器/鬧鐘(java定時器,Android AlarmManager)來調度工作完成時間,或調用Thread.sleep()延遲到下一個更新時間間隔。

+0

我正在考慮Thread.sleep(),但據我所知它並不能保證它會等待指定的時間。正如我在我的問題中所說的,我意識到了繁忙的等待問題。 – 2012-08-15 11:15:23

+0

你是對的,但不能保證。如果您需要實時系統,請徹底擺脫Android並改用實時系統。您的忙碌循環也不能保證實時響應(當您需要CPU時,系統可能忙於其他線程,並且由於您消耗盡可能多的CPU,因此您甚至可能更容易成爲案件)。 – mah 2012-08-15 11:17:39

+0

所以,如果我'等待()'一個'計時器'我可以接近OK? – 2012-08-15 11:19:48

相關問題