【问题标题】:Frame-rate not constant android game帧率不恒定的安卓游戏
【发布时间】:2014-02-24 06:05:09
【问题描述】:

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

要在这项任务中获得最高速率,必须制作帧速率必须恒定的游戏。因为我获得了最高学位,而且老师对我的源代码非常满意,所以我认为没有错误修复。但是 - 问题是帧速率不是恒定的。更奇怪的是:在屏幕上移动的图形对象 - 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
    }
}   

}

提前致谢

【问题讨论】:

  • 我不确定为什么对象移动缓慢,仍在考虑那个...但是提示-您的更新方法应该接受增量时间。这样,您的对象可以在考虑时间的情况下自行更新。

标签: android frame-rate


【解决方案1】:

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

问题是您的while (sleepTime &lt; 0 &amp;&amp; framesSkipped &lt; MAX_FRAME_SKIPS) 追赶循环没有考虑到完成追赶模型时剩余的时间。

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

同时,在睡眠时间始终为正的快速设备上,游戏将始终显示正确。

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


但除了上述简化的情况(忽略 Vsync)之外,睡眠时间永远不会被操作系统完全应用——它每次都会变化正负几毫秒。

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

如果您有一个固定的增量时间(对于具有物理特性的游戏,或者您的游戏已经使用了固定的增量时间),this site 会清楚地说明如何实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-16
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多