除非鼠标移动,否则 Java 物理循环(按间隔重绘)不稳定

Posted

技术标签:

【中文标题】除非鼠标移动,否则 Java 物理循环(按间隔重绘)不稳定【英文标题】:Java Physics loop (repaint on interval) is choppy unless mouse is moving 【发布时间】:2014-11-19 15:37:36 【问题描述】:

我有一个简单的物理循环,它计算时间间隔,等待时间间隔过去,然后在屏幕上呈现结果。这是非常简单的代码(即使时间可能是错误的,但这正是我想要了解的)并且当我在屏幕上移动鼠标时效果很好。

package physicssim;

import java.awt.Graphics;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class PhysicsSim extends JFrame 

    private static class PhysicsObject 

        public PhysicsObject(double x, double y, double v_x, double v_y)  
            this.x = x;
            this.y = y;
            this.v_x = v_x;
            this.v_y = v_y;
        

        public double x;
        public double y;
        public double v_x;
        public double v_y;
    

    PhysicsObject particle;
    boolean running = true;
    DrawPane drawPane;
    public PhysicsSim() 
        particle = new PhysicsObject(10,10, .1, .2);
        drawPane = new DrawPane(particle);
        this.setSize(800,600);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        this.setContentPane(drawPane);
        this.setVisible(true);
    


    private static class DrawPane extends JPanel 

        PhysicsObject p;
        public DrawPane(PhysicsObject p) 
            this.p = p;
        

        @Override
        public void paint(Graphics g) 
            super.paint(g); //To change body of generated methods, choose Tools | Templates.           
            g.fillOval((int)p.x, (int) p.y, 10, 10);
        

    

    public void start() 
        int FPS = 60;

        long TIME_BETWEEN_FRAMES_NS = 1000000000/FPS;

        // Initial draw
        drawPane.repaint();
        long lastDrawTime = System.nanoTime();

        while(running) 

            // Update physics
            particle.x+=particle.v_x*(TIME_BETWEEN_FRAMES_NS*.0000001);
            particle.y+=particle.v_y*(TIME_BETWEEN_FRAMES_NS*.0000001);

            // While there is time until the next draw wait
            while(TIME_BETWEEN_FRAMES_NS > (System.nanoTime()-lastDrawTime)) 

                try 
                    Thread.sleep(1);
                 catch (InterruptedException ex) 
                    Logger.getLogger(PhysicsSim.class.getName()).log(Level.SEVERE, null, ex);
                
                      

            drawPane.repaint();
            long currentTime = System.nanoTime();
            System.out.println(currentTime - lastDrawTime);
            lastDrawTime = currentTime;
        
    

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 

        PhysicsSim sim = new PhysicsSim();
        sim.start();

    


关于打印时差的最后一点只是一个健全性检查,以确保它实际上是在请求的间隔周围调用。结果相当一致,所以我不明白为什么会有任何波动。

正如我上面提到的,如果我在屏幕上移动鼠标,这段代码效果很好,一切都很顺利。

如果我不移动鼠标,它会变得非常不稳定,直到我开始将鼠标移动到应用程序上。

我认为这很简单,但我希望你们能帮助我。谢谢你。

【问题讨论】:

考虑提供一个runnable example 来证明您的问题。这将导致更少的混乱和更好的响应。 “主循环”的效率非常低,睡 1 毫秒就足够接近不睡了,没有任何区别。最好预先计算延迟并至少休眠一段时间 - 但这只是我...... @MadProgrammer 谢谢。我发布了完整的代码。循环确实有效,但仅当您在屏幕上移动鼠标时。没有休眠一定时间的原因是 Thread.sleep(x) 并不总是一致的;我最终将运行非常详细的计算,这只是为了解决愚蠢的错误......就像这个。 @MadProgrammer 睡 1 毫秒(实际上更多)可以避免烤面包机。确实不需要再睡更长时间,尽管它不会伤害并且可能会获得更精确的时间。 @user2864740 据我了解(并且它是有限的),Thread.sleep 受操作系统的准确性和调度的支配,在 windows 下可以在 10-15ms 之间,所以这样做会很有效一点也不... @MadProgrammer 即使它在现实世界中只睡了 1 毫秒,它仍然会在相当长的时间内产生执行,这样虽然不理想,但它并不是“效率极低”。 (设置的 FPS 也产生约 16 毫秒的周期,因此更大的问题实际上是不规则更新 - 但通过 Swing/repaint 堆栈运行主要没有实际意义。) 【参考方案1】:

好吧,看来我的问题是我在paint() 中直接绘制到g。替换为以下内容后,一切正常。

 @Override
 public void paint(Graphics g) 
    BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR);
    img.getGraphics().fillOval((int) p.x, (int) p.y, 10, 10);
    g.drawImage(img, 0, 0, null);
  

我正在考虑删除此代码 sn-p,因为它粗鲁且可耻,但也许它会对其他人有所帮助。快乐编码。

【讨论】:

如果是缓冲问题,请使用paintComponent 而不是paint,因为它将在组件的双缓冲容量内。另外,你真的应该使用 getWidthgetHeight 而不是幻数

以上是关于除非鼠标移动,否则 Java 物理循环(按间隔重绘)不稳定的主要内容,如果未能解决你的问题,请参考以下文章

鼠标移动上的CSS js重绘而不消失

实现没有饥饿的while循环以在wpf中重绘图像

除非与mouselistener一起使用,否则JLabel不会显示

除非单击鼠标,否则按钮不会重新启用的奇怪问题

除非再次初始化GoogleMap,否则位置为空

如何及时重绘画布?