如何(正确)在纯Java中以高帧速率渲染

时间:2017-10-08 01:29:55

标签: java swing

我一直试图用纯粹的java制作一个简单的游戏,而且我在绘图时遇到了问题。我试图保持相对较高的帧速率,但却遇到JFrame.repaint()不能强制使用的问题。并且仅仅是在下一个可用机会重绘框架的请求。结果,下面代码的帧速率非常糟糕。然而,(这是一个奇怪的部分)当我的鼠标移动时似乎只是可怕。如果我的鼠标移动并且在窗口顶部,则帧速率快速且清晰。

我已经尝试了各种在线建议,甚至编写了如何执行此操作的示例,当鼠标不在窗口上移动时,它们似乎都有相同的问题,帧速率急剧下降。

(我使用Linux,如果重要的话)

非常感谢任何和所有帮助!

%ld

2 个答案:

答案 0 :(得分:2)

经过太多研究,结果证明java在许多Linux系统上不会自动同步/刷新显示缓冲区。所有的重绘都是正确的,但显示缓冲区没有冲洗,因此产生了奇怪的滞后效应。

解决方案:

Toolkit toolkit = Toolkit.getDefaultToolkit(); /* get AWT toolkit */

/* initialize things */
...

while (your_loop) {

  /* run your logic */
  ...

  /* paint everything */
  ...

  toolkit.sync(); /* force display buffer to flush */
}

感谢大家的投入

答案 1 :(得分:1)

问题并不简单。下面的代码尚未经过测试,只是为了给你一个想法......在下一行中,AWT是Swing的基础。

首先,你必须保持你的paintComponent()非常快(确实!)。这是第一个要求。基本上,对于60 fps,您必须在不到15毫秒的时间内绘制。忘记transaparency和其他东西(在Windows上工作很糟糕,我不知道Linux)。尽可能尝试保存计算。

其次,在另一个线程中执行其他所有操作。这是我用于自己程序的方式。注意每次调用AWT(包括Swing,当然)必须封装在EventQueue.invokeLater()的调用中,以确保你在AWT线程中运行东西,因为设置标签绝不能在AWT线程之外完成。

当您从AWT收到需要时间的输入时,不要忘记创建一个线程!

第三,用timer之类的

替换你的循环
new Timer("Drawer", true).scheduleAtFixedRate( new TimerTak(){
    public void run(){
       frame.repaint();
    }
}, 
100, // Start in 100 ms
(int)(1000 / 60)); // 60 is the frame rate.

一切都应该顺利进行。对于帧计数k,请使用以下内容:

// You should initialize just before you create the timer...!
static private long startedAt = System.currentTimeMillis();

@Override public void paintComponent(Graphics g) {
    super.paintComponent(g);

    // Microseconds since the game started.
    long k = (System.currentTimeMillis() - startedAt);

    // Increment only one by frame (60 fps)
    k = (int)((double)k * 60 / 1000.0)

    // Draw the game...!      
}

这就是全部。请注意,如果计算机功能不够强大(或者需要CPU密集型,或垃圾收集器......),则可以删除某些帧。但是,如果可能,您的游戏将以最高60 fps的速度运行。

奖励:如果每次通过paintComponent()时增加一个值,就可以找到自开始以来真正显示的丢帧数或平均每秒帧数游戏。