Android平滑的游戏循环

我在OpenGL平滑滚动方面遇到问题(在SGS2和ACE上testing)
我创build了简单的应用程序 – 只有固定速度的水平滚动图像,或者只有一个图像(玩家)通过加速器移动,但它的运动不平滑:-(
我尝试了很多不同的代码,但不满意…

首先我试着用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; } 

这是最好的,但它不像以前那样stream畅,有时是轻弹

第二我试图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)); } } 

这是不顺利的,这是没有问题的“后面跑步”

我的目标是像上面的第一个代码示例一样stream畅的图像移动。

可能的错误是其他地方,然后我看。 请,有人可以帮我吗?
更好地使用RENDERMODE_WHEN_DIRTY还是RENDERMODE_CONTINUOUSLY?

谢谢。

  • 具有透明背景的OpenGL场景+以下和以上的原生小部件
  • Android OpenGL ES 2.0屏幕坐标为世界坐标
  • Android - OpenGL ES 2.0:模拟器(工程) - 设备(不)
  • Android OpenGL ES 2.0模拟器
  • Android EGL / OpenGL ES帧率口吃
  • Android OpenGL ES 2.0屏幕坐标到世界坐标
  • 如何在OpenGL ES 2.0 Android中绘制基本圆
  • Android OpenGL演示“未选择配置”
  • 我在几天的时间里也遇到了同样的问题。 该循环看起来很平滑,没有任何Android时间参考,但只要包括任何types的“时间同步”,Android开发控制之外的外部因素就会给最终结果带来严重的不连续性。

    基本上这些因素是:

    • eglSwapInterval没有在Android中实现,所以很难知道硬件在屏幕上最终绘制的时刻(硬件屏幕同步)
    • Thread.sleep不精确。 线程可能会比请求更多或更less。
    • SystemClock.uptimeMillis()System.nanoTime(),System.currentTimeMillis()和其他与时间有关的测量不准确(其精确)。

    问题是独立于绘图技术(绘图,OpenGL 1.0 / 1.1和2.0)和游戏循环方法(固定时间步长,插值,可变时间步长)。 像你一样,我正在尝试Thread.sleep,疯狂的插值,定时器等等。不pipe你要做什么,我们都不能控制这个因素。

    根据本网站上的许多问答,制作平滑连续animation的基本规则是:

    • 通过删除所有的dynamic内存请求来降低GC的最低限度。
    • 渲染框架尽可能快的硬件可以处理它们(40至60fps在大多数的Android设备都可以)。
    • 使用固定的时间步长插值或可变的时间步长。
    • 优化更新物理和绘制例程以相对恒定的时间执行而没有高峰变化。

    当然,通过优化updateGame()和drawGame()(没有明显的GC和相对的持续执行时间),在发布这个问题之前,你做了很多以前的工作,以便在你的主循环中获得一个stream畅的animation。简单绝对平稳“。

    您的特殊情况下可变的stepTime和没有特别的要求是与实时事件(如音乐)完美同步,解决scheme很简单:“平滑步骤时间variables”。
    该解决scheme适用于其他游戏循环scheme(固定时间步长,可变渲染),并且易于移植概念(平滑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. 

    对于固定时间步长scheme,可以实施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); } 

    使用以下设备testingcanvas和openGlES10graphics:SG SII(57 FPS),SG Note(57 FPS),SG tab(60 FPS),在Windows XP(8 FPS)上运行的非品牌Android 2.3(43 FPS)慢速仿真器。 testing平台在真实物理参数(km / h和G's)中指定的path上移动45个对象+ 1个巨大背景(来自70MP源图像的纹理),在几个设备之间没有尖峰或轻拂(模拟器上8 FPS看起来不太好,但如预期那样以恒定速度stream动)

    检查android如何报告时间的图表。 有些时候,Android会报告一个很大的delta时间,只是下一个循环比平均值小,这意味着realTime值的偏移量。

    在这里输入图像说明

    更多细节: 在这里输入图像说明

    如何限制使用Android的GLSurfaceView.RENDERMODE_CONTINUOUSLY时的帧率?

    System.currentTimeMillis与System.nanoTime

    System.currentTimeMillis()方法是否真的返回当前时间?