Search code examples
javaswinganimationcollision-detection

Java Animation, update animation with frequency of 1x, 1y


When my animation is in progress figures get stuck together. I think it's because if Figure get velocity x=5 y=5 i move them and then check if they hit anything and my figure can be already inside 2nd figure. Like that I want to check if they hit anything more often but im not sure how to put my methods in actionPerformed method. Velocity of figures is not constant. Do you have any ideas, examples or suggestions?

public class PaintFigures extends JPanel implements ActionListener {

    static List<Figure> figuresList = new ArrayList<Figure>();
    Timer t = new Timer(5, this);

    public PaintFigures(List<Figure> figuresList) {
        PaintFigures.figuresList = figuresList;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        t.start();
        for (Figure figure : figuresList) {
            figure.drawItself(g2d);
        }

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        FiguresUpdate.update(figuresList); // Check if they hit anything (other figure or frame)
        FiguresUpdate.move(figuresList); // move them
        repaint();

    }
}

Runnable Example Here Class main

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test extends JPanel implements ActionListener {
    static private List<Square> figuresList = new ArrayList<Square>();
    Timer t = new Timer(5, this);

    public static void main(String[] args) {
        Square s1 = new Square(40);
        Square s2 = new Square(60);
        Square s3 = new Square(20);
        figuresList.add(s1);
        figuresList.add(s3);
        figuresList.add(s2);
        JFrame frame = new JFrame("Figures Animation");
        frame.setSize(700, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new Test();
        panel.setBackground(Color.GRAY);
        frame.getContentPane().add(BorderLayout.CENTER, panel);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        t.start();
        for (Square figure : figuresList) {
            figure.drawItself(g2d);
        }

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Test.update(figuresList); // Check if they bounce
        // FiguresUpdate.move(figuresList); // move them
        repaint();

    }

    public static void update(List<Square> list) {
        updateFlags(list);
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setLocationX(
                    list.get(i).getLocationX() + (list.get(i).getVelocityX()));
            list.get(i).setLocationY(
                    list.get(i).getLocationY() + (list.get(i).getVelocityY()));
            if (list.get(i).getLocationX() < 0
                    || list.get(i).getLocationX() > 680 - (list.get(i)
                            .getWidth())) {

                WallXBounceDetected(list.get(i));
            }
            if (list.get(i).getLocationY() < 0
                    || list.get(i).getLocationY() > 360 - (list.get(i)
                            .getHeight())) {

                WallYBounceDetected(list.get(i));
            }

            for (int j = i + 1; j < list.size(); j++) {
                if (list.get(i).getBounds().intersects(list.get(j).getBounds())
                        && (!list.get(i).getDidHeBounce())
                        && (!list.get(j).getDidHeBounce())) {
                    System.out.println(list.get(i).getClass().getSimpleName());

                    FigureBounceDetected(list.get(i), list.get(j));
                }
            }

        }
    }

    public static void updateFlags(List<Square> list) {
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setDidHeBounce(false);
        }
    }

    public static void WallXBounceDetected(Square f) {
        f.setVelocityX(-f.getVelocityX());

    }

    public static void WallYBounceDetected(Square f) {
        f.setVelocityY(-f.getVelocityY());

    }

    public static void FigureBounceDetected(Square f1, Square f2) {
        // Elastic Collision
        // Figure 1
        double newSpeedF1X = (f1.getVelocityX() * (f1.getMass() - f2.getMass()) + (2 * f2
                .getMass() * f2.getVelocityX()))
                / (f1.getMass() + f2.getMass());
        double newSpeedF1Y = (f1.getVelocityY() * (f1.getMass() - f2.getMass()) + (2 * f2
                .getMass() * f2.getVelocityY()))
                / (f1.getMass() + f2.getMass());
        // Figure 2
        double newSpeedF2X = (f2.getVelocityX() * (f2.getMass() - f1.getMass()) + (2 * f1
                .getMass() * f1.getVelocityX()))
                / (f1.getMass() + f2.getMass());
        double newSpeedF2Y = (f2.getVelocityY() * (f2.getMass() - f1.getMass()) + (2 * f1
                .getMass() * f1.getVelocityX()))
                / (f1.getMass() + f2.getMass());

        f1.setLocationX(f1.getLocationX() + (newSpeedF1X));
        f1.setLocationY(f1.getLocationY() + (newSpeedF1Y));
        f2.setLocationX(f2.getLocationX() + (newSpeedF2X));
        f2.setLocationY(f2.getLocationY() + (newSpeedF2Y));
        // new velocity
        f1.setVelocityX(newSpeedF1X);
        f1.setVelocityY(newSpeedF1Y);
        f2.setVelocityX(newSpeedF2X);
        f2.setVelocityY(newSpeedF2Y);
        // flag true
        f1.setDidHeBounce(true);
        f2.setDidHeBounce(true);
    }

}

