平滑Android游戏循环

时间:2014-07-03 19:11:26

标签: java android multithreading game-loop

我使用下面的代码作为简单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;
}

2 个答案:

答案 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

要说明这一点,请参阅以下两个图表。一个具有假定的帧速率,另一个具有测量的帧速率。点代表实际渲染的帧,线代表观众脑中发生的插值。

enter image description here

enter image description here

  • 你还可以坚持使用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