Search code examples
javaswingactionlistener

Is there a way to smooth out the transition of a graphics component from one point to another using Timer class in Java?


I am trying to get a simple animation done smoothly in Java using Timer class, i.e. to move a target from its current position to the farthest end-of-frame horizontally.

I have used the graphics component's current position inside the Timer class's actionPerformed() method and have been increasing the x location of the component by a certain amount but when I try to run it the component instantly jumps to the next location and waits for the timer to complete and repaint to a new location. (I apologize if I am not phrasing the problem properly.)

I have included the code as a whole so as to provide the entire context:

public class FireAtBill extends JPanel {
    private static final long serialVersionUID = 4682094035006759518L;

    private int hits, misses;
    private Point targetPos;
    private Timer t;
    private BufferedImage crosshair, target;


    public FireAtBill() {

        Random random = new Random();
        targetPos = new Point(random.nextInt(500),random.nextInt(500) );
        t = new Timer(500, new TimerListener());
        t.start();

        setForeground(Color.WHITE);
        setBackground(Color.BLACK);

        try {
            crosshair = ImageIO.read(new File(System.getProperty("user.dir")+ "/crosshair.jpg"));
            target = ImageIO.read(new File(System.getProperty("user.dir")+ "/target.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawString("hits: " + hits + "  misses: " + misses, 0, 10);
        g2.drawImage(crosshair, 125, 50, this);
        g2.drawImage(target, targetPos.x, targetPos.y, this);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Fire At Bill");
        frame.setSize(500,500 );
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        FireAtBill fireAtBill = new FireAtBill();
        frame.getContentPane().add(fireAtBill);
        frame.setVisible(true);
    }

    private class TimerListener implements ActionListener{
        double x = targetPos.getX();
        public void actionPerformed(ActionEvent event) {
            if (x <= 500||targetPos.getX() >= 0) {
                if (x<250) {
                    targetPos.setLocation(targetPos.getX()+15, targetPos.getY());
                }
                else {
                    targetPos.setLocation(targetPos.getX()-15, targetPos.getY());
                }
            }
            repaint();
        }

    }

}

I expect the target to move smoothly across the screen horizontally, but the target is jumping from one location to another. I have tried to increment the x position of the target using smaller values and while this smooths out the movement, it does so at the cost of the speed of the target which I hope to implement as a variable which can be altered by changing its value. Thanks in advance.


Solution

  • Okay, I found that by decreasing the delay value when initializing the Timer object I can speed up the repaint process here t = new Timer(500, new TimerListener()); by changing 500 to something like 50. Decreasing this value will speed up the animation.

    And the smoothing can be implemented by decreasing the increment value inside the actionPerformed() method of the TimeListener class here targetPos.setLocation(targetPos.getX()+15, targetPos.getY());by changing the increment value 15 to something like 3. Decreasing this value will help smooth the animation.