2012-08-04 146 views
0

我有一個Android遊戲的開始。它有一個GameLoop(線程)類,它跟蹤一個fps值和一個GameView(surfaceview),它將一個對象設置爲一個對象並將其設置爲等等。 現在我有一個單獨的FpsText對象,它知道如何將自己繪製到屏幕上,但更新,我需要做的是這樣fpsText.setText(gameloop.getFps());有沒有辦法將線程安全地作爲參數傳遞?

,如果我在GameView類做這個心不是一個問題,但我想嘗試,並有一個FpsText對象,可以照顧自己更新。我沒有任何其他線程經驗,我認爲這可能適得其反,但我沒有嘗試通過fpsTextgameloop參考作爲參數。不出所料,我每次運行應用程序都會得到不同的不需要的結果,所以我想知道您認爲處理這種情況的最佳方法是什麼?

作爲GameView中更新fpsText的替代方法,我總是可以在gameloop類中公開fps值,但我知道這是不好的做法!既不是好的解決方案,也許有更好的方法?

僅供參考,這裏是我gameloop,由RedOrav建議的第二個變化來實現:

package biz.hireholly.engine; 

import android.graphics.Canvas; 
import android.util.Log; 
import android.view.SurfaceHolder; 
import biz.hireholly.gameplay.FpsText; 

import java.util.ArrayList; 


/** 
* The GameLoop is a thread that will ensure updating and drawing is done at set intervals. 
* The thread will sleep when it has updated/rendered quicker than needed to reach the desired fps. 
* The loop is designed to skip drawing if the update/draw cycle is taking to long, up to a MAX_FRAME_SKIPS. 
* The Canvas object is created and managed to some extent in the game loop, 
* this is so that we can prevent multiple objects trying to draw to it simultaneously. 
* Note that the gameloop has a reference to the gameview and vice versa. 
*/ 

public class GameLoop extends Thread { 

    private static final String TAG = GameLoop.class.getSimpleName(); 
    //desired frames per second 
    private final static int MAX_FPS = 30; 
    //maximum number of drawn frames to be skipped if drawing took too long last cycle 
    private final static int MAX_FRAME_SKIPS = 5; 
    //ideal time taken to update & draw 
    private final static int CYCLE_PERIOD = 1000/MAX_FPS; 

    private SurfaceHolder surfaceHolder; 
    //the gameview actually handles inputs and draws to the surface 
    private GameView gameview; 

    private boolean running; 
    private long beginTime = 0; // time when cycle began 
    private long timeDifference = 0; // time it took for the cycle to execute 
    private int sleepTime = 0; // milliseconds to sleep (<0 if drawing behind schedule) 
    private int framesSkipped = 0; // number of render frames skipped 

    private double lastFps = 0; //The last FPS tracked, the number displayed onscreen 
    private ArrayList<Double> fpsStore = new ArrayList<Double>(); //For the previous fps values 
    private long lastTimeFpsCalculated = System.currentTimeMillis(); //used in trackFps 
    FpsText fpsText; 

    public GameLoop(SurfaceHolder surfaceHolder, GameView gameview, FpsText fpsText) { 
     super(); 
     this.surfaceHolder = surfaceHolder; 
     this.gameview = gameview; 
     this.fpsText = fpsText; 
    } 

