0

次のスイングタイマーを検討してください。

timer = new Timer (ballSpeed, tc);

ballSpeed最初は10tcです。は、画面に描画されたオブジェクトのx値をインクリメントするアクションリスナークラスです。値が小さいほどオブジェクトの移動が速くなるため、変数ballSpeedは一種の誤称です。

ここで、オブジェクトの動きをできるだけスムーズに見せたいと思います。したがって、ActionListenerでx値を1つずつインクリメントするだけです。つまり、オブジェクトはピクセルごとに移動するだけです。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(つまり、1秒あたり50回のリフレッシュ)を使用すると、適切な結果を達成できるはずです。問題は、ピクセルごとに1ピクセルしか移動できず、ボールの速度を移動の増加にリンクできるという仮定をやめたいということです。

次に例を示します(スライダーをドラッグするだけで結果が表示されます)。

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 に答える