Skip to content
Advertisement

Reverse the motion if its crosses the borders of the frame

I am working on program that should make rectangle moves from top to bottom and then left to right once the rectangle crosses the borders of the frame it should reverse its motion from right to left and bottom to top. I managed to do the first part with a simple if statement but I didn’t know how to reverse it.

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class RectangleAnimation extends JFrame {

        private static final int FRAME_WIDTH = 800; 
        private static final int FRAME_HEIGHT = 800; 

        public RectangleAnimation() {

        setLayout(new BorderLayout( ));     
        setSize(FRAME_WIDTH, FRAME_HEIGHT); 
        setTitle("Rectangle Animation 2D");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
    
        PanelRectangleAnimation panel = new PanelRectangleAnimation();
        add(panel, BorderLayout.CENTER); 
        
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {        
        
        new RectangleAnimation();           
    }

}

class PanelRectangleAnimation extends JPanel implements Runnable {

    private static final int PANEL_WIDTH = 400;
    private static final int PANEL_HEIGHT = 400;
    private static final int RW = 60;
    private static final int RH = 40;
        
    int x, y;
    
    Rectangle2D.Double rec;
    
    Thread mythread;

    public PanelRectangleAnimation() {
        setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
        this.setBackground(Color.white);

        mythread = new Thread(this);
        mythread.start();
    }

    public void run() {
        
        // animation loop
        boolean flag = true;
        while (flag) {
            try{
                Thread.sleep(10);
            }
            catch(InterruptedException e){}
            if(y == 355){
                    x = x + 5;
                }else{
                    y = y + 5;
                }

            if ( x > PANEL_WIDTH || y > PANEL_HEIGHT) {
                x = 0;
                y = 0;
            }                       
            repaint();
        }        
        
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;        
         
        g2.setColor(Color.red);             
        rec = new Rectangle2D.Double(x, y, RW, RH);     
        g2.fill(rec);       
    }
}


Advertisement

Answer

So, you basic problem can be described as a basic “collision detection” problem.

The basic concept is, if one of the box’s edges exceeds the viewable area, you want to reverse the delta, for example…

box += delta;
if box > viewable_width {
    box = viewable_width
    delta *= -1; // reverse delta
}

Now, you also need to check for left/top edge as well (ie < 0), but the basic idea is the same.

Swing is also not thread safe and is single threaded. This means that you should not be making changes to the UI, or any state the UI relies on, from outside the context of the event dispatching thread AND you should not be performing any long running/blocking operations from within the context of the event dispatching thread.

See Concurrency in Swing for more details and How to Use Swing Timers for a possible/common solution

For example…

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Timer timer;
        private Rectangle box = new Rectangle(0, 0, 64, 40);

        public TestPane() {
            timer = new Timer(5, new ActionListener() {

                private int xDelta = 1;
                private int yDelta = 1;

                @Override
                public void actionPerformed(ActionEvent e) {
                    box.x += xDelta;
                    box.y += yDelta;

                    if (box.x + box.width > getWidth()) {
                        box.x = getWidth() - box.width;
                        xDelta *= -1;
                    } else if (box.x < 0) {
                        box.x = 0;
                        xDelta *= -1;
                    }
                    if (box.y + box.height > getHeight()) {
                        box.y = getHeight() - box.height;
                        yDelta *= -1;
                    } else if (box.y < 0) {
                        box.y = 0;
                        yDelta *= -1;
                    }
                    repaint();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 400);
        }

        @Override
        public void addNotify() {
            super.addNotify();
            if (timer == null) {
                return;
            }
            timer.start();
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            if (timer == null) {
                return;
            }
            timer.stop();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            g2d.fill(box);
            g2d.dispose();
        }

    }
}

Advertisement