Class Square.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;

public class Square    {

    Rectangle2D.Double square;
    private double locationX = 120;
    private double locationY = 120;
    private double velocityX =1;
    private double velocityY =1;
    private double width;
    private double height = width;
    private double mass = width;
    private boolean didHeBounce=false;

    Color color;



    public Square(int width) {
        this.width = width;
        height = this.width;
        mass = height;
        Random r = new Random();
        if(r.nextInt(2)>0){
            velocityX=-1;
        } else {
            velocityX=1;
        }
        if(r.nextInt(2)>0){
            velocityY=-1;
        } else {
            velocityY=1;
        }
        locationX =r.nextInt(540);
        locationY= r.nextInt(220);
    }

    public void drawItself(Graphics g){
        Graphics2D g2d = (Graphics2D) g;
        square = new Rectangle2D.Double(locationX,locationY,height,width);
        g2d.fill(square);
        g.setColor(Color.BLUE);
    }


    public boolean getDidHeBounce() {
        return didHeBounce;
    }
    public void setDidHeBounce(boolean didHeBounce){
        this.didHeBounce = didHeBounce;

    }
    public double getLocationX() {
        return locationX;
    }
    public void setLocationX(double locationX) {
        this.locationX = locationX;
    }
    public double getLocationY() {
        return locationY;
    }
    public void setLocationY(double locationY) {
        this.locationY = locationY;
    }
    public double getVelocityX() {
        return velocityX;
    }
    public void setVelocityX(double velocityX) {
        this.velocityX = velocityX;
    }
    public double getVelocityY() {
        return velocityY;
    }
    public void setVelocityY(double velocityY) {
        this.velocityY = velocityY;
    }




    public double getMass() {
        return mass;
    }


    public double getHeight() {
        return height;
    }


    public double getWidth() {
        return width;
    }


    public Rectangle2D getBounds() {
        return square.getBounds2D();
    }
}

Solution

  • Hello first of all you moved them here:

    f1.setLocationX(f1.getLocationX() + (newSpeedF1X));
    f1.setLocationY(f1.getLocationY() + (newSpeedF1Y));
    f2.setLocationX(f2.getLocationX() + (newSpeedF2X));
    f2.setLocationY(f2.getLocationY() + (newSpeedF2Y));
    

    So it's not frequency problem.

    Problem has to be in formula and it is. You got:

         //figure 1
        newSpeedF1X = velocityXF1*(MassF1-MassF2)+(2*MassF2*VelocityXF2)/(MassF1+MassF2)
    
        newSpeedF1Y = velocityYF1*(MassF1-MassF2)+(2*MassF2*VelocityXF2)/(MassF1+MassF2)
         // Figure 2
        newSpeedF2X = velocityXF2*(MassF2-MassF1)+(2*MassF1*VelocityXF1)/(MassF1+MassF2)
    
        newSpeedF2Y = velocityYF2*(MassF2-MassF1)+(2*MassF1*VelocityXF1)/(MassF1+MassF2)
    

    And in newSpeedF2Y should be VelocityYF1 not X

     newSpeedF2Y = velocityYF2*(MassF2-MassF1)+(2*MassF1*VelocityYF1)/(MassF1+MassF2)
    

    Additional remarks

    Wall bounce detect:

    I noticed your figures getting stuck in walls and you change their velocity to -velocity when they are out of bunds so they cant get out. To avoid figures getting stuck in wall you should do something like this:

        public void wallXBounceDetect(Figure f) {
            f.setVelocityX(wallBounceDetect(f.getLocationX(), f.getWidth(), canvas.getWidth(), f.getVelocityX()));
        }
    
        public void wallYBounceDetect(Figure f) {
            f.setVelocityY(wallBounceDetect(f.getLocationY(), f.getHeight(), canvas.getHeight(), f.getVelocityY()));
        }
        public double wallBounceDetect(double location, double size, double maxValue, double velocity) {
            if ((location < 0 && velocity < 0) || (location + size > maxValue && velocity > 0)) {  
                return -velocity;
            }
            return velocity;
        }
    

    Where canvas is your class with method PaintComponent which extends JPanel.