Проблемы с анимацией траектории полета шара (по координатам)

Уважаемые участники сообщества, добрый день. Возникла трудность с анимацией движения шара. Результат работы кода, который отвечает за расчет координат, меня вполне устраивает, но основная проблема заключается в том, что у меня не получается сделать именно анимацию движения шара. При выполнении кода ниже с параметрами 40 (начальная скорость тела) / 90 (угол, под которым брошено тело) (для видимости шара на полотне) сразу отрисовывается вся траектория полета. При этом, если передавать значение переменной X и Y вне цикла FOR (в методе paintComponent) вообще никакой траектории нет, прописывается только одно значение X и Y (оно и понятно), но если же добавить кусок (на данный момент он закомментирован в коде)

   x = x + 1; //координаты движения (X)
   y = y + 1; //координаты движения (Y)

и убрать вообще цикл FOR(в методе paintComponent), то анимация заработает. В идеале планируется создание полноценной анимации траектории движения шара, которая считается по вводимым пользователем параметрам. У меня есть подозрение, что я где-то уже достаточно близко, но разобраться в течение долгого времени самостоятельно не получается, поэтому прошу подсказать в чем моя основная ошибка/направить в нужное русло. Заранее спасибо. Код прилагаю:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class MovingCircle extends JComponent implements ActionListener {
    private static CoordinateSetting coordinateSetting;
    private double scale;
    private Color color;
    private Timer timer;
    public double x = 0; //стартовые координаты шара (x)
    public double y = 0; //стартовые координаты шара (y)


    public MovingCircle(Color color, int delay, CoordinateSetting coordinateSetting) {
        this.coordinateSetting = coordinateSetting;
        scale = 1.0;
        timer = new Timer(delay, this);
        this.color = color;
        setPreferredSize(new Dimension(500, 500));
    }

    public void start() {
        timer.start();
    }

    public void stop() {
        timer.stop();
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.white);
        int width = 1000;
        int height = 1000;
        g.fillRect(0, 0, width, height); //белый фон, на котором летит шар
        g2d.setColor(Color.black);
        g2d.drawRect(0, 0, width - 1, height - 1); //левая и правая линии черного цвета
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(color);
        g2d.scale(scale, scale);
   //Первоначальные значения, которые позволяют анимировать движение шара (ниже)
       //x = x + 1; //координаты движения (X)
       //y = y + 1; //координаты движения (Y) 
   //Первоначальные значения, которые позволяют анимировать движение шара (выше)

//Вводимые параметры: 40 / 90 (которые видны на полотне)
//КУСОК, КОТОРЫЙ ОТРИСОВЫВАЕТ ВСЕ КООРДИНАТЫ (НИЖЕ) 
        for (int i = 0; i < coordinateSetting.xCoordinates().length; i++){
            System.out.println("Координата Х ["+i+"]=:" + coordinateSetting.xCoordinates()[i]);
            System.out.println("Координата Y ["+i+"]=:" + coordinateSetting.yCoordinates()[i]);

            x = coordinateSetting.xCoordinates()[i];
            y = coordinateSetting.yCoordinates()[i];

            if (i > 0) {
                x0 = coordinateSetting.xCoordinates()[i-1];
                y0 = coordinateSetting.yCoordinates()[i-1];
        Ellipse2D el = new Ellipse2D.Double(x, y, 15, 15);
        g2d.fill(el);
            }
        }
//КУСОК, КОТОРЫЙ ОТРИСОВЫВАЕТ ВСЕ КООРДИНАТЫ (ВЫШЕ)         
    }

    @FunctionalInterface
    interface CoordinateCalculator {
        int calculate (double u, double a, double g, double t);
    }

    //Класс, отвечающий за принятие параметров шара.
    static class CoordinateSetting {

        /* Расчет вертикального положения тела через 1 сек
         *
         * @param u - начальная скорость тела (м/с)
         * @param a - угол под которым брошено тело
         * @param g - сила гравитации
         * @param t - время движения (с)
         * @param n - кол-во точек на графике
         */

        private final double u, a, g, t;
        private final int n;

        public CoordinateSetting(double u, double a, double g, double t, int n) {
            this.u = u;
            this.a = a;
            this.g = g;
            this.t = t;
            this.n = n;
        }

        public int getN() {
            return n;
        }

        public int[] xCoordinates() {
            CoordinateCalculator calculator = (t1, t2, t3, t4)-> ((int) ((t1 * t2 * t3)*10));
            return coordinates(u, a, g, t, calculator, Math.cos(Math.toRadians(a)));
        }

        public int[] yCoordinates() {
            CoordinateCalculator calculator = (t1, t2, t3, t4)-> ((int)(1000 - ((t1 * t2 * t3 - t4 * t2 * t2 / 2) * 10)));
            return coordinates(u, a, g, t, calculator, Math.sin(Math.toRadians(a)));
        }

        private int[] coordinates(double u, double a, double g, double t,
                                  CoordinateCalculator calculator, double tmp) {
            float tpol_100 = (float)(2 * u * Math.sin(Math.toRadians(a)) / g / 100);
            int[] coordinates = new int[n];
            for (int i = 0; i < n; i++) {
                coordinates[i] = calculator.calculate(u, t, tmp, g);
                t = t + tpol_100;
            }
            return coordinates;
        }

    }

    public static void main(String[] args) {
        float u = 0, a = 0, g = 9.81f, t = 0f;
        int n = 101;
        Scanner sc = new Scanner(System.in);
        System.out.print("Введите параметр 'Начальная скорость тела' (число): ");
        if (sc.hasNextFloat()) {
            u = sc.nextFloat();
            System.out.println("Вы ввели корректное значение " + u);
        } else {
            System.out.println("Извините, но это явно не число. Приложение будет закрыто!");
            System.exit(0);
        }

        System.out.print("Введите параметр 'Угол под которым брошено тело' (число): ");
        if (sc.hasNextFloat()) {
            a = sc.nextFloat();
            System.out.println("Вы ввели корректное значение " + a);
        } else {
            System.out.println("Извините, но это явно не число. Приложение будет закрыто!");
            System.exit(0);
        }
        sc.close();
        float finalU = u;
        float finalA = a;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Moving Circle");
                JPanel panel = new JPanel();
                //final MovingCircle MovingCircleGreen = new MovingCircle(Color.green, 20,  coordinateSetting);
                final MovingCircle MovingCircleGreen = new MovingCircle(Color.green, 20,  new CoordinateSetting(finalU, finalA, g, t, n));

                panel.add(MovingCircleGreen);
                frame.getContentPane().add(panel);
                final JButton button = new JButton("Start");
                button.addActionListener(new ActionListener() {
                    private boolean pulsing = false;
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (pulsing) {
                            pulsing = false;
                            MovingCircleGreen.stop();
                            button.setText("Start");
                        } else {
                            pulsing = true;
                            MovingCircleGreen.start();
                            button.setText("Stop");
                        }
                    }
                });
                panel.add(button);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(600, 600);
                frame.setVisible(true);
            }
        });
    }
}

Ответы (1 шт):

Автор решения: Stranger in the Q

Вот, могу предложить что-то такое:

List<Point> points = new ArrayList<>();
for (int i=0; i < 300; i++)
    points.add(new Point(i, 100+(int) (Math.sin(((float)i)/10)*50)));
JFrame frame = new JFrame();
JComponent comp = new JComponent() {
    int index = 0;
    @Override
    protected void paintComponent(Graphics g) {
        index = (index + 1) % points.size();
        Point p = points.get(index);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.drawOval(p.x-10, p.y-10, 20, 20);
    }
};
frame.add(comp);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(new Dimension(300, 300));
frame.setLocationRelativeTo(null);
frame.setVisible(true);

Executors.newScheduledThreadPool(1)
         .scheduleAtFixedRate(() -> comp.repaint(), 0, 30, TimeUnit.MILLISECONDS);
→ Ссылка