Jole Mola
Jole Mola

Reputation: 25

Ball/Circle collision issue

I'm trying to get the balls to bounce, or reverse direction when they collide. i have it check for the collision within the move method. it checks if there is a collision between two balls and if its true it'll reverse the speeds. The problem is that, once in a while the balls just pass through each other, the smaller balls mostly. balls will either bounce to early, bounce late, get stuck together vibrating,or pass through each other.

public class Balls{

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


public Balls(){
    JFrame frame = new JFrame("Balls");
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.setSize(1000,1000);
    frame.add(new ballHolder());
}
public class ballHolder extends JPanel{
    ;
    public  List<Ball> balls = new ArrayList<>();
    public ballHolder(){
//add balls(x,y,speedX,speedY,radius,color,parent)

        balls.add(new Ball(670,180,2,9,20,Color.RED,this));
        balls.add(new Ball(570,380,-8,-9,20,Color.ORANGE,this));
        balls.add(new Ball(170,780,2,2,50,Color.PINK,this));
        balls.add(new Ball(470,680,5,3,50,Color.GREEN,this));
        balls.add(new Ball(270,280,9,7,50,Color.CYAN,this));

        System.out.println("Number of Balls: "+ balls.size());
        Timer timer = new Timer(20, new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                for(Ball ball : balls){
                    ball.move();
                }
                repaint();  
            }});
        timer.start();
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        for(Ball ball : balls){
            ball.paint(g2); 
        }
    }
    //gets list of balls
    public  List<Ball> getBalls(){
        return balls;
    }

}

public class Ball{
    int x;
    int y;
    public int speedX;
    public int speedY;
    int radius;
    int height;
    int width;
    Color color;
    ballHolder parent;
    public Ball(int x,int y,int speedX,int speedY,int radius,Color color,ballHolder parent){
        this.x = x;
        this.y = y;
        this.speedX = speedX;
        this.speedY = speedY;
        this.radius = radius;
        this.color = color;
        this.parent = parent;
        this.height = radius * 2;
        this.width = radius * 2;    
    }
    //moves ball
    public void move(){
        x += speedX;
        y += speedY;
        if(x + width > parent.getWidth()){
             x = parent.getWidth() - width;
            speedX = -speedX;
        }else if (0 > x){ 
            x = 0;
            speedX = -speedX;
        }
        if(y + height > parent.getHeight()){
            y = parent.getHeight() - height;
            speedY = -speedY;
        }else if (0 > y){ 
            y = 0;
            speedY = -speedY;
        }
        //check for ball collision
        for(int i=0;i < parent.getBalls().size();i++){
            for(int j=0;j < parent.getBalls().size();j++){
                if(i != j){
                    if(ballCollision(parent.getBalls().get(i), parent.getBalls().get(j))){
                        System.out.println("Collision");
                        parent.getBalls().get(i).speedX *= -1;
                        parent.getBalls().get(i).speedY *= -1;
                    }
                }
            }
        }

    }
    //checks for collision
    public boolean ballCollision(Ball a, Ball b){
        if((b.x-a.x)*(b.x-a.x) + (a.y-b.y)*(a.y-b.y) <= (a.radius+b.radius)*(a.radius+b.radius)){
            return true;
        }else{
            return false;
        }

    }                   
    private void paint(Graphics g2){
        g2.setColor(color);
        g2.fillOval(x, y, width, height);

    }
}
}

Updated Version: with suggestion from J W and ZnW. and added more balls so you can see the problem clearer

