摆动计时器的浮点值



考虑以下摆动计时器:

timer = new Timer (ballSpeed, tc);

ballSpeed最初是 10 岁。 tc 是一个操作侦听器类,用于递增屏幕上绘制的对象的 x 值。变量ballSpeed有点用词不当,因为值越低,对象移动得越快。

现在,我希望对象的移动看起来尽可能平滑。因此,我只会在操作侦听器中逐个递增 x 值。也就是说,对象应该只逐个像素地移动。我用x++而不是x+=10.因此,我不会以这种方式修改球的速度。

现在,由于 Timer 的第一个参数只接受整数,因此它不能让我对对象的速度进行大量控制。我只能使用10,9,8等。对象移动得太快或太慢。

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

有没有办法解决这个问题?或者是否有一种整体上更好的方法来在屏幕上实现对象移动?

javax.swing.Timer没有足够的

准确度或精度来生成您尝试实现的平滑图形。此外,摆动计时器受摆动事件队列中的任何其他内容的影响:

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

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

使用这种方法,您将在动力不足的机器上拥有更多锯齿状但更正确的动画。

好的,所以如果你真的想追求纳秒,在这个答案的最后是一种使用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();
                }
            }
        });
    }
}

相关内容

  • 没有找到相关文章

最新更新