我正在制作一款游戏,但游戏循环的实现存在问题。我使用了SurfaceView并绘制2D精灵(位图)。目前,游戏中的玩家是一艘飞船,在陨石田中移动。飞船保持在屏幕中央,手机通过倾斜来移动陨石(陨石改变位置而不是玩家)。新的陨石会在旧的陨石从屏幕上消失后生成,营造出无尽的陨石场景感觉。
我在Nexus 5上运行游戏时发现,大约三秒后,沿屏幕下方移动的陨石变得不流畅,尽管我的游戏循环设置为以60fps运行。以下是代码:
我认为问题可能是我的模型在绘制到画布上,而我没有使用SurfaceView的onDraw()方法。重构后使用onDraw(),但结果与之前相同。我打印出每帧的休眠时间,我的线程始终睡眠5-10毫秒(一帧16.667毫秒),所以我的Nexus性能不足。
我在stackoverflow上读到,不应该在视图持有器上使用synchronize()。仍然没有运气。
然后,我再次在stackoverflow上阅读到,使用Thread.sleep()可能会引起问题,因为它并不总是准确的。我进行了如下重大改进。
这种新的方法应该尽可能快地使用增量时间来绘制下一帧,而不是像以前那样使用设置的增量。但仍然没有成功。
我将位图位置从矩形改为一个新的位置类,该类使用双精度浮点数代替整数模拟子像素移动。但仍然没有成功。
我尝试过以下几种方法:
出于好奇,我再次测试了我的第一个游戏循环(第一个代码块),并在玩游戏时点击屏幕,一切都运行得很顺畅。天哪...为了无用的复杂性增加了这么多工作量。我想这是一个很好的学习经验。有什么办法可以让我的SurfaceView保持全速运行吗?非常感谢你的帮助。 :)
我在Nexus 5上运行游戏时发现,大约三秒后,沿屏幕下方移动的陨石变得不流畅,尽管我的游戏循环设置为以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();
}
}
我认为问题可能是我的模型在绘制到画布上,而我没有使用SurfaceView的onDraw()方法。重构后使用onDraw(),但结果与之前相同。我打印出每帧的休眠时间,我的线程始终睡眠5-10毫秒(一帧16.667毫秒),所以我的Nexus性能不足。
我在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();
}
}
这种新的方法应该尽可能快地使用增量时间来绘制下一帧,而不是像以前那样使用设置的增量。但仍然没有成功。
我将位图位置从矩形改为一个新的位置类,该类使用双精度浮点数代替整数模拟子像素移动。但仍然没有成功。
我尝试过以下几种方法:
- 使用onDraw()而不是gameModel drawing到canvas
- 不要在view的holder上使用synchronize()
- 使用自上次更新以来的时间来更新下一帧,而不是每个update()调用都推进一帧。
- 去掉Thread.sleep()
- 子像素移动
出于好奇,我再次测试了我的第一个游戏循环(第一个代码块),并在玩游戏时点击屏幕,一切都运行得很顺畅。天哪...为了无用的复杂性增加了这么多工作量。我想这是一个很好的学习经验。有什么办法可以让我的SurfaceView保持全速运行吗?非常感谢你的帮助。 :)