public class Balls2{

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


public Balls2(){
    JFrame frame = new JFrame("Balls");
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.setSize(1000,1000);
    frame.add(new ballHolder());
}
public class ballHolder extends JPanel{
    ;
    public  List<Ball> balls = new ArrayList<>();
    public ballHolder(){
//add balls(x,y,speedX,speedY,radius,color,parent)
        balls.add(new Ball(350,350,0,0,150,Color.PINK,this));

        balls.add(new Ball(500,0,0,10,20,Color.RED,this));
        balls.add(new Ball(0,500,10,0,20,Color.CYAN,this));
        balls.add(new Ball(500,1000,0,-10,20,Color.ORANGE,this));
        balls.add(new Ball(1000,500,-10,0,20,Color.GREEN,this));

        System.out.println("Number of Balls: "+ balls.size());

        Timer timer = new Timer(20, new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                for(Ball ball : balls){
                    ball.move();
                    repaint();
                    ball.checkCollision();
                }       

            }});
        timer.start();
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        for(Ball ball : balls){
            ball.paint(g2); 
        }
    }
    //gets list of balls
    public  List<Ball> getBalls(){
        return balls;
    }

}

public class Ball{
    private int x;
    private int y;
    private int speedX;
    private int speedY;
    private int radius;
    private int height;
    private int width;
    private Color color;
    private ballHolder parent;
    public Ball(int x,int y,int speedX,int speedY,int radius,Color color,ballHolder parent){
        this.x = x;
        this.y = y;
        this.speedX = speedX;
        this.speedY = speedY;
        this.radius = radius;
        this.color = color;
        this.parent = parent;
        this.height = radius * 2;
        this.width = radius * 2;    
    }
    //moves ball
    public void move(){

        if(x + width > parent.getWidth()){
             x = parent.getWidth() - width;
            speedX = -speedX;
        }else if (0 > x){ 
            x = 0;
            speedX = -speedX;
        }
        if(y + height > parent.getHeight()){
            y = parent.getHeight() - height;
            speedY = -speedY;
        }else if (0 > y){ 
            y = 0;
            speedY = -speedY;
        }


        x += speedX;
        y += speedY;

    }
    public void checkCollision(){
        for(int j=0;j < parent.getBalls().size();j++){
            if(parent.getBalls().get(j) != this){
                if(ballCollision(parent.getBalls().get(j), this)){
                    System.out.println("Collision");
                    speedX *= -1;
                    speedY *= -1;
                }
            }
        }
    }
    public boolean ballCollision(Ball a, Ball b){
        if((b.x-a.x)*(b.x-a.x) + (a.y-b.y)*(a.y-b.y) <= (a.radius+b.radius)*(a.radius+b.radius)){
            return true;
        }else{
            return false;
        }

    }                   
    private void paint(Graphics g2){
        g2.setColor(color);
        g2.fillOval(x, y, width, height);

    }
}
}

Upvotes: 0

Views: 1701

Answers (2)

J W
J W

Reputation: 1848

Ball move and ball collision should not be done in the same function for each ball. Say you have ball A and B that are about to collide. Ball A moves and then checks for collision, finds one and reverses direction. Then ball B moves, and when it does, it could move out of the collision zone before it detects it. Then it checks for collision and doesn't find one, and keeps going in the same direction.

All balls should be moved, and then you should check for any collisions (or vice versa - they are equivalent).

So I would remove the ball collision check from the move() function and create a new function (building on ZnW's suggested improvement):

public void checkCollision(){
    for(int j=0;j < parent.getBalls().size();j++){
        if(parent.getBalls().get(j) != this){
            if(ballCollision(parent.getBalls().get(j), this)){
                System.out.println("Collision");
                speedX *= -1;
                speedY *= -1;
            }
        }
    }
}

And then in the actionPerformed function, add a loop to check the collisions:

for(Ball ball : balls){
    ball.checkCollision();
}

Upvotes: 1

Oldestkon
Oldestkon

Reputation: 251

Try to modify the position AFTER all calculations. And you have very "resource-costly" collision check, you should compare the balls like this:

 for (int j = 0; j < parent.getBalls().size(); j++)
     if (parent.getBalls().get(j) != this)
          if (ballCollision(parent.getBalls().get(j), this)) {
                 System.out.println("Collision");
                 parent.getBalls().get(j).speedX *= -1;
                 parent.getBalls().get(j).speedY *= -1;
                 speedX *= -1;
                 speedY *= -1;
             }
         }

Just you are comparing all the balls in each ball. You should compare only current ball and another, but not all the balls between all other balls every time.

Upvotes: 0

Related Questions