如果没有触摸事件发生,Android SurfaceView会变慢

我正在制作一个游戏,除了游戏循环外,一切都很顺利。 我正在使用SurfaceView并绘制2D Sprites(位图)。 目前游戏是一艘穿越小行星场的船。 船停留在屏幕的中心,手机向任一方向倾斜以移动小行星(小行星改变位置而不是玩家)。 新的小行星产生的旧小行星从屏幕上掉下来,形成无限的小行星场感。

在我的Nexus 5上运行时,我注意到大约3秒后,尽管我的游戏循环设置为60fps,但小行星在屏幕上移动变得不稳定。 这是代码:

@Override public void run() { Canvas canvas; long startTime; long timeTaken; long sleepTime; while (running) { canvas = null; //Update and Draw try { startTime = System.nanoTime(); canvas = gameView.getHolder().lockCanvas(); //Update gameModel.update(gameController.getPlayerShots(), gameController.getCurrentTilt()); //Game Over? if (gameModel.isGameOver()) { running = false; } //Draw synchronized (gameView.getHolder()) { gameModel.drawToCanvas(canvas); } } finally { if (canvas != null) { gameView.getHolder().unlockCanvasAndPost(canvas); } } //Sleep if needed timeTaken = System.nanoTime() - startTime; sleepTime = FRAME_TIME - timeTaken; try { if (sleepTime > 0) { sleep(Math.abs((int)sleepTime/1000000), Math.abs((int)sleepTime % 1000000)); } } catch (InterruptedException e) {} } //Load menu after game over if (gameModel.isGameOver()) { gameController.launchContinueMenu(); } } 

我认为问题可能是我的模型正在绘制canvas并且我没有使用我的surfaceview的onDraw()方法。 重构使用onDraw()和以前相同的结果。 我打印出每帧的睡眠时间,我的线程一直在睡觉5-10毫秒(一帧16.667毫秒),所以我的关系并不缺乏计算能力。

我在stackoverflow上读到,我不应该在视图持有者上使用synchronize()。 仍然没有运气。

然后我再次阅读stackoverflow,使用Thread.sleep()可能会导致问题,因为它并不总是准确的。 我做了一个主要的重构,如下所示。

 @SuppressLint("WrongCall") @Override public void run() { Canvas canvas; while (running) { canvas = null; //Update and Draw try { canvas = gameView.getHolder().lockCanvas(); //Calc and smooth time long currentTimeMS = SystemClock.uptimeMillis(); double currentDeltaTimeMS; if (lastTimeMS > 0) { currentDeltaTimeMS = (currentTimeMS - lastTimeMS); } else { currentDeltaTimeMS = smoothedDeltaTimeMS; // just the first time } avgDeltaTimeMS = (currentDeltaTimeMS + avgDeltaTimeMS * (avgPeriod - 1)) / avgPeriod; // Calc a better aproximation for smooth stepTime smoothedDeltaTimeMS = smoothedDeltaTimeMS + (avgDeltaTimeMS - smoothedDeltaTimeMS) * smoothFactor; lastTimeMS = currentTimeMS; //Update gameModel.update(smoothedDeltaTimeMS / 1000.0d, gameController.getCurrentTilt()); //Game Over? if (gameModel.isGameOver()) { running = false; } //Draw // synchronized (gameView.getHolder()) { // gameModel.drawToCanvas(canvas); gameView.onDraw(canvas); // } } //Release canvas finally { if (canvas != null) { gameView.getHolder().unlockCanvasAndPost(canvas); } } } //Load menu after game over if (gameModel.isGameOver()) { gameController.launchContinueMenu(); } } 

这种新方法应尽可能快地使用增量时间来绘制下一帧而不是像以前那样绘制设定增量。 仍然没有运气。

我将位图位置从Rect更改为我创建的新位置类,它使用双精度而不是整数来模拟子像素运动。 仍然没有运气。

回顾一下我尝试过的事情:

  1. 使用onDraw()而不是gameModel绘制到canvas
  2. 不要在视图的持有者上使用synchronize()
  3. 使用自上次更新后的时间更新下一帧而不是每次update()调用前进一帧。
  4. 删除Thread.sleep()
  5. 亚像素运动

毕竟,我注意到手机充电时游戏运行正常。 拔掉手机3秒后,游戏再次变得不稳定。 然后我注意到,如果我在游戏变得波涛汹涌时点击屏幕,一切都会再次平滑3秒钟。 我猜是有某种节电function影响我的SurfaceView性能? 这里发生了什么,我怎么能禁用它? 这太让人抓狂了……

我出于好奇心(第一个代码块)再次测试了我的第一个游戏循环,并在播放时点击了屏幕,一切都顺利进行。 男人……所有这些都会增加复杂性。 我想这是一个很好的学习经历。 有关如何让SurfaceView全速运行的任何想法? 非常感谢帮助。

🙂

你的观察是正确的。 简而言之,N5的电源管理积极地限制了电源。 如果您的手指与屏幕接触,则会增加时钟速度以确保交互顺畅。 (有关更多信息,请参阅我对此问题的回答 。)

处理此问题的最佳方法是识别您有时需要丢帧。 Grafika中的“记录GL应用程序”活动使用Choreographer的简单技巧来执行此操作。 有关游戏循环的其他一些想法,请参阅本文 。

我认为你应该尝试让你的游戏更节省电池,使游戏更暗(使用背景布局#000)更多的游戏function,你也可以使用清除顶部的 Intent标志来杀死以前的活动以节省电池,清除ram(避免出局)记忆)

但对于你的情况,你可以使用唤醒锁