帧速率不是常数安卓游戏

时间:2014-01-31 13:57:54

标签: android frame-rate

去年春天,我在大学学习Android,最后的任务是开发游戏。我不会提供有关游戏本身的任何细节,但我使用了一个将SurfaceView扩展为游戏面板的类,并将游戏循环放在后台线程中。这场比赛需要一个月的努力才能完成,但结果非常好 - 我在Android市场推出它并且现在拥有越来越多的下载量。但是有一个相当大的缺点,它遵循:

要获得此作业中的最高费率,必须制作一个帧率必须保持不变的游戏。因为我获得了最高学位,老师对我的源代码非常满意,我认为没有bugfix。但是 - 问题是帧速率不是恒定的。而且更奇怪的是:在屏幕上移动的图形对象 - CPU运行得越快就越慢。

我有三款不同型号的手机,显示三种不同的速度:

  • SAMSUNG GALAXY MINI(处理器时钟速度1 GHz)此处对象快速移动
  • SAMSUNG GALAXY S3(处理器时钟速度1.8 GHz)此处物体以中速移动
  • SAMSUNG GALAXY S4(处理器时钟速度2.3 GHz)此处对象移动缓慢

如此简单 - 智能手机在处理器环境中越强大,图形对象在屏幕上移动的速度就越慢 - 人们可能会反过来说 - 对于速度更快的智能手机来说帧率更高。

我在下面提供了背景线程,它包含了游戏循环 - 我无法看到我在这段代码中做错了什么 - 我的老师也没看错

public class MainThread extends Thread {

  private static final String TAG = MainThread.class.getSimpleName();

  private final static int  MAX_FPS = 50;   // Antal frames per sekund.
  private final static int  MAX_FRAME_SKIPS = 5;    // Antal förlorade frames.
  private final static int  FRAME_PERIOD = 1000 / MAX_FPS;

  private SurfaceHolder surfaceHolder;  
  private GamePanel gamePanel;

  private boolean running;
  public void setRunning(boolean running) {
    this.running = running;
 }

 public MainThread(SurfaceHolder surfaceHolder, GamePanel gamePanel) {
    super();
    this.surfaceHolder = surfaceHolder;
    this.gamePanel = gamePanel;
}

@Override
public void run() {
    Canvas canvas;
    Log.d(TAG, "Starting game loop");

    long beginTime;     // Cykelns starttid
    long timeDiff;      // Tiden det tar för en cykel
    int sleepTime;      // Cykel (update + render = timediff) + sleeptime => frameperiod. 
    int framesSkipped;  // Antalet förlorade frames. 

    sleepTime = 0;
    while (running) {
        canvas = null;
        try {
            canvas = this.surfaceHolder.lockCanvas();
            synchronized (surfaceHolder) {
                beginTime = System.currentTimeMillis();
                framesSkipped = 0;  

                try {
                    this.gamePanel.update_graphics();
                    this.gamePanel.render_graphics(canvas);             
                }
                catch (Exception e) {
                    System.out.println("fel uppstod i bakgrundstråden: " + e);
                }
                timeDiff = System.currentTimeMillis() - beginTime;
                sleepTime = (int)(FRAME_PERIOD - timeDiff);
                //System.out.println("sleeptime: " + sleepTime);
                if (sleepTime > 0) {
                    // Sleeptime ska vara positiv, med marginal. (Annars risk för hackig animation)
                    try {
                        Thread.sleep(sleepTime);    // Tråden sover en stund.
                    } catch (InterruptedException e) {}
                }

                while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                    // Används för att "komma igen/komma ikapp".
                    try {
                        this.gamePanel.update_graphics(); // endast updatering.
                    }
                    catch (Exception e) {
                        System.out.println("fel uppstod i bakgrundstråden: " + e);
                    }
                    sleepTime += FRAME_PERIOD;
                    framesSkipped++;
                }

                if (framesSkipped > 0) {
                    //Log.d(TAG, "Skipped:" + framesSkipped);
                }

            }
        } finally {

            if (canvas != null) {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }   // slut finally
    }
}   

}

提前致谢

1 个答案:

答案 0 :(得分:1)

我假设您的update_graphics方法基于FRAME_PERIOD的固定增量时间。

问题是你的while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)追赶循环没有考虑完成追赶模型后剩余的时间。

例如: 您的FRAME_PERIOD目前是20毫秒。假设Galaxy Mini运行update_graphics需要2ms,运行render_graphics需要19ms。现在它将使用sleepTime=-1ms进入追赶循环,因此它将运行update_graphics一次。然后在下一次while循环继续时,它会在try块中再次调用update_graphicsrender_graphics。当render_graphics完成第二次绘制时,自上次完成以来已经过了23毫秒,但update_graphics已被调用两次。因此,似乎40ms的游戏时间已经过去了23ms,所以你的游戏似乎运行了40/23或预期速度的173%。

同时,在睡眠时间总是正面的快速设备上,游戏总是显得正确。

另请注意,Galaxy Mini和Galaxy S3之间的速度差异可能会向另一个方向发展(S3显得更快),具体取决于更新和渲染方法所需的相对时间以及它们与{{1}的比率}。


除了上面简化的情况(忽略了Vsync)之外,睡眠时间永远不会被操作系统完全应用 - 每次都会加上或减去几毫秒。

对于没有物理模拟的简单游戏,在更新方法中使用变量增量时间是一种更简单的解决方案。

如果你有一个固定的增量时间(你可能需要使用物理游戏,或者你的游戏已经使用固定增量时间),this site会清楚地说明如何实现它。 / p>