我使用下面的代码作为简单Android游戏的游戏循环。它运作得很好,但是游戏中偶尔会出现一些打嗝。一切都冻结了一秒,然后向前跳了一点比它应该的更远。我在游戏循环中做了很多阅读,但是我仍然在实现平滑循环时遇到一些麻烦。内插会解决这个问题吗?我怎么能去平滑运动?
private final static int MAX_FPS = 30;
private final static int MAX_FRAME_SKIPS = 5;
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
protected Void doInBackground(Void... params) {
Log.d("MainTask", "starting game task");
Canvas canvas;
long begin; // the time when the cycle began
long delta; // the time it took for the cycle to execute
int sleep; // ms to sleep if ahead
int skipped; // number of frames being skipped
sleep = 0;
while (running) {
canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
begin = System.currentTimeMillis();
skipped = 0;
gamePanel.update();
gamePanel.draw(canvas);
delta = System.currentTimeMillis() - begin;
sleep = (int)(FRAME_PERIOD - delta);
if (sleep > 0) {
try {
Thread.sleep(sleep);
} catch(InterruptedException e) {}
}
while (sleep < 0 && skipped < MAX_FRAME_SKIPS) {
gamePanel.update();
sleep += FRAME_PERIOD;
skipped++;
}
}
} finally {
if (canvas != null) surfaceHolder.unlockCanvasAndPost(canvas);
}
}
Log.d("MainTask", "the task is complete");
return null;
}
答案 0 :(得分:3)
你实际上已经为这个循环做了大部分事情。然而,我可以看到几个潜在的陷阱
您没有将帧时间传递给更新方法。这意味着您的更新方法必须假定一定的帧时间。这可能是你跳跃的原因。如果您在机器的最高性能附近,您将无法获得恒定的帧速率,您不会。这大致无关紧要。如果你错过了一个框架,你的大脑就会填满丢失的框架,看起来很好。绝对重要的是变量对象速度。在这种情况下,可变帧速率将导致对象的可变速度。您的更新方法应为:
long previousFrameTime=System.currentTimeMillis();
while (running){
.....
.....
long currentTime=System.currentTimeMillis();
long frameTime=currentTime-previousFrameTime
previousFrameTime=currentTime
gamePanel.update(frameTime/1000.0); //I personally like my frametimes in seconds, hence /1000.0, but thats up to you
.....
.....
}
更新中的所有动作都应在确定移动距离时考虑frametime
要说明这一点,请参阅以下两个图表。一个具有假定的帧速率,另一个具有测量的帧速率。点代表实际渲染的帧,线代表观众脑中发生的插值。
你还可以坚持使用Canvas锁定比nessissary更长的时间。一直到你的更新方法和睡眠,你在画布上都有一个锁定。 This saps performance.在开始渲染之前获取锁定,并在完成后立即释放
gamePanel.update();
canvas = surfaceHolder.lockCanvas();
gamePanel.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
答案 1 :(得分:0)
这可能为时已晚,但是,这就是我实现游戏循环的方式。有了它,它可以帮助您跟踪游戏的滴答声(或更新)和每秒帧数。
long lastTime = System.nanoTime();
double nsPerTick = 1_000_000_000D/60D;
int frames = 0;
int ticks = 0;
long lastTimer = System.currentTimeMillis();
double delta = 0;
while (mRunning) {
Canvas canvas = null;
long now = System.nanoTime();
delta += (now-lastTime)/nsPerTick;
lastTime = now;
boolean shouldRender = false;
while (delta >= 1) {
ticks++;
gameView.update();
delta -= 1;
shouldRender = true;
}
if (shouldRender) {
frames++;
canvas = surfaceHolder.lockCanvas();
gameView.render(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
if (System.currentTimeMillis() - lastTimer >= 1000) {
lastTimer += 1000;
Log.d("FPS", "Ticks: " + ticks + ", Frames: " + frames);
ticks = 0;
frames = 0;
}
}
这是基于vanZeben的视频教程制作的,该视频教程旨在在PC上制作Java游戏,但它也可以在Android游戏上运行(至少对我来说如此)。
希望有帮助。
参考:https://www.youtube.com/watch?v=VE7ezYCTPe4&list=PL8CAB66181A502179