2012-05-18 37 views
18


我有在OpenGL平滑滾動(測試上SGS2和ACE)
問題我創建簡單的應用程序 - 圖像的唯一固定速度橫向滾動,或僅一個圖像(播放器)由加速器移動, 但它的運動不順暢:-(
我嘗試了許多不同的代碼,但沒有滿意...的Android光滑遊戲循環

首先我想工作,GLSurfaceView.RENDERMODE_CONTINUOUSLY,我把所有的代碼onDrawFrame:

public void onDrawFrame(GL10 gl) 
    { 
     updateGame(gl, 0.017f); 
     drawGame(gl); 
    } 

這是最簡單和絕對平滑! - 但它是依賴於硬件速度(=沒用)


public void onDrawFrame(GL10 gl) 
    { 
     frameTime = SystemClock.elapsedRealtime();   
     elapsedTime = (frameTime - lastTime)/1000; 
     updateGame(gl, elapsedTime); 
     drawGame(gl); 
     lastTime = frameTime; 
    } 

,這是最重要的,但它並不像以前的,有時輕拂


第二我想GLSurfaceView.RENDERMODE_WHEN_DIRTY順利,在onDrawFrame我只有繪圖對象和此代碼在單獨的線程中:

while (true) 
    { 
    updateGame(renderer, 0.017f); 
    mGLSurfaceView.requestRender(); 
    next_game_tick += SKIP_TICKS; 
    sleep_time = next_game_tick - System.currentTimeMillis(); 
    if (sleep_time >= 0) 
    { 
     try 
     { 
      Thread.sleep(sleep_time); 
     } 
     catch (Exception e) {} 
     } 
     else 
     { 
     Log.d("running behind: ", String.valueOf(sleep_time)); 
     } 
    } 

這不光滑,它不是問題m與「後面跑步」


我的目標是像上面第一個代碼示例一樣的平滑圖像移動。

可能的錯誤是其他地方,然後我看。請,有人可以幫我嗎?
更好地使用RENDERMODE_WHEN_DIRTY或RENDERMODE_CONTINUOUSLY?

謝謝。

+1

'通過移動加速器' 加速還給你心驚肉跳的數據。這可能是你的問題嗎?試着用「someLoc + =(newLoc - someLoc)* .1f;」來平滑運動 – zezba9000

+0

你好,如果我嘗試禁用加速度計,只用簡單的移動圖像通過不斷的價值,它零星的輕彈(現在嘗試用RENDERMODE_WHEN_DIRTY) – Commanche

回答

32

我幾天來一直在爭着同一個問題。該循環看起來很平滑,沒有任何Android時間參考,但只要它包含任何類型的「時間同步」,android開發控制之外的外部因素就會對最終結果產生嚴重的不連續性。

基本上,這些因素是:

  • eglSwapInterval沒有在Android中實現的,所以很難知道的那一刻,當硬件暴露在屏幕(硬屏同步)最終戰平
  • 主題。睡眠不準確。線程可能會比請求更多或更少。 System.locktimeMillis()System.nanoTime(),System.currentTimeMillis()和其他與時間相關的測量不準確(其精確)。

該問題與繪圖技術(繪圖,openGL 1.0/1.1和2.0)和遊戲循環方法(固定時間步長,插值,可變時間步長)無關。 像你一樣,我正在嘗試Thread.sleep,瘋狂的插值,定時器等。無論你將要做什麼,我們都無法控制這些因素。

與許多Q &甲在此網站,基本規則,以產生平滑連續的動畫。根據是:

  • 通過去除所有動態存儲器請求降低在最小的GC。
  • 渲染幀的硬件可以快速處理它們(40至60fps在大多數Android設備中都可以)。
  • 使用具有插值或可變時間步長的固定時間步長。
  • 優化更新物理和繪製例程以相對恆定的時間執行而沒有高峯變化。

可以肯定,你在爲了獲得流暢的動畫在主優化updateGame()和drawGame()(沒有明顯的GC和相對恆定的執行時間)做了很多前期工作的職務之前,這個問題就像你提到的那樣循環:「簡單而絕對平滑」。

您的特殊情況下可變stepTime和沒有特別要求與實時事件(如音樂)完美同步,解決方案很簡單:「平滑步驟時間變量」。
解決方案的工作與其他遊戲環方案(固定時間步長具有可變渲染)並且易於端口的概念(光滑位移由updateGame以及跨越多個幀中的實時時鐘產生的量。)

// avoid GC in your threads. declare nonprimitive variables out of onDraw 
    float smoothedDeltaRealTime_ms=17.5f; // initial value, Optionally you can save the new computed value (will change with each hardware) in Preferences to optimize the first drawing frames 
    float movAverageDeltaTime_ms=smoothedDeltaRealTime_ms; // mov Average start with default value 
    long lastRealTimeMeasurement_ms; // temporal storage for last time measurement 

    // smooth constant elements to play with 
    static final float movAveragePeriod=40; // #frames involved in average calc (suggested values 5-100) 
    static final float smoothFactor=0.1f; // adjusting ratio (suggested values 0.01-0.5) 

    // sample with opengl. Works with canvas drawing: public void OnDraw(Canvas c) 
    public void onDrawFrame(GL10 gl){  
     updateGame(gl, smoothedDeltaRealTime_ms); // divide 1000 if your UpdateGame routine is waiting seconds instead mili-seconds. 
     drawGame(gl); 

     // Moving average calc 
     long currTimePick_ms=SystemClock.uptimeMillis(); 
     float realTimeElapsed_ms; 
     if (lastRealTimeMeasurement_ms>0){ 
     realTimeElapsed_ms=(currTimePick_ms - lastRealTimeMeasurement_ms); 
     } else { 
       realTimeElapsed_ms=smoothedDeltaRealTime_ms; // just the first time 
     } 
     movAverageDeltaTime_ms=(realTimeElapsed_ms + movAverageDeltaTime_ms*(movAveragePeriod-1))/movAveragePeriod; 

     // Calc a better aproximation for smooth stepTime 
     smoothedDeltaRealTime_ms=smoothedDeltaRealTime_ms +(movAverageDeltaTime_ms - smoothedDeltaRealTime_ms)* smoothFactor; 

     lastRealTimeMeasurement_ms=currTimePick_ms; 
    } 

    // Optional: check if the smoothedDeltaRealTIme_ms is too different from original and save it in Permanent preferences for further use. 

對於一個固定的時間步驟方案,intermetiate updateGame可以實現改善的結果:

float totalVirtualRealTime_ms=0; 
float speedAdjustments_ms=0; // to introduce a virtual Time for the animation (reduce or increase animation speed) 
float totalAnimationTime_ms=0; 
float fixedStepAnimation_ms=20; // 20ms for a 50FPS descriptive animation 
int currVirtualAnimationFrame=0; // useful if the updateGameFixedStep routine ask for a frame number 

private void updateGame(){ 
    totalVirtualRealTime_ms+=smoothedDeltaRealTime_ms + speedAdjustments_ms; 

    while (totalVirtualRealTime_ms> totalAnimationTime_ms){ 
     totalAnimationTime_ms+=fixedStepAnimation_ms; 
     currVirtualAnimationFrame++; 
     // original updateGame with fixed step     
     updateGameFixedStep(currVirtualAnimationFrame); 
    } 


    float interpolationRatio=(totalAnimationTime_ms-totalVirtualRealTime_ms)/fixedStepAnimation_ms; 
    Interpolation(interpolationRatio); 
} 

用帆布和openGlES10使用以下的設備繪製測試:SG SII(57 FPS),SG注(57 FPS ),SG標籤(60 FPS),非品牌Android 2.3(43 FPS)慢模擬器runni在Windows XP上(8 FPS)。測試平臺在真實物理參數(km/h和G's)中指定的路徑上繪製大約45個對象+ 1個巨大背景(來自70MP源圖像的紋理),而幾個設備之間沒有尖峯或輕拂(仿真器上8 FPS看起來不太好,但它按照預期以恆定速度流動)

檢查android報告時間的圖表。有些時候,Android會報告很大的增量時間,而下一個循環比平均值小,這意味着實時值讀數的偏移量。

enter image description here

更多的細節: enter image description here

How to limit framerate when using Android's GLSurfaceView.RENDERMODE_CONTINUOUSLY?

System.currentTimeMillis vs System.nanoTime

Does the method System.currentTimeMillis() really return the current time?

+0

非常感謝javgui,我會盡快測試這個,我會報告... – Commanche

+0

嗨, 所以,這個例子肯定有錯誤 - smoothTimeStep的值反覆增加,但如果我嘗試「smoothTimeStep = gameTimeElapsed * smoothFactor +(temp-gameTimeElapsed)」 - 它看起來不錯:-) 現在它是更好,但有時也輕彈(不是經常) - 我記錄了smoothTimeStep,這裏是它的值:0.021 0.021 0.021 ........ 0.06 0.095 0.03 0.008 0.007 0.005 0.021 0.021 也許這只是三星HW在SGS2上測試),我會嘗試另一臺設備。 – Commanche

+0

你說得對。在爲你手動簡化和調整我的代碼的過程中,我輸入了錯誤的公式。 (現在固定並在實際設備中測試)。我的情況需要與背景音樂同步(音頻和動畫同步運行)。我在沒有音樂實時需求的情況下編輯答案以更好地滿足您的要求。 (移動平均線)。試試這個代碼,如果你感到高興,我建議編輯一個更普遍的問題,如「Android平滑遊戲循環」的標題。問題(你的問題)和解決方案與繪製技術(canvas或openGL)無關。 – javqui