2014-01-31 141 views
0

去年春天在大学学习Android,最后一项任务是开发一款游戏。我不会给出关于游戏本身的任何细节,但我使用了一个将SurfaceView作为游戏面板并将游戏循环放入后台线程的类。游戏花了一个月的时间完成,但结果非常好 - 非常好,我在Android市场上推出了它,现在有更多的下载。但是有一个相当大的缺点,它在这里:帧速率不恒定android游戏

为了在这个任务中获得最高的速度,必须制作一个游戏,帧频必须是恒定的。而且因为我获得了最高学位,老师对我的源代码非常满意,所以我认为没有错误修正。但是 - 事情是帧速率不是恒定的。更奇怪的是:在屏幕上移动的图形对象 - 移动速度越慢,CPU速度越快。

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

  • SAMSUNG GALAXY MINI(处理器时钟速度1 GHz)这里的物体快速移动
  • 三星GALAXY S3(处理器主频1.8 GHz的)在此,对象移动以中等速度
  • SAMSUNG GALAXY S4(处理器时钟速度2.3千兆赫)在这里,对象移动缓慢

所以简要 - 更强大吨智能手机处于处理器环境中时,图形对象在屏幕上移动的速度越慢 - 人们可能会想到相反的情况 - 对于速度更快的智能手机,帧速率更高。

我提出下面的backgroundthread持有gameloop - 我看不出我做错了什么在此代码 - 也不是我的老师

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 
    } 
} 

}

在此先感谢

+0

我不确定物体为什么缓慢移动,仍然在想那个......但是有一个提示 - 你的更新方法应该接受增量时间。这样,您的对象可以考虑更新时间。 –

+0

也许这可以帮助你http://gafferongames.com/game-physics/fix-your-timestep/ – Rad1

回答

1

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

问题是你的while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)追赶循环没有考虑到追赶模型时遗留下来的时间。

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

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

另外需要注意的是,Galaxy Mini和Galaxy S3之间的速度差异可能已经在另一个方向上(S3出现得更快),具体取决于更新和渲染方法的相对时间以及它们与FRAME_PERIOD的比率。


但是,除了上述简化局面(这忽略VSYNC),睡眠时间是永远不会被操作系统正是应用 - 它每次都会变化加上或减去几毫秒。

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

如果你有一个固定的增量时间(你可能需要一个物理游戏,或者你的游戏已经使用固定增量时间),this site提供了一个清楚的解释如何实现它。