Search code examples
javagraphicsjpaneltransformgraphics2d

how to move multiple objects in JPanel


I want to create multiple squares that move independently and at the same time,and I think the most efficient way is through the transform method in Graphics2D,but I'm not sure how to make it work for each square.I want the square object to be self contained and create its own transforms(instance transforms). Here's what I have so far.

TransformPanel

import javax.swing.*; 
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class TranformPanel extends JPanel {
    private int[] xcoords = {250,248,253,255,249}; 
    private int[] ycoords = {250,253,249,245,250}; 
    private double randomx = 0; 
    private double randomy = 0;
    public void paintComponent(Graphics g) 
    { 
        super.paintComponent(g); 
        drawTransform(g,randomx,randomy);
    } 
    private void drawTransform(Graphics g,double randomx,double randomy) 
    {  
        Random rn = new Random(); 
        int xnum = rn.nextInt(10)-5; 
        randomx = xnum; 
        int ynum = rn.nextInt(10)-5; 
        randomy = ynum;

        Rectangle rect = new Rectangle(250,250,10,10);  
        AffineTransform transform = new AffineTransform();   
        Graphics2D g2d = (Graphics2D)g;   

        transform.translate(randomx,randomy);
        g2d.draw(transform.createTransformedShape(rect));  

        }
    }

TransformDraw

import java.awt.*; 
import javax.swing.*; 
import java.awt.geom.AffineTransform;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class TransformDraw{ 
    private static TranformPanel panel = new TranformPanel();
    public static void main(String[] args) {    
        // Setup our JFrame details 
        JFrame frame = new JFrame();
        frame.setTitle("Transform Polygon Example");
        frame.setSize(500,500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());   
        frame.setVisible(true);
        frame.add(panel); 
        Scanner input = new Scanner(System.in); 
        for (int i=0;i<10;i++) 
        { 
        try {
            TimeUnit.SECONDS.sleep(1);
            frame.repaint(); 
        } catch (InterruptedException e) {
            e.printStackTrace();
            }
        } 
    } 
}

Thanks is Advance!


Solution

  • Start by creating something that can manage it's location (and other properties) and which can be "painted"

    public interface Box {
        public void update(Dimension size);
        public void paint(Graphics2D g2d);
    }
    

    So, this is pretty basic, all it can do is be updated (within a given area) and be painted. You could expose other properties (like it's bounding box for example) based on your particular needs

    Next, we need a simple implementation, something like...

    public class DefaultBox implements Box {
    
        private Color color;
        private Rectangle bounds;
    
        private int xDelta;
        private int yDelta;
    
        public DefaultBox(Color color, Dimension size) {
            this.color = color;
            bounds = new Rectangle(new Point(0, 0), size);
    
            xDelta = 1 + (int) (Math.random() * 10);
            yDelta = 1 + (int) (Math.random() * 10);
        }
    
        @Override
        public void update(Dimension size) {
            bounds.x += xDelta;
            bounds.y += yDelta;
    
            if (bounds.x < 0) {
                bounds.x = 0;
                xDelta *= -1;
            } else if (bounds.x + bounds.width > size.width) {
                bounds.x = size.width - bounds.width;
                xDelta *= -1;
            }
            if (bounds.y < 0) {
                bounds.y = 0;
                yDelta *= -1;
            } else if (bounds.y + bounds.height > size.height) {
                bounds.y = size.height - bounds.height;
                yDelta *= -1;
            }
        }
    
        @Override
        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fill(bounds);
        }
    
    }
    

    Now, this maintains a simple Rectangle instance, which describes the location and size of the object, it also maintains properties about the color and it's speed.

    When update is called, it updates it's location and does some simple bounds checking to make sure that the box remains within the specified area.

    When paint is called, it simply paints itself.

    Finally, we need some way to update and paint these boxes....

    public class TestPane extends JPanel {
    
        private List<Box> boxes;
        private Color[] colors = {Color.RED, Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE, Color.YELLOW};
    
        public TestPane() {
            boxes = new ArrayList<>(25);
            for (int index = 0; index < 100; index++) {
                Color color = colors[(int) (Math.random() * colors.length)];
                int width = 10 + (int) (Math.random() * 9);
                int height = 10 + (int) (Math.random() * 9);
                boxes.add(new DefaultBox(color, new Dimension(width, height)));
            }
    
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Box box : boxes) {
                        box.update(getSize());
                    }
                    repaint();
                }
            });
            timer.start();
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (Box box : boxes) {
                Graphics2D g2d = (Graphics2D) g.create();
                box.paint(g2d);
                g2d.dispose();
            }
        }
    
    }
    

    Okay, so again, this is pretty simple. It maintains a List of Box's, a Swing Timer to periodically update the List of Box's, calling their update method. The Timer the simply calls repaint, which (in a round about way) ends up calling paintComponent, which then just calls paint on each instance of Box.

    100 boxes...

    100 Boxes