2012-05-18 87 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