2017-10-08 108 views
0

我一直在尝试在纯Java中制作一个简单的游戏,并且在绘图中遇到了问题。我试图保持相对较高的帧速率,但是JFrame.repaint()不能被“强制”并且只是在下一个可用机会重新绘制帧的请求时遇到问题。结果,下面的代码的帧率非常糟糕。然而,(这是一个奇怪的部分),当我的鼠标没有移动时,它似乎只是很糟糕。如果我的鼠标正在移动并且超出了窗口的顶部,则帧速率很快且很清晰。我如何(正确)在纯Java中以高帧率渲染

我已经尝试过各种在线建议,甚至编译过关于如何做到这一点的示例,并且当鼠标不移动到窗口上时,它们似乎都具有相同的帧速率下降问题。

(我使用Linux,如果该事项)

任何及所有的帮助深表感谢!

import java.awt.*; 
import javax.swing.*; 


public class Test extends JPanel { 


    public static void main(String[] args) { 

    JFrame frame = new JFrame(); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setSize(new Dimension(300, 300)); 
    frame.setVisible(true); 
    frame.getContentPane().add(new Test()); 


    for (int k = 0; k < 1_000_000; k++) { 
     frame.repaint(); 
     try { 
     Thread.sleep(10); 
     } catch (InterruptedException e) { 
     e.printStackTrace(); 
     System.exit(1); 
     } 
    } 

    frame.dispose(); 
    System.exit(0); 
    } 



    private int k = 0; 

    public Test() { 
    super(); 
    } 

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

    g.setColor(Color.WHITE); 
    g.fillRect(0, 0, getWidth(), getHeight()); 

    g.setColor(Color.BLACK); 
    int height = (int) (((k * 0.01) % 1) * getHeight()); 
    g.drawLine(
     0, height, 
     getWidth(), height 
    ); 

    k++; 

    } 

} 
+2

更好看看gamedev.stackexchange.com。这对你来说看起来像是一个有用的问题(“不要在绘画方法中更新你的”k“,它基于壁钟)”https://gamedev.stackexchange.com/questions/43347/how-to-handle -frame-rates-and-synchronizing-screen-repaints –

+0

'这似乎只在我的鼠标没有移动时变得糟糕透顶。如果我的鼠标正在移动并且超出了窗口的顶部,则帧速率很快且很清晰。“ - 对于我来说,无论鼠标是否在移动,帧速率都与我相同。我在Window 7上使用JDK8。另外,只需在构造函数中设置面板的背景。不需要画背景两次。 – camickr

+0

为了简单起见,只是把K放在那里。游戏要复杂得多。我只写了一小段代码来演示我的渲染更新问题。但我会看看gamedev。谢谢 – LeqxLeqx

回答

1

经过太多的研究,事实证明,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

这个问题并不简单。下面的代码没有经过测试,只是为了给你提供这个想法......在接下来的几行中,AWT是Swing的基础。

首先,你必须保持你的paintComponent()非常快(确实!)。这是第一个要求。基本上,对于60 fps,你必须画出少于15毫秒。忘记透明性和其他东西(在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()时间增量的值,你可以找到的帧数下降或自比赛开始帧每秒的平均数目确实显示。

+0

不幸的是,这个(虽然稍微好点)仍然有同样的问题 – LeqxLeqx