2014-01-31 100 views
0

去年春天在大學學習Android,最後一項任務是開發一款遊戲。我不會給出關於遊戲本身的任何細節,但我使用了一個將SurfaceView作爲遊戲面板並將遊戲循環放入後臺線程的類。遊戲花了一個月的時間完成,但結果非常好 - 非常好,我在Android市場上推出了它,現在有更多的下載。但是有一個相當大的缺點,它在這裏:幀速率不恆定android遊戲

爲了在這個任務中獲得最高的速度,必須製作一個遊戲,幀頻必須是恆定的。而且因爲我獲得了最高學位,老師對我的源代碼非常滿意,所以我認爲沒有錯誤修正。但是 - 事情是幀速率不是恆定的。更奇怪的是:在屏幕上移動的圖形對象 - 移動速度越慢,CPU速度越快。

我有三部手機不同的型號,顯示三種不同的速度:

  • SAMSUNG GALAXY MINI(處理器時鐘速度1 GHz)這裏的物體快速移動
  • 三星GALAXY S3(處理器主頻1.8 GHz的)在此,對象移動以中等速度
  • SAMSUNG GALAXY S4(處理器時鐘速度2.3千兆赫)在這裏,對象移動緩慢

所以簡要 - 更強大噸智能手機處於處理器環境中時,圖形對象在屏幕上移動的速度越慢 - 人們可能會想到相反的情況 - 對於速度更快的智能手機,幀速率更高。

我提出下面的backgroundthread持有gameloop - 我看不出我做錯了什麼在此代碼 - 也不是我的老師

public class MainThread extends Thread { 

    private static final String TAG = MainThread.class.getSimpleName(); 

    private final static int MAX_FPS = 50; // Antal frames per sekund. 
    private final static int MAX_FRAME_SKIPS = 5; // Antal förlorade frames. 
    private final static int FRAME_PERIOD = 1000/MAX_FPS; 

    private SurfaceHolder surfaceHolder; 
    private GamePanel gamePanel; 

    private boolean running; 
    public void setRunning(boolean running) { 
    this.running = running; 
} 

public MainThread(SurfaceHolder surfaceHolder, GamePanel gamePanel) { 
    super(); 
    this.surfaceHolder = surfaceHolder; 
    this.gamePanel = gamePanel; 
} 

@Override 
public void run() { 
    Canvas canvas; 
    Log.d(TAG, "Starting game loop"); 

    long beginTime;  // Cykelns starttid 
    long timeDiff;  // Tiden det tar för en cykel 
    int sleepTime;  // Cykel (update + render = timediff) + sleeptime => frameperiod. 
    int framesSkipped; // Antalet förlorade frames. 

    sleepTime = 0; 
    while (running) { 
     canvas = null; 
     try { 
      canvas = this.surfaceHolder.lockCanvas(); 
      synchronized (surfaceHolder) { 
       beginTime = System.currentTimeMillis(); 
       framesSkipped = 0; 

       try { 
        this.gamePanel.update_graphics(); 
        this.gamePanel.render_graphics(canvas);    
       } 
       catch (Exception e) { 
        System.out.println("fel uppstod i bakgrundstråden: " + e); 
       } 
       timeDiff = System.currentTimeMillis() - beginTime; 
       sleepTime = (int)(FRAME_PERIOD - timeDiff); 
       //System.out.println("sleeptime: " + sleepTime); 
       if (sleepTime > 0) { 
        // Sleeptime ska vara positiv, med marginal. (Annars risk för hackig animation) 
        try { 
         Thread.sleep(sleepTime); // Tråden sover en stund. 
        } catch (InterruptedException e) {} 
       } 

       while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) { 
        // Används för att "komma igen/komma ikapp". 
        try { 
         this.gamePanel.update_graphics(); // endast updatering. 
        } 
        catch (Exception e) { 
         System.out.println("fel uppstod i bakgrundstråden: " + e); 
        } 
        sleepTime += FRAME_PERIOD; 
        framesSkipped++; 
       } 

       if (framesSkipped > 0) { 
        //Log.d(TAG, "Skipped:" + framesSkipped); 
       } 

      } 
     } finally { 

      if (canvas != null) { 
       surfaceHolder.unlockCanvasAndPost(canvas); 
      } 
     } // slut finally 
    } 
} 

}

在此先感謝

+0

我不確定物體爲什麼緩慢移動,仍然在想那個......但是有一個提示 - 你的更新方法應該接受增量時間。這樣,您的對象可以考慮更新時間。 –

+0

也許這可以幫助你http://gafferongames.com/game-physics/fix-your-timestep/ – Rad1

回答

1

我假設您的update_graphics方法基於FRAME_PERIOD的固定增量時間。

問題是你的while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)追趕循環沒有考慮到追趕模型時遺留下來的時間。

例如: 您的FRAME_PERIOD當前是20ms。假設Galaxy Mini需要2ms運行update_graphics,19ms運行render_graphics。現在它將進入追趕循環與sleepTime=-1ms,所以它會運行update_graphics一次。然後在下一次while循環繼續時,它會在您的try塊中再次調用update_graphicsrender_graphics。到render_graphics完成第二次繪製時,距上次完成時間爲23毫秒,但update_graphics已被調用兩次。因此,看起來在23ms的時間內已經過去了40ms的遊戲時間,所以你的遊戲似乎正在運行40/23或173%的預期速度。

同時,在睡眠時間總是正值的快速設備上,遊戲始終顯示正確。

另外需要注意的是,Galaxy Mini和Galaxy S3之間的速度差異可能已經在另一個方向上(S3出現得更快),具體取決於更新和渲染方法的相對時間以及它們與FRAME_PERIOD的比率。


但是,除了上述簡化局面(這忽略VSYNC),睡眠時間是永遠不會被操作系統正是應用 - 它每次都會變化加上或減去幾毫秒。

對於一個沒有物理模擬的簡單遊戲,在更新方法中使用可變增量時間是一個更簡單的解決方案。

如果你有一個固定的增量時間(你可能需要一個物理遊戲,或者你的遊戲已經使用固定增量時間),this site提供了一個清楚的解釋如何實現它。