    public void setRunning(boolean running) { 
     this.running = running;  
    } 
    @Override 
    public void run(){ 

     Canvas c; 

     while (running) { 
      c = null; 
      //try locking canvas, so only we can edit pixels on surface 
      try{ 
       c = this.surfaceHolder.lockCanvas(); 
       //sync so nothing else can modify while were using it 
       synchronized (surfaceHolder){ 

        beginTime = System.currentTimeMillis(); 
        framesSkipped = 0; //reset frame skips 

        this.gameview.update(); 
        this.gameview.draw(c); 

        //calculate how long cycle took 
        timeDifference = System.currentTimeMillis() - beginTime; 
        //good time to trackFps? 
        trackFps(); 

        //calculate potential sleep time 
        sleepTime = (int)(CYCLE_PERIOD - timeDifference); 

        //sleep for remaining cycle 
        if (sleepTime >0){ 
         try{ 
          Thread.sleep(sleepTime); //saves battery! :) 
         } catch (InterruptedException e){} 
        } 
        //if sleepTime negative then we're running behind 
        while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS){ 
         //update without rendering to catch up 
         this.gameview.update(); 
         //skip as many frame renders as needed to get back into 
         //positive sleepTime and continue as normal 
         sleepTime += CYCLE_PERIOD; 
         framesSkipped++; 
        } 

       } 

      } finally{ 
       //finally executes regardless of exception, 
       //so surface is not left in an inconsistent state 
       if (c != null){ 
        surfaceHolder.unlockCanvasAndPost(c); 
       } 
      } 
     } 

    } 

    /* Calculates the average fps every second */ 
    private void trackFps(){ 
     synchronized(fpsStore){ 
      long currentTime = System.currentTimeMillis(); 

      if(timeDifference != 0){ 
       fpsStore.add((double)(1000/timeDifference)); 
      } 
      //If a second has past since last time average was calculated, 
      // it's time to calculate a new average fps to display 
      if ((currentTime - 1000) > lastTimeFpsCalculated){ 


       for (Double fps : fpsStore){ 
       lastFps += fps; 
       } 

       lastFps /= fpsStore.size(); 

       synchronized(fpsText){ //update the Drawable FpsText object 
       fpsText.setText(String.valueOf((int)lastFps)); 
       } 

       lastTimeFpsCalculated = System.currentTimeMillis(); 

       //Log.d("trackFPS()", " fpsStore.size() = "+fpsStore.size()+"\t"+fpsStore.toString()); 
       fpsStore.clear(); 


      } 
     } 
    } 

} 

回答

1

什麼是你不想要的結果?讓你的fps變量公開並設置一個getfps()就獲得你的fps值而言是完全一樣的,修改GameView中的fps值是沒有任何意義的。

我懷疑你在做什麼GameLoop是修改你的fps變量之間的秒,即獲得中間值,您的GameView也可能讀取。我建議每秒只修改一次fps,並在時間間隔中進行計算。假設你在開始時有0 fps。 GameLoop進行計算,每秒一次,你已經收集了它處理的幀數和時間(1秒)。在此之前,無論GameLoop在做什麼,GameView總是讀取0。在那之後,假設您完成了60 fps,fps變量將被修改,並且在下一秒之前保持不變。因此,對於第二個以下,即使GameView可以做更多的讀取您的變量,它總是會得到60

// Inside GameLoop 

private int fps; // Variable you're going to read 
private int frameCount; // Keeps track or frames processed 
private long lastUpdated; 

while(running) { 

    if(System.currentTimeMillis() - lastUpdated > 1000) { // 1 second elapsed 
     fps = frameCount; 
     frameCount = 0; 
    } 
    frameCount++; 
} 

public int getFps() { 
    return fps; 
} 

做它傳遞給fpsText一個參考GameLoop的另一種方式,這將修改它只是在每秒過後。這樣,你的渲染線程只需要擔心渲染,而你的GameLoop關於每秒幀數。但請記住,您可能會遇到競爭條件(您的渲染方法中間的GameLoop中setText()的位置),您不必擔心其他方式。在渲染時以及在修改它時,將fpsText對象包含在同步括號中應該爲您解決問題。

讓我們知道是否有幫助!

+0

非常感謝您的回覆!我意識到現在我應該包括我的源代碼,因爲我實際上認爲我以某種方式實現了我的循環,而fps只是更新了第二個,如果你想檢查它,我不完全是上面的源代碼,因爲我並不完全確保它的正確性是誠實的:/這個邏輯對我來說是有意義的,但是在屏幕上我得到800-1000之間的fps!所以即時擔心它不應該睡覺時,應該。無論如何,我實現了你建議的第二個解決方案,並且應用程序再次正常運行,所以你回答了我的問題:)謝謝! – Holly 2012-08-04 12:27:52

相關問題