Swing计时器的浮点值?

时间:2012-11-30 16:42:03

标签: java swing graphics timer

考虑以下Swing计时器:

timer = new Timer (ballSpeed, tc);

ballSpeed最初为10. tc是一个Action Listener类,它增加屏幕上绘制的对象的x值。变量ballSpeed有点用词不当,因为值越低,对象移动得越快。

现在我希望对象的移动看起来尽可能平滑。因此,我只会在ActionListener中逐个递增x值。那就是对象应该只移动逐像素移动。我使用x++代替x+=10。因此我不会以这种方式改变球的速度。

现在因为Timer的第一个参数只接受一个整数,所以它不会让我对对象的速度有很大的控制权。我只能使用10,9,8等。对象移动得太快或太慢。

总而言之,毫秒精度是不够的。

有解决方法吗?或者是否有更好的方法在屏幕上实现对象移动?

2 个答案:

答案 0 :(得分:2)

javax.swing.Timer没有足够的准确度或精确度来生成您想要实现的平滑图形。此外,挥杆计时器为affected by anything else in the swing event queue

  

其次,它的自动线程共享意味着您不必采取特殊步骤来避免产生太多线程。相反,您的计时器使用相同的线程来使光标闪烁,出现工具提示等等。

如果您不想使用某些媒体框架或使用描述对象运动的API(而不是实际移动对象),则应使用swing计时器作为计划下一次计算的方法,但要确定自上次计算以来经过的时间,查看现在System.nanoTime()与上次计算过程中nanoTime之间的差异。

使用这种方法,您可以在动力不足的机器上获得更多交错但更正确的动画。

答案 1 :(得分:2)

好的,所以如果你真的想要纳秒,那么在这个答案的最后是一种使用ScheduledThreadPool的方法。但这只是纯粹的疯狂,这可能导致大量问题,结果令人失望。我真的不会走那条路。

使用50Hz(即每秒50次刷新),您应该能够获得不错的结果。问题是我宁愿放弃你的每个像素只能移动像素并将你的球速与你的移动增加联系起来的假设。

以下是一个示例(只需拖动滑块即可查看结果):

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestAnimation2 {

    private static final int NB_OF_IMAGES_PER_SECOND = 50;
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;

    private static final int MIN = 0;
    private static final int MAX = 100;

    private double speed = convert(50);

    private double dx;
    private double dy;

    private double x = WIDTH / 2;
    private double y = HEIGHT / 2;

    private JFrame frame;
    private CirclePanel circle;
    private Runnable job;

    private long lastMove = System.currentTimeMillis();

    protected void initUI() throws MalformedURLException {
        frame = new JFrame(TestAnimation2.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);
        circle = new CirclePanel();
        circle.setSize(20, 20);
        frame.add(circle);
        frame.setSize(WIDTH, HEIGHT);
        dx = speed;
        dy = speed;
        final JSlider slider = new JSlider(MIN, MAX);
        slider.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                speed = convert(slider.getValue());
                if (dx > 0) {
                    dx = speed;
                } else {
                    dx = -speed;
                }
                if (dy > 0) {
                    dy = speed;
                } else {
                    dy = -speed;
                }

            }
        });
        slider.setValue(50);
        slider.setLocation(0, 0);
        slider.setSize(slider.getPreferredSize());
        frame.add(slider);
        frame.setVisible(true);
        Timer t = new Timer(1000 / NB_OF_IMAGES_PER_SECOND, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                move();
            }
        });
        t.start();
    }

    protected double convert(double sliderValue) {
        return sliderValue + 1;
    }

    protected void move() {
                x += dx;
                y += dy;
                if (x + circle.getWidth() > frame.getContentPane().getWidth()) {
                    x = frame.getContentPane().getWidth() - circle.getWidth();
                    dx = -speed;
                } else if (x < 0) {
                    x = 0;
                    dx = speed;
                }
                if (y + circle.getHeight() > frame.getContentPane().getHeight()) {
                    y = frame.getContentPane().getHeight() - circle.getHeight();
                    dy = -speed;
                } else if (y < 0) {
                    y = 0;
                    dy = speed;
                }
                circle.setLocation((int) x, (int) y);
                circle.repaint();
    }

    public static class CirclePanel extends JPanel {

        public CirclePanel() {
            super();
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.RED);
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    new TestAnimation2().initUI();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

以下是使用预定线程池的示例(非常危险且令人失望)

import java.awt.Color;
import java.awt.Graphics;
import java.net.MalformedURLException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestAnimation2 {
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    private static final int SLOWEST_RATE = 10000000;
    private static final int FASTEST_RATE = 1000;
    private static final int RANGE = SLOWEST_RATE - FASTEST_RATE;

    private static final int MIN = 0;
    private static final int MAX = 100;

    private double dx;
    private double dy;

    private double x = WIDTH / 2;
    private double y = HEIGHT / 2;

    private volatile long delay = convert(50);
    private JFrame frame;
    private CirclePanel circle;
    private Runnable job;

    private long lastMove = System.currentTimeMillis();

    protected void initUI() throws MalformedURLException {
        frame = new JFrame(TestAnimation2.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);
        circle = new CirclePanel();
        circle.setSize(20, 20);
        frame.add(circle);
        frame.setSize(WIDTH, HEIGHT);
        dx = 1;
        dy = 1;
        final ScheduledExecutorService sheduledThreadPool = Executors.newScheduledThreadPool(1);
        final JSlider slider = new JSlider(MIN, MAX);
        slider.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                delay = convert(slider.getValue());
            }
        });
        slider.setValue(50);
        slider.setLocation(0, 0);
        slider.setSize(slider.getPreferredSize());
        frame.add(slider);
        frame.setVisible(true);
        job = new Runnable() {

            @Override
            public void run() {
                move();
                sheduledThreadPool.schedule(job, delay, TimeUnit.NANOSECONDS);
            }
        };
        sheduledThreadPool.schedule(job, delay, TimeUnit.NANOSECONDS);
    }

    protected long convert(float sliderValue) {
        return (long) (SLOWEST_RATE - sliderValue / (MAX - MIN) * RANGE);
    }

    protected void move() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                System.err.println("Ellapsed " + (System.currentTimeMillis() - lastMove) + " delay is " + (double) delay / 1000000 + " ms");
                x += dx;
                y += dy;
                if (x + circle.getWidth() > frame.getContentPane().getWidth()) {
                    x = frame.getContentPane().getWidth() - circle.getWidth();
                    dx = -1;
                } else if (x < 0) {
                    x = 0;
                    dx = 1;
                }
                if (y + circle.getHeight() > frame.getContentPane().getHeight()) {
                    y = frame.getContentPane().getHeight() - circle.getHeight();
                    dy = -1;
                } else if (y < 0) {
                    y = 0;
                    dy = 1;
                }
                circle.setLocation((int) x, (int) y);
                frame.repaint();
                lastMove = System.currentTimeMillis();
            }
        });
    }

    public static class CirclePanel extends JPanel {

        public CirclePanel() {
            super();
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.RED);
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    new TestAnimation2().initUI();
                } catch (MalformedURLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
    }
}