0

考虑以下 Swing 计时器:

timer = new Timer (ballSpeed, tc);

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

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

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

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

有没有解决的办法?或者有没有更好的方法来实现屏幕上的对象移动?

4

2 回答 2

2

Ajavax.swing.Timer没有足够的准确度或精度来生成您想要实现的平滑图形。此外,摇摆计时器会受到摇摆事件队列中其他任何东西的影响

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

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

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

于 2012-11-30T17:27:23.290 回答
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();
                }
            }
        });
    }
}
于 2012-11-30T17:53:30.757 回答