尽管使用了paintComponent和动画线程,但使用Java/Swing的动画仍然很跳跃

尽管使用了paintComponent和动画线程,但使用Java/Swing的动画仍然很跳跃,java,swing,animation,Java,Swing,Animation,我有一个简单的Java/Swing应用程序,它尝试通过从左向右移动来设置长方体的动画: public class TestFrame extends JFrame { protected long startTime = new Date().getTime(); public class MainPanel extends JPanel { @Override protected void paintComponent(Graphics g)

我有一个简单的Java/Swing应用程序,它尝试通过从左向右移动来设置长方体的动画:

public class TestFrame extends JFrame {
    protected long startTime = new Date().getTime();

    public class MainPanel extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            // calculate elapsed time
            long currentTime = new Date().getTime();
            long timeDiff = currentTime - TestFrame.this.startTime;

            // animation time dependent
            g.fillRect((int) (timeDiff / 100), 10, 10, 10);
        }
    }

    public class MainLoop implements Runnable {
        @Override
        public void run() {
            while (true) {
                // trigger repaint
                TestFrame.this.repaint();
            }
        }
    }

    public static void main(String[] args) {
        new TestFrame();
    }

    public TestFrame() {
        // init window
        this.setTitle("Test");
        this.setSize(500, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(new MainPanel());
        this.setVisible(true);

        // start render loop
        Thread loop = new Thread(new MainLoop());
        loop.start();
    }
}
问题是动画不干净,长方体(有时)会跳跃几个像素。我已经做了一些研究,根据他们的说法,如果使用paintComponent(而不是paint)和基于时间的动画(而不是基于帧),应该可以很好地工作。我两个都做了,但是动画还是不干净


谁能告诉我出了什么问题吗?

你应该让你的while-true循环稍微休息一下。你有点烧坏了你的CPU!您正在生成大量的绘画事件;在线程调度程序决定的某个时间,调度程序会将任务交给事件调度线程,据我所知,该线程可能会将trillon绘画事件折叠为单个事件,并最终执行paintComponent

在下面的示例中,线程睡眠时间为20ms,最大帧速率为50fps。这应该足够了

while (true) {
    // trigger repaint
    TestFrame.this.repaint();

    try {
        Thread.sleep(20);
    } catch(InterruptedException exc() {
    }
}

您在我的机器上的绘制相当平滑,但它在循环中消耗了大量性能,但在速度较慢的机器上,如果您的应用程序忙于执行while或处理绘制事件而不是渲染,我可以想象动画会出现跳跃

根据每次渲染经过的时间更新位置可能更好,我不确定通过
Date
对象比较动画时间的准确性,因此使用
System.nanoTime()
比较小的时间差可能更好。例如:

long currentNanoTime = System.nanoTime();
long timeElapsed = currentNanoTime - lastUpdateNanoTime;
lastUpdateNanoTime = currentNanoTime;
...
int newXPosition = oldXPosition + (velocityXInPixelsPerNanoSecond * timeElapsed);

我对你的代码做了一些修改

  • 我调用了SwingUtilities invokeLater方法来创建和使用服务器上的Swing组件

  • 我调用了系统currentTimeinMillis方法来获取当前时间

  • 我没有设置JFrame的大小,而是设置了JPanel的大小并打包了JFrame。我缩小了JPanel的尺寸,以加快重新喷漆的速度

  • 我在while(true)循环中添加了一个延迟,正如fjf2002在他的回答中所建议的那样

  • 以下是经过修订和格式化的代码:

    package com.ggl.testing;
    
    import java.awt.Dimension;
    import java.awt.Graphics;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TestFrame extends JFrame {
    
        private static final long serialVersionUID = 272649179566160531L;
    
        protected long startTime = System.currentTimeMillis();
    
        public class MainPanel extends JPanel {
    
            private static final long serialVersionUID = 5312139184947337026L;
    
            public MainPanel() {
                this.setPreferredSize(new Dimension(500, 30));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                // calculate elapsed time
                long currentTime = System.currentTimeMillis();
                long timeDiff = currentTime - TestFrame.this.startTime;
    
                // animation time dependent
                g.fillRect((int) (timeDiff / 100), 10, 10, 10);
            }
        }
    
        public class MainLoop implements Runnable {
            @Override
            public void run() {
                while (true) {
                    // trigger repaint
                    TestFrame.this.repaint();
    
                    try {
                        Thread.sleep(20L);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new TestFrame();
                }
            });
        }
    
        public TestFrame() {
            // init window
            this.setTitle("Test");
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.add(new MainPanel());
            this.pack();
            this.setVisible(true);
    
            // start render loop
            Thread loop = new Thread(new MainLoop());
            loop.start();
        }
    }
    

    同样,考虑使用双缓冲:所以,不确定你想走多远,但是当我学习做动画的游戏循环时,这个教程非常整洁:@ AayMe:看起来很有趣。我来看看:)@mad程序员:嗯,让我想想。帧的时间乘以fps得到的常数是1秒。如果时间增加,fps将降低。我认为你